SConscript revision 13577:70ab75eec40b
1# -*- mode:python -*- 2 3# Copyright (c) 2018 ARM Limited 4# 5# The license below extends only to copyright in the software and shall 6# not be construed as granting a license to any other intellectual 7# property including but not limited to intellectual property relating 8# to a hardware implementation of the functionality of the software 9# licensed hereunder. You may use the software subject to the license 10# terms below provided that you ensure that this notice is replicated 11# unmodified and in its entirety in all distributions of the software, 12# modified or unmodified, in source code or in binary form. 13# 14# Copyright (c) 2004-2005 The Regents of The University of Michigan 15# All rights reserved. 16# 17# Redistribution and use in source and binary forms, with or without 18# modification, are permitted provided that the following conditions are 19# met: redistributions of source code must retain the above copyright 20# notice, this list of conditions and the following disclaimer; 21# redistributions in binary form must reproduce the above copyright 22# notice, this list of conditions and the following disclaimer in the 23# documentation and/or other materials provided with the distribution; 24# neither the name of the copyright holders nor the names of its 25# contributors may be used to endorse or promote products derived from 26# this software without specific prior written permission. 27# 28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39# 40# Authors: Nathan Binkert 41 42from __future__ import print_function 43 44import array 45import bisect 46import functools 47import imp 48import marshal 49import os 50import re 51import subprocess 52import sys 53import zlib 54 55from os.path import basename, dirname, exists, isdir, isfile, join as joinpath 56 57import SCons 58 59from gem5_scons import Transform 60 61# This file defines how to build a particular configuration of gem5 62# based on variable settings in the 'env' build environment. 63 64Import('*') 65 66# Children need to see the environment 67Export('env') 68 69build_env = [(opt, env[opt]) for opt in export_vars] 70 71from m5.util import code_formatter, compareVersions 72 73######################################################################## 74# Code for adding source files of various types 75# 76# When specifying a source file of some type, a set of tags can be 77# specified for that file. 78 79class SourceFilter(object): 80 def __init__(self, predicate): 81 self.predicate = predicate 82 83 def __or__(self, other): 84 return SourceFilter(lambda tags: self.predicate(tags) or 85 other.predicate(tags)) 86 87 def __and__(self, other): 88 return SourceFilter(lambda tags: self.predicate(tags) and 89 other.predicate(tags)) 90 91def with_tags_that(predicate): 92 '''Return a list of sources with tags that satisfy a predicate.''' 93 return SourceFilter(predicate) 94 95def with_any_tags(*tags): 96 '''Return a list of sources with any of the supplied tags.''' 97 return SourceFilter(lambda stags: len(set(tags) & stags) > 0) 98 99def with_all_tags(*tags): 100 '''Return a list of sources with all of the supplied tags.''' 101 return SourceFilter(lambda stags: set(tags) <= stags) 102 103def with_tag(tag): 104 '''Return a list of sources with the supplied tag.''' 105 return SourceFilter(lambda stags: tag in stags) 106 107def without_tags(*tags): 108 '''Return a list of sources without any of the supplied tags.''' 109 return SourceFilter(lambda stags: len(set(tags) & stags) == 0) 110 111def without_tag(tag): 112 '''Return a list of sources with the supplied tag.''' 113 return SourceFilter(lambda stags: tag not in stags) 114 115source_filter_factories = { 116 'with_tags_that': with_tags_that, 117 'with_any_tags': with_any_tags, 118 'with_all_tags': with_all_tags, 119 'with_tag': with_tag, 120 'without_tags': without_tags, 121 'without_tag': without_tag, 122} 123 124Export(source_filter_factories) 125 126class SourceList(list): 127 def apply_filter(self, f): 128 def match(source): 129 return f.predicate(source.tags) 130 return SourceList(filter(match, self)) 131 132 def __getattr__(self, name): 133 func = source_filter_factories.get(name, None) 134 if not func: 135 raise AttributeError 136 137 @functools.wraps(func) 138 def wrapper(*args, **kwargs): 139 return self.apply_filter(func(*args, **kwargs)) 140 return wrapper 141 142class SourceMeta(type): 143 '''Meta class for source files that keeps track of all files of a 144 particular type.''' 145 def __init__(cls, name, bases, dict): 146 super(SourceMeta, cls).__init__(name, bases, dict) 147 cls.all = SourceList() 148 149class SourceFile(object): 150 '''Base object that encapsulates the notion of a source file. 151 This includes, the source node, target node, various manipulations 152 of those. A source file also specifies a set of tags which 153 describing arbitrary properties of the source file.''' 154 __metaclass__ = SourceMeta 155 156 static_objs = {} 157 shared_objs = {} 158 159 def __init__(self, source, tags=None, add_tags=None): 160 if tags is None: 161 tags='gem5 lib' 162 if isinstance(tags, basestring): 163 tags = set([tags]) 164 if not isinstance(tags, set): 165 tags = set(tags) 166 self.tags = tags 167 168 if add_tags: 169 if isinstance(add_tags, basestring): 170 add_tags = set([add_tags]) 171 if not isinstance(add_tags, set): 172 add_tags = set(add_tags) 173 self.tags |= add_tags 174 175 tnode = source 176 if not isinstance(source, SCons.Node.FS.File): 177 tnode = File(source) 178 179 self.tnode = tnode 180 self.snode = tnode.srcnode() 181 182 for base in type(self).__mro__: 183 if issubclass(base, SourceFile): 184 base.all.append(self) 185 186 def static(self, env): 187 key = (self.tnode, env['OBJSUFFIX']) 188 if not key in self.static_objs: 189 self.static_objs[key] = env.StaticObject(self.tnode) 190 return self.static_objs[key] 191 192 def shared(self, env): 193 key = (self.tnode, env['OBJSUFFIX']) 194 if not key in self.shared_objs: 195 self.shared_objs[key] = env.SharedObject(self.tnode) 196 return self.shared_objs[key] 197 198 @property 199 def filename(self): 200 return str(self.tnode) 201 202 @property 203 def dirname(self): 204 return dirname(self.filename) 205 206 @property 207 def basename(self): 208 return basename(self.filename) 209 210 @property 211 def extname(self): 212 index = self.basename.rfind('.') 213 if index <= 0: 214 # dot files aren't extensions 215 return self.basename, None 216 217 return self.basename[:index], self.basename[index+1:] 218 219 def __lt__(self, other): return self.filename < other.filename 220 def __le__(self, other): return self.filename <= other.filename 221 def __gt__(self, other): return self.filename > other.filename 222 def __ge__(self, other): return self.filename >= other.filename 223 def __eq__(self, other): return self.filename == other.filename 224 def __ne__(self, other): return self.filename != other.filename 225 226def blobToCpp(data, symbol, cpp_code, hpp_code=None, namespace=None): 227 ''' 228 Convert bytes data into C++ .cpp and .hh uint8_t byte array 229 code containing that binary data. 230 231 :param data: binary data to be converted to C++ 232 :param symbol: name of the symbol 233 :param cpp_code: append the generated cpp_code to this object 234 :param hpp_code: append the generated hpp_code to this object 235 If None, ignore it. Otherwise, also include it 236 in the .cpp file. 237 :param namespace: namespace to put the symbol into. If None, 238 don't put the symbols into any namespace. 239 ''' 240 symbol_len_declaration = 'const std::size_t {}_len'.format(symbol) 241 symbol_declaration = 'const std::uint8_t {}[]'.format(symbol) 242 if hpp_code is not None: 243 cpp_code('''\ 244#include "blobs/{}.hh" 245'''.format(symbol)) 246 hpp_code('''\ 247#include <cstddef> 248#include <cstdint> 249''') 250 if namespace is not None: 251 hpp_code('namespace {} {{'.format(namespace)) 252 hpp_code('extern ' + symbol_len_declaration + ';') 253 hpp_code('extern ' + symbol_declaration + ';') 254 if namespace is not None: 255 hpp_code('}') 256 if namespace is not None: 257 cpp_code('namespace {} {{'.format(namespace)) 258 cpp_code(symbol_len_declaration + ' = {};'.format(len(data))) 259 cpp_code(symbol_declaration + ' = {') 260 cpp_code.indent() 261 step = 16 262 for i in xrange(0, len(data), step): 263 x = array.array('B', data[i:i+step]) 264 cpp_code(''.join('%d,' % d for d in x)) 265 cpp_code.dedent() 266 cpp_code('};') 267 if namespace is not None: 268 cpp_code('}') 269 270def Blob(blob_path, symbol): 271 ''' 272 Embed an arbitrary blob into the gem5 executable, 273 and make it accessible to C++ as a byte array. 274 ''' 275 blob_path = os.path.abspath(blob_path) 276 blob_out_dir = os.path.join(env['BUILDDIR'], 'blobs') 277 path_noext = joinpath(blob_out_dir, symbol) 278 cpp_path = path_noext + '.cc' 279 hpp_path = path_noext + '.hh' 280 def embedBlob(target, source, env): 281 data = file(str(source[0]), 'r').read() 282 cpp_code = code_formatter() 283 hpp_code = code_formatter() 284 blobToCpp(data, symbol, cpp_code, hpp_code, namespace='Blobs') 285 cpp_path = str(target[0]) 286 hpp_path = str(target[1]) 287 cpp_dir = os.path.split(cpp_path)[0] 288 if not os.path.exists(cpp_dir): 289 os.makedirs(cpp_dir) 290 cpp_code.write(cpp_path) 291 hpp_code.write(hpp_path) 292 env.Command([cpp_path, hpp_path], blob_path, 293 MakeAction(embedBlob, Transform("EMBED BLOB"))) 294 Source(cpp_path) 295 296def GdbXml(xml_id, symbol): 297 Blob(joinpath(gdb_xml_dir, xml_id), symbol) 298 299class Source(SourceFile): 300 ungrouped_tag = 'No link group' 301 source_groups = set() 302 303 _current_group_tag = ungrouped_tag 304 305 @staticmethod 306 def link_group_tag(group): 307 return 'link group: %s' % group 308 309 @classmethod 310 def set_group(cls, group): 311 new_tag = Source.link_group_tag(group) 312 Source._current_group_tag = new_tag 313 Source.source_groups.add(group) 314 315 def _add_link_group_tag(self): 316 self.tags.add(Source._current_group_tag) 317 318 '''Add a c/c++ source file to the build''' 319 def __init__(self, source, tags=None, add_tags=None): 320 '''specify the source file, and any tags''' 321 super(Source, self).__init__(source, tags, add_tags) 322 self._add_link_group_tag() 323 324class PySource(SourceFile): 325 '''Add a python source file to the named package''' 326 invalid_sym_char = re.compile('[^A-z0-9_]') 327 modules = {} 328 tnodes = {} 329 symnames = {} 330 331 def __init__(self, package, source, tags=None, add_tags=None): 332 '''specify the python package, the source file, and any tags''' 333 super(PySource, self).__init__(source, tags, add_tags) 334 335 modname,ext = self.extname 336 assert ext == 'py' 337 338 if package: 339 path = package.split('.') 340 else: 341 path = [] 342 343 modpath = path[:] 344 if modname != '__init__': 345 modpath += [ modname ] 346 modpath = '.'.join(modpath) 347 348 arcpath = path + [ self.basename ] 349 abspath = self.snode.abspath 350 if not exists(abspath): 351 abspath = self.tnode.abspath 352 353 self.package = package 354 self.modname = modname 355 self.modpath = modpath 356 self.arcname = joinpath(*arcpath) 357 self.abspath = abspath 358 self.compiled = File(self.filename + 'c') 359 self.cpp = File(self.filename + '.cc') 360 self.symname = PySource.invalid_sym_char.sub('_', modpath) 361 362 PySource.modules[modpath] = self 363 PySource.tnodes[self.tnode] = self 364 PySource.symnames[self.symname] = self 365 366class SimObject(PySource): 367 '''Add a SimObject python file as a python source object and add 368 it to a list of sim object modules''' 369 370 fixed = False 371 modnames = [] 372 373 def __init__(self, source, tags=None, add_tags=None): 374 '''Specify the source file and any tags (automatically in 375 the m5.objects package)''' 376 super(SimObject, self).__init__('m5.objects', source, tags, add_tags) 377 if self.fixed: 378 raise AttributeError, "Too late to call SimObject now." 379 380 bisect.insort_right(SimObject.modnames, self.modname) 381 382class ProtoBuf(SourceFile): 383 '''Add a Protocol Buffer to build''' 384 385 def __init__(self, source, tags=None, add_tags=None): 386 '''Specify the source file, and any tags''' 387 super(ProtoBuf, self).__init__(source, tags, add_tags) 388 389 # Get the file name and the extension 390 modname,ext = self.extname 391 assert ext == 'proto' 392 393 # Currently, we stick to generating the C++ headers, so we 394 # only need to track the source and header. 395 self.cc_file = File(modname + '.pb.cc') 396 self.hh_file = File(modname + '.pb.h') 397 398 399exectuable_classes = [] 400class ExecutableMeta(type): 401 '''Meta class for Executables.''' 402 all = [] 403 404 def __init__(cls, name, bases, d): 405 if not d.pop('abstract', False): 406 ExecutableMeta.all.append(cls) 407 super(ExecutableMeta, cls).__init__(name, bases, d) 408 409 cls.all = [] 410 411class Executable(object): 412 '''Base class for creating an executable from sources.''' 413 __metaclass__ = ExecutableMeta 414 415 abstract = True 416 417 def __init__(self, target, *srcs_and_filts): 418 '''Specify the target name and any sources. Sources that are 419 not SourceFiles are evalued with Source().''' 420 super(Executable, self).__init__() 421 self.all.append(self) 422 self.target = target 423 424 isFilter = lambda arg: isinstance(arg, SourceFilter) 425 self.filters = filter(isFilter, srcs_and_filts) 426 sources = filter(lambda a: not isFilter(a), srcs_and_filts) 427 428 srcs = SourceList() 429 for src in sources: 430 if not isinstance(src, SourceFile): 431 src = Source(src, tags=[]) 432 srcs.append(src) 433 434 self.sources = srcs 435 self.dir = Dir('.') 436 437 def path(self, env): 438 return self.dir.File(self.target + '.' + env['EXE_SUFFIX']) 439 440 def srcs_to_objs(self, env, sources): 441 return list([ s.static(env) for s in sources ]) 442 443 @classmethod 444 def declare_all(cls, env): 445 return list([ instance.declare(env) for instance in cls.all ]) 446 447 def declare(self, env, objs=None): 448 if objs is None: 449 objs = self.srcs_to_objs(env, self.sources) 450 451 if env['STRIP_EXES']: 452 stripped = self.path(env) 453 unstripped = env.File(str(stripped) + '.unstripped') 454 if sys.platform == 'sunos5': 455 cmd = 'cp $SOURCE $TARGET; strip $TARGET' 456 else: 457 cmd = 'strip $SOURCE -o $TARGET' 458 env.Program(unstripped, objs) 459 return env.Command(stripped, unstripped, 460 MakeAction(cmd, Transform("STRIP"))) 461 else: 462 return env.Program(self.path(env), objs) 463 464class UnitTest(Executable): 465 '''Create a UnitTest''' 466 def __init__(self, target, *srcs_and_filts, **kwargs): 467 super(UnitTest, self).__init__(target, *srcs_and_filts) 468 469 self.main = kwargs.get('main', False) 470 471 def declare(self, env): 472 sources = list(self.sources) 473 for f in self.filters: 474 sources = Source.all.apply_filter(f) 475 objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS'] 476 if self.main: 477 objs += env['MAIN_OBJS'] 478 return super(UnitTest, self).declare(env, objs) 479 480class GTest(Executable): 481 '''Create a unit test based on the google test framework.''' 482 all = [] 483 def __init__(self, *srcs_and_filts, **kwargs): 484 super(GTest, self).__init__(*srcs_and_filts) 485 486 self.skip_lib = kwargs.pop('skip_lib', False) 487 488 @classmethod 489 def declare_all(cls, env): 490 env = env.Clone() 491 env.Append(LIBS=env['GTEST_LIBS']) 492 env.Append(CPPFLAGS=env['GTEST_CPPFLAGS']) 493 env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib') 494 env['GTEST_OUT_DIR'] = \ 495 Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX']) 496 return super(GTest, cls).declare_all(env) 497 498 def declare(self, env): 499 sources = list(self.sources) 500 if not self.skip_lib: 501 sources += env['GTEST_LIB_SOURCES'] 502 for f in self.filters: 503 sources += Source.all.apply_filter(f) 504 objs = self.srcs_to_objs(env, sources) 505 506 binary = super(GTest, self).declare(env, objs) 507 508 out_dir = env['GTEST_OUT_DIR'] 509 xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml') 510 AlwaysBuild(env.Command(xml_file, binary, 511 "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}")) 512 513 return binary 514 515class Gem5(Executable): 516 '''Create a gem5 executable.''' 517 518 def __init__(self, target): 519 super(Gem5, self).__init__(target) 520 521 def declare(self, env): 522 objs = env['MAIN_OBJS'] + env['STATIC_OBJS'] 523 return super(Gem5, self).declare(env, objs) 524 525 526# Children should have access 527Export('Blob') 528Export('GdbXml') 529Export('Source') 530Export('PySource') 531Export('SimObject') 532Export('ProtoBuf') 533Export('Executable') 534Export('UnitTest') 535Export('GTest') 536 537######################################################################## 538# 539# Debug Flags 540# 541debug_flags = {} 542def DebugFlag(name, desc=None): 543 if name in debug_flags: 544 raise AttributeError, "Flag %s already specified" % name 545 debug_flags[name] = (name, (), desc) 546 547def CompoundFlag(name, flags, desc=None): 548 if name in debug_flags: 549 raise AttributeError, "Flag %s already specified" % name 550 551 compound = tuple(flags) 552 debug_flags[name] = (name, compound, desc) 553 554Export('DebugFlag') 555Export('CompoundFlag') 556 557######################################################################## 558# 559# Set some compiler variables 560# 561 562# Include file paths are rooted in this directory. SCons will 563# automatically expand '.' to refer to both the source directory and 564# the corresponding build directory to pick up generated include 565# files. 566env.Append(CPPPATH=Dir('.')) 567 568for extra_dir in extras_dir_list: 569 env.Append(CPPPATH=Dir(extra_dir)) 570 571# Workaround for bug in SCons version > 0.97d20071212 572# Scons bug id: 2006 gem5 Bug id: 308 573for root, dirs, files in os.walk(base_dir, topdown=True): 574 Dir(root[len(base_dir) + 1:]) 575 576######################################################################## 577# 578# Walk the tree and execute all SConscripts in subdirectories 579# 580 581here = Dir('.').srcnode().abspath 582for root, dirs, files in os.walk(base_dir, topdown=True): 583 if root == here: 584 # we don't want to recurse back into this SConscript 585 continue 586 587 if 'SConscript' in files: 588 build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:]) 589 Source.set_group(build_dir) 590 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir) 591 592for extra_dir in extras_dir_list: 593 prefix_len = len(dirname(extra_dir)) + 1 594 595 # Also add the corresponding build directory to pick up generated 596 # include files. 597 env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:]))) 598 599 for root, dirs, files in os.walk(extra_dir, topdown=True): 600 # if build lives in the extras directory, don't walk down it 601 if 'build' in dirs: 602 dirs.remove('build') 603 604 if 'SConscript' in files: 605 build_dir = joinpath(env['BUILDDIR'], root[prefix_len:]) 606 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir) 607 608for opt in export_vars: 609 env.ConfigFile(opt) 610 611def makeTheISA(source, target, env): 612 isas = [ src.get_contents() for src in source ] 613 target_isa = env['TARGET_ISA'] 614 def define(isa): 615 return isa.upper() + '_ISA' 616 617 def namespace(isa): 618 return isa[0].upper() + isa[1:].lower() + 'ISA' 619 620 621 code = code_formatter() 622 code('''\ 623#ifndef __CONFIG_THE_ISA_HH__ 624#define __CONFIG_THE_ISA_HH__ 625 626''') 627 628 # create defines for the preprocessing and compile-time determination 629 for i,isa in enumerate(isas): 630 code('#define $0 $1', define(isa), i + 1) 631 code() 632 633 # create an enum for any run-time determination of the ISA, we 634 # reuse the same name as the namespaces 635 code('enum class Arch {') 636 for i,isa in enumerate(isas): 637 if i + 1 == len(isas): 638 code(' $0 = $1', namespace(isa), define(isa)) 639 else: 640 code(' $0 = $1,', namespace(isa), define(isa)) 641 code('};') 642 643 code(''' 644 645#define THE_ISA ${{define(target_isa)}} 646#define TheISA ${{namespace(target_isa)}} 647#define THE_ISA_STR "${{target_isa}}" 648 649#endif // __CONFIG_THE_ISA_HH__''') 650 651 code.write(str(target[0])) 652 653env.Command('config/the_isa.hh', map(Value, all_isa_list), 654 MakeAction(makeTheISA, Transform("CFG ISA", 0))) 655 656def makeTheGPUISA(source, target, env): 657 isas = [ src.get_contents() for src in source ] 658 target_gpu_isa = env['TARGET_GPU_ISA'] 659 def define(isa): 660 return isa.upper() + '_ISA' 661 662 def namespace(isa): 663 return isa[0].upper() + isa[1:].lower() + 'ISA' 664 665 666 code = code_formatter() 667 code('''\ 668#ifndef __CONFIG_THE_GPU_ISA_HH__ 669#define __CONFIG_THE_GPU_ISA_HH__ 670 671''') 672 673 # create defines for the preprocessing and compile-time determination 674 for i,isa in enumerate(isas): 675 code('#define $0 $1', define(isa), i + 1) 676 code() 677 678 # create an enum for any run-time determination of the ISA, we 679 # reuse the same name as the namespaces 680 code('enum class GPUArch {') 681 for i,isa in enumerate(isas): 682 if i + 1 == len(isas): 683 code(' $0 = $1', namespace(isa), define(isa)) 684 else: 685 code(' $0 = $1,', namespace(isa), define(isa)) 686 code('};') 687 688 code(''' 689 690#define THE_GPU_ISA ${{define(target_gpu_isa)}} 691#define TheGpuISA ${{namespace(target_gpu_isa)}} 692#define THE_GPU_ISA_STR "${{target_gpu_isa}}" 693 694#endif // __CONFIG_THE_GPU_ISA_HH__''') 695 696 code.write(str(target[0])) 697 698env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list), 699 MakeAction(makeTheGPUISA, Transform("CFG ISA", 0))) 700 701######################################################################## 702# 703# Prevent any SimObjects from being added after this point, they 704# should all have been added in the SConscripts above 705# 706SimObject.fixed = True 707 708class DictImporter(object): 709 '''This importer takes a dictionary of arbitrary module names that 710 map to arbitrary filenames.''' 711 def __init__(self, modules): 712 self.modules = modules 713 self.installed = set() 714 715 def __del__(self): 716 self.unload() 717 718 def unload(self): 719 import sys 720 for module in self.installed: 721 del sys.modules[module] 722 self.installed = set() 723 724 def find_module(self, fullname, path): 725 if fullname == 'm5.defines': 726 return self 727 728 if fullname == 'm5.objects': 729 return self 730 731 if fullname.startswith('_m5'): 732 return None 733 734 source = self.modules.get(fullname, None) 735 if source is not None and fullname.startswith('m5.objects'): 736 return self 737 738 return None 739 740 def load_module(self, fullname): 741 mod = imp.new_module(fullname) 742 sys.modules[fullname] = mod 743 self.installed.add(fullname) 744 745 mod.__loader__ = self 746 if fullname == 'm5.objects': 747 mod.__path__ = fullname.split('.') 748 return mod 749 750 if fullname == 'm5.defines': 751 mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env) 752 return mod 753 754 source = self.modules[fullname] 755 if source.modname == '__init__': 756 mod.__path__ = source.modpath 757 mod.__file__ = source.abspath 758 759 exec file(source.abspath, 'r') in mod.__dict__ 760 761 return mod 762 763import m5.SimObject 764import m5.params 765from m5.util import code_formatter 766 767m5.SimObject.clear() 768m5.params.clear() 769 770# install the python importer so we can grab stuff from the source 771# tree itself. We can't have SimObjects added after this point or 772# else we won't know about them for the rest of the stuff. 773importer = DictImporter(PySource.modules) 774sys.meta_path[0:0] = [ importer ] 775 776# import all sim objects so we can populate the all_objects list 777# make sure that we're working with a list, then let's sort it 778for modname in SimObject.modnames: 779 exec('from m5.objects import %s' % modname) 780 781# we need to unload all of the currently imported modules so that they 782# will be re-imported the next time the sconscript is run 783importer.unload() 784sys.meta_path.remove(importer) 785 786sim_objects = m5.SimObject.allClasses 787all_enums = m5.params.allEnums 788 789for name,obj in sorted(sim_objects.iteritems()): 790 for param in obj._params.local.values(): 791 # load the ptype attribute now because it depends on the 792 # current version of SimObject.allClasses, but when scons 793 # actually uses the value, all versions of 794 # SimObject.allClasses will have been loaded 795 param.ptype 796 797######################################################################## 798# 799# calculate extra dependencies 800# 801module_depends = ["m5", "m5.SimObject", "m5.params"] 802depends = [ PySource.modules[dep].snode for dep in module_depends ] 803depends.sort(key = lambda x: x.name) 804 805######################################################################## 806# 807# Commands for the basic automatically generated python files 808# 809 810# Generate Python file containing a dict specifying the current 811# buildEnv flags. 812def makeDefinesPyFile(target, source, env): 813 build_env = source[0].get_contents() 814 815 code = code_formatter() 816 code(""" 817import _m5.core 818import m5.util 819 820buildEnv = m5.util.SmartDict($build_env) 821 822compileDate = _m5.core.compileDate 823_globals = globals() 824for key,val in _m5.core.__dict__.iteritems(): 825 if key.startswith('flag_'): 826 flag = key[5:] 827 _globals[flag] = val 828del _globals 829""") 830 code.write(target[0].abspath) 831 832defines_info = Value(build_env) 833# Generate a file with all of the compile options in it 834env.Command('python/m5/defines.py', defines_info, 835 MakeAction(makeDefinesPyFile, Transform("DEFINES", 0))) 836PySource('m5', 'python/m5/defines.py') 837 838# Generate python file containing info about the M5 source code 839def makeInfoPyFile(target, source, env): 840 code = code_formatter() 841 for src in source: 842 data = ''.join(file(src.srcnode().abspath, 'r').xreadlines()) 843 code('$src = ${{repr(data)}}') 844 code.write(str(target[0])) 845 846# Generate a file that wraps the basic top level files 847env.Command('python/m5/info.py', 848 [ '#/COPYING', '#/LICENSE', '#/README', ], 849 MakeAction(makeInfoPyFile, Transform("INFO"))) 850PySource('m5', 'python/m5/info.py') 851 852######################################################################## 853# 854# Create all of the SimObject param headers and enum headers 855# 856 857def createSimObjectParamStruct(target, source, env): 858 assert len(target) == 1 and len(source) == 1 859 860 name = source[0].get_text_contents() 861 obj = sim_objects[name] 862 863 code = code_formatter() 864 obj.cxx_param_decl(code) 865 code.write(target[0].abspath) 866 867def createSimObjectCxxConfig(is_header): 868 def body(target, source, env): 869 assert len(target) == 1 and len(source) == 1 870 871 name = str(source[0].get_contents()) 872 obj = sim_objects[name] 873 874 code = code_formatter() 875 obj.cxx_config_param_file(code, is_header) 876 code.write(target[0].abspath) 877 return body 878 879def createEnumStrings(target, source, env): 880 assert len(target) == 1 and len(source) == 2 881 882 name = source[0].get_text_contents() 883 use_python = source[1].read() 884 obj = all_enums[name] 885 886 code = code_formatter() 887 obj.cxx_def(code) 888 if use_python: 889 obj.pybind_def(code) 890 code.write(target[0].abspath) 891 892def createEnumDecls(target, source, env): 893 assert len(target) == 1 and len(source) == 1 894 895 name = source[0].get_text_contents() 896 obj = all_enums[name] 897 898 code = code_formatter() 899 obj.cxx_decl(code) 900 code.write(target[0].abspath) 901 902def createSimObjectPyBindWrapper(target, source, env): 903 name = source[0].get_text_contents() 904 obj = sim_objects[name] 905 906 code = code_formatter() 907 obj.pybind_decl(code) 908 code.write(target[0].abspath) 909 910# Generate all of the SimObject param C++ struct header files 911params_hh_files = [] 912for name,simobj in sorted(sim_objects.iteritems()): 913 py_source = PySource.modules[simobj.__module__] 914 extra_deps = [ py_source.tnode ] 915 916 hh_file = File('params/%s.hh' % name) 917 params_hh_files.append(hh_file) 918 env.Command(hh_file, Value(name), 919 MakeAction(createSimObjectParamStruct, Transform("SO PARAM"))) 920 env.Depends(hh_file, depends + extra_deps) 921 922# C++ parameter description files 923if GetOption('with_cxx_config'): 924 for name,simobj in sorted(sim_objects.iteritems()): 925 py_source = PySource.modules[simobj.__module__] 926 extra_deps = [ py_source.tnode ] 927 928 cxx_config_hh_file = File('cxx_config/%s.hh' % name) 929 cxx_config_cc_file = File('cxx_config/%s.cc' % name) 930 env.Command(cxx_config_hh_file, Value(name), 931 MakeAction(createSimObjectCxxConfig(True), 932 Transform("CXXCPRHH"))) 933 env.Command(cxx_config_cc_file, Value(name), 934 MakeAction(createSimObjectCxxConfig(False), 935 Transform("CXXCPRCC"))) 936 env.Depends(cxx_config_hh_file, depends + extra_deps + 937 [File('params/%s.hh' % name), File('sim/cxx_config.hh')]) 938 env.Depends(cxx_config_cc_file, depends + extra_deps + 939 [cxx_config_hh_file]) 940 Source(cxx_config_cc_file) 941 942 cxx_config_init_cc_file = File('cxx_config/init.cc') 943 944 def createCxxConfigInitCC(target, source, env): 945 assert len(target) == 1 and len(source) == 1 946 947 code = code_formatter() 948 949 for name,simobj in sorted(sim_objects.iteritems()): 950 if not hasattr(simobj, 'abstract') or not simobj.abstract: 951 code('#include "cxx_config/${name}.hh"') 952 code() 953 code('void cxxConfigInit()') 954 code('{') 955 code.indent() 956 for name,simobj in sorted(sim_objects.iteritems()): 957 not_abstract = not hasattr(simobj, 'abstract') or \ 958 not simobj.abstract 959 if not_abstract and 'type' in simobj.__dict__: 960 code('cxx_config_directory["${name}"] = ' 961 '${name}CxxConfigParams::makeDirectoryEntry();') 962 code.dedent() 963 code('}') 964 code.write(target[0].abspath) 965 966 py_source = PySource.modules[simobj.__module__] 967 extra_deps = [ py_source.tnode ] 968 env.Command(cxx_config_init_cc_file, Value(name), 969 MakeAction(createCxxConfigInitCC, Transform("CXXCINIT"))) 970 cxx_param_hh_files = ["cxx_config/%s.hh" % simobj 971 for name,simobj in sorted(sim_objects.iteritems()) 972 if not hasattr(simobj, 'abstract') or not simobj.abstract] 973 Depends(cxx_config_init_cc_file, cxx_param_hh_files + 974 [File('sim/cxx_config.hh')]) 975 Source(cxx_config_init_cc_file) 976 977# Generate all enum header files 978for name,enum in sorted(all_enums.iteritems()): 979 py_source = PySource.modules[enum.__module__] 980 extra_deps = [ py_source.tnode ] 981 982 cc_file = File('enums/%s.cc' % name) 983 env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])], 984 MakeAction(createEnumStrings, Transform("ENUM STR"))) 985 env.Depends(cc_file, depends + extra_deps) 986 Source(cc_file) 987 988 hh_file = File('enums/%s.hh' % name) 989 env.Command(hh_file, Value(name), 990 MakeAction(createEnumDecls, Transform("ENUMDECL"))) 991 env.Depends(hh_file, depends + extra_deps) 992 993# Generate SimObject Python bindings wrapper files 994if env['USE_PYTHON']: 995 for name,simobj in sorted(sim_objects.iteritems()): 996 py_source = PySource.modules[simobj.__module__] 997 extra_deps = [ py_source.tnode ] 998 cc_file = File('python/_m5/param_%s.cc' % name) 999 env.Command(cc_file, Value(name), 1000 MakeAction(createSimObjectPyBindWrapper, 1001 Transform("SO PyBind"))) 1002 env.Depends(cc_file, depends + extra_deps) 1003 Source(cc_file) 1004 1005# Build all protocol buffers if we have got protoc and protobuf available 1006if env['HAVE_PROTOBUF']: 1007 for proto in ProtoBuf.all: 1008 # Use both the source and header as the target, and the .proto 1009 # file as the source. When executing the protoc compiler, also 1010 # specify the proto_path to avoid having the generated files 1011 # include the path. 1012 env.Command([proto.cc_file, proto.hh_file], proto.tnode, 1013 MakeAction('$PROTOC --cpp_out ${TARGET.dir} ' 1014 '--proto_path ${SOURCE.dir} $SOURCE', 1015 Transform("PROTOC"))) 1016 1017 # Add the C++ source file 1018 Source(proto.cc_file, tags=proto.tags) 1019elif ProtoBuf.all: 1020 print('Got protobuf to build, but lacks support!') 1021 Exit(1) 1022 1023# 1024# Handle debug flags 1025# 1026def makeDebugFlagCC(target, source, env): 1027 assert(len(target) == 1 and len(source) == 1) 1028 1029 code = code_formatter() 1030 1031 # delay definition of CompoundFlags until after all the definition 1032 # of all constituent SimpleFlags 1033 comp_code = code_formatter() 1034 1035 # file header 1036 code(''' 1037/* 1038 * DO NOT EDIT THIS FILE! Automatically generated by SCons. 1039 */ 1040 1041#include "base/debug.hh" 1042 1043namespace Debug { 1044 1045''') 1046 1047 for name, flag in sorted(source[0].read().iteritems()): 1048 n, compound, desc = flag 1049 assert n == name 1050 1051 if not compound: 1052 code('SimpleFlag $name("$name", "$desc");') 1053 else: 1054 comp_code('CompoundFlag $name("$name", "$desc",') 1055 comp_code.indent() 1056 last = len(compound) - 1 1057 for i,flag in enumerate(compound): 1058 if i != last: 1059 comp_code('&$flag,') 1060 else: 1061 comp_code('&$flag);') 1062 comp_code.dedent() 1063 1064 code.append(comp_code) 1065 code() 1066 code('} // namespace Debug') 1067 1068 code.write(str(target[0])) 1069 1070def makeDebugFlagHH(target, source, env): 1071 assert(len(target) == 1 and len(source) == 1) 1072 1073 val = eval(source[0].get_contents()) 1074 name, compound, desc = val 1075 1076 code = code_formatter() 1077 1078 # file header boilerplate 1079 code('''\ 1080/* 1081 * DO NOT EDIT THIS FILE! Automatically generated by SCons. 1082 */ 1083 1084#ifndef __DEBUG_${name}_HH__ 1085#define __DEBUG_${name}_HH__ 1086 1087namespace Debug { 1088''') 1089 1090 if compound: 1091 code('class CompoundFlag;') 1092 code('class SimpleFlag;') 1093 1094 if compound: 1095 code('extern CompoundFlag $name;') 1096 for flag in compound: 1097 code('extern SimpleFlag $flag;') 1098 else: 1099 code('extern SimpleFlag $name;') 1100 1101 code(''' 1102} 1103 1104#endif // __DEBUG_${name}_HH__ 1105''') 1106 1107 code.write(str(target[0])) 1108 1109for name,flag in sorted(debug_flags.iteritems()): 1110 n, compound, desc = flag 1111 assert n == name 1112 1113 hh_file = 'debug/%s.hh' % name 1114 env.Command(hh_file, Value(flag), 1115 MakeAction(makeDebugFlagHH, Transform("TRACING", 0))) 1116 1117env.Command('debug/flags.cc', Value(debug_flags), 1118 MakeAction(makeDebugFlagCC, Transform("TRACING", 0))) 1119Source('debug/flags.cc') 1120 1121# version tags 1122tags = \ 1123env.Command('sim/tags.cc', None, 1124 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET', 1125 Transform("VER TAGS"))) 1126env.AlwaysBuild(tags) 1127 1128# Embed python files. All .py files that have been indicated by a 1129# PySource() call in a SConscript need to be embedded into the M5 1130# library. To do that, we compile the file to byte code, marshal the 1131# byte code, compress it, and then generate a c++ file that 1132# inserts the result into an array. 1133def embedPyFile(target, source, env): 1134 def c_str(string): 1135 if string is None: 1136 return "0" 1137 return '"%s"' % string 1138 1139 '''Action function to compile a .py into a code object, marshal 1140 it, compress it, and stick it into an asm file so the code appears 1141 as just bytes with a label in the data section''' 1142 1143 src = file(str(source[0]), 'r').read() 1144 1145 pysource = PySource.tnodes[source[0]] 1146 compiled = compile(src, pysource.abspath, 'exec') 1147 marshalled = marshal.dumps(compiled) 1148 compressed = zlib.compress(marshalled) 1149 data = compressed 1150 sym = pysource.symname 1151 1152 code = code_formatter() 1153 code('''\ 1154#include "sim/init.hh" 1155 1156namespace { 1157 1158''') 1159 blobToCpp(data, 'data_' + sym, code) 1160 code('''\ 1161 1162 1163EmbeddedPython embedded_${sym}( 1164 ${{c_str(pysource.arcname)}}, 1165 ${{c_str(pysource.abspath)}}, 1166 ${{c_str(pysource.modpath)}}, 1167 data_${sym}, 1168 ${{len(data)}}, 1169 ${{len(marshalled)}}); 1170 1171} // anonymous namespace 1172''') 1173 code.write(str(target[0])) 1174 1175for source in PySource.all: 1176 env.Command(source.cpp, source.tnode, 1177 MakeAction(embedPyFile, Transform("EMBED PY"))) 1178 Source(source.cpp, tags=source.tags, add_tags='python') 1179 1180######################################################################## 1181# 1182# Define binaries. Each different build type (debug, opt, etc.) gets 1183# a slightly different build environment. 1184# 1185 1186# List of constructed environments to pass back to SConstruct 1187date_source = Source('base/date.cc', tags=[]) 1188 1189gem5_binary = Gem5('gem5') 1190 1191# Function to create a new build environment as clone of current 1192# environment 'env' with modified object suffix and optional stripped 1193# binary. Additional keyword arguments are appended to corresponding 1194# build environment vars. 1195def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs): 1196 # SCons doesn't know to append a library suffix when there is a '.' in the 1197 # name. Use '_' instead. 1198 libname = 'gem5_' + label 1199 secondary_exename = 'm5.' + label 1200 1201 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's') 1202 new_env.Label = label 1203 new_env.Append(**kwargs) 1204 1205 lib_sources = Source.all.with_tag('gem5 lib') 1206 1207 # Without Python, leave out all Python content from the library 1208 # builds. The option doesn't affect gem5 built as a program 1209 if GetOption('without_python'): 1210 lib_sources = lib_sources.without_tag('python') 1211 1212 static_objs = [] 1213 shared_objs = [] 1214 1215 for s in lib_sources.with_tag(Source.ungrouped_tag): 1216 static_objs.append(s.static(new_env)) 1217 shared_objs.append(s.shared(new_env)) 1218 1219 for group in Source.source_groups: 1220 srcs = lib_sources.with_tag(Source.link_group_tag(group)) 1221 if not srcs: 1222 continue 1223 1224 group_static = [ s.static(new_env) for s in srcs ] 1225 group_shared = [ s.shared(new_env) for s in srcs ] 1226 1227 # If partial linking is disabled, add these sources to the build 1228 # directly, and short circuit this loop. 1229 if disable_partial: 1230 static_objs.extend(group_static) 1231 shared_objs.extend(group_shared) 1232 continue 1233 1234 # Set up the static partially linked objects. 1235 file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial") 1236 target = File(joinpath(group, file_name)) 1237 partial = env.PartialStatic(target=target, source=group_static) 1238 static_objs.extend(partial) 1239 1240 # Set up the shared partially linked objects. 1241 file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial") 1242 target = File(joinpath(group, file_name)) 1243 partial = env.PartialShared(target=target, source=group_shared) 1244 shared_objs.extend(partial) 1245 1246 static_date = date_source.static(new_env) 1247 new_env.Depends(static_date, static_objs) 1248 static_objs.extend(static_date) 1249 1250 shared_date = date_source.shared(new_env) 1251 new_env.Depends(shared_date, shared_objs) 1252 shared_objs.extend(shared_date) 1253 1254 main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ] 1255 1256 # First make a library of everything but main() so other programs can 1257 # link against m5. 1258 static_lib = new_env.StaticLibrary(libname, static_objs) 1259 shared_lib = new_env.SharedLibrary(libname, shared_objs) 1260 1261 # Keep track of the object files generated so far so Executables can 1262 # include them. 1263 new_env['STATIC_OBJS'] = static_objs 1264 new_env['SHARED_OBJS'] = shared_objs 1265 new_env['MAIN_OBJS'] = main_objs 1266 1267 new_env['STATIC_LIB'] = static_lib 1268 new_env['SHARED_LIB'] = shared_lib 1269 1270 # Record some settings for building Executables. 1271 new_env['EXE_SUFFIX'] = label 1272 new_env['STRIP_EXES'] = strip 1273 1274 for cls in ExecutableMeta.all: 1275 cls.declare_all(new_env) 1276 1277 new_env.M5Binary = File(gem5_binary.path(new_env)) 1278 1279 new_env.Command(secondary_exename, new_env.M5Binary, 1280 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK"))) 1281 1282 # Set up regression tests. 1283 SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'), 1284 variant_dir=Dir('tests').Dir(new_env.Label), 1285 exports={ 'env' : new_env }, duplicate=False) 1286 1287# Start out with the compiler flags common to all compilers, 1288# i.e. they all use -g for opt and -g -pg for prof 1289ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'], 1290 'perf' : ['-g']} 1291 1292# Start out with the linker flags common to all linkers, i.e. -pg for 1293# prof, and -lprofiler for perf. The -lprofile flag is surrounded by 1294# no-as-needed and as-needed as the binutils linker is too clever and 1295# simply doesn't link to the library otherwise. 1296ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'], 1297 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']} 1298 1299# For Link Time Optimization, the optimisation flags used to compile 1300# individual files are decoupled from those used at link time 1301# (i.e. you can compile with -O3 and perform LTO with -O0), so we need 1302# to also update the linker flags based on the target. 1303if env['GCC']: 1304 if sys.platform == 'sunos5': 1305 ccflags['debug'] += ['-gstabs+'] 1306 else: 1307 ccflags['debug'] += ['-ggdb3'] 1308 ldflags['debug'] += ['-O0'] 1309 # opt, fast, prof and perf all share the same cc flags, also add 1310 # the optimization to the ldflags as LTO defers the optimization 1311 # to link time 1312 for target in ['opt', 'fast', 'prof', 'perf']: 1313 ccflags[target] += ['-O3'] 1314 ldflags[target] += ['-O3'] 1315 1316 ccflags['fast'] += env['LTO_CCFLAGS'] 1317 ldflags['fast'] += env['LTO_LDFLAGS'] 1318elif env['CLANG']: 1319 ccflags['debug'] += ['-g', '-O0'] 1320 # opt, fast, prof and perf all share the same cc flags 1321 for target in ['opt', 'fast', 'prof', 'perf']: 1322 ccflags[target] += ['-O3'] 1323else: 1324 print('Unknown compiler, please fix compiler options') 1325 Exit(1) 1326 1327 1328# To speed things up, we only instantiate the build environments we 1329# need. We try to identify the needed environment for each target; if 1330# we can't, we fall back on instantiating all the environments just to 1331# be safe. 1332target_types = ['debug', 'opt', 'fast', 'prof', 'perf'] 1333obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof', 1334 'gpo' : 'perf'} 1335 1336def identifyTarget(t): 1337 ext = t.split('.')[-1] 1338 if ext in target_types: 1339 return ext 1340 if obj2target.has_key(ext): 1341 return obj2target[ext] 1342 match = re.search(r'/tests/([^/]+)/', t) 1343 if match and match.group(1) in target_types: 1344 return match.group(1) 1345 return 'all' 1346 1347needed_envs = [identifyTarget(target) for target in BUILD_TARGETS] 1348if 'all' in needed_envs: 1349 needed_envs += target_types 1350 1351disable_partial = False 1352if env['PLATFORM'] == 'darwin': 1353 # Up until Apple LLVM version 10.0.0 (clang-1000.11.45.5), partial 1354 # linked objects do not expose symbols that are marked with the 1355 # hidden visibility and consequently building gem5 on Mac OS 1356 # fails. As a workaround, we disable partial linking, however, we 1357 # may want to revisit in the future. 1358 disable_partial = True 1359 1360# Debug binary 1361if 'debug' in needed_envs: 1362 makeEnv(env, 'debug', '.do', 1363 CCFLAGS = Split(ccflags['debug']), 1364 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'], 1365 LINKFLAGS = Split(ldflags['debug']), 1366 disable_partial=disable_partial) 1367 1368# Optimized binary 1369if 'opt' in needed_envs: 1370 makeEnv(env, 'opt', '.o', 1371 CCFLAGS = Split(ccflags['opt']), 1372 CPPDEFINES = ['TRACING_ON=1'], 1373 LINKFLAGS = Split(ldflags['opt']), 1374 disable_partial=disable_partial) 1375 1376# "Fast" binary 1377if 'fast' in needed_envs: 1378 disable_partial = disable_partial and \ 1379 env.get('BROKEN_INCREMENTAL_LTO', False) and \ 1380 GetOption('force_lto') 1381 makeEnv(env, 'fast', '.fo', strip = True, 1382 CCFLAGS = Split(ccflags['fast']), 1383 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1384 LINKFLAGS = Split(ldflags['fast']), 1385 disable_partial=disable_partial) 1386 1387# Profiled binary using gprof 1388if 'prof' in needed_envs: 1389 makeEnv(env, 'prof', '.po', 1390 CCFLAGS = Split(ccflags['prof']), 1391 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1392 LINKFLAGS = Split(ldflags['prof']), 1393 disable_partial=disable_partial) 1394 1395# Profiled binary using google-pprof 1396if 'perf' in needed_envs: 1397 makeEnv(env, 'perf', '.gpo', 1398 CCFLAGS = Split(ccflags['perf']), 1399 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1400 LINKFLAGS = Split(ldflags['perf']), 1401 disable_partial=disable_partial) 1402