SConscript revision 13576
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 296class Source(SourceFile): 297 ungrouped_tag = 'No link group' 298 source_groups = set() 299 300 _current_group_tag = ungrouped_tag 301 302 @staticmethod 303 def link_group_tag(group): 304 return 'link group: %s' % group 305 306 @classmethod 307 def set_group(cls, group): 308 new_tag = Source.link_group_tag(group) 309 Source._current_group_tag = new_tag 310 Source.source_groups.add(group) 311 312 def _add_link_group_tag(self): 313 self.tags.add(Source._current_group_tag) 314 315 '''Add a c/c++ source file to the build''' 316 def __init__(self, source, tags=None, add_tags=None): 317 '''specify the source file, and any tags''' 318 super(Source, self).__init__(source, tags, add_tags) 319 self._add_link_group_tag() 320 321class PySource(SourceFile): 322 '''Add a python source file to the named package''' 323 invalid_sym_char = re.compile('[^A-z0-9_]') 324 modules = {} 325 tnodes = {} 326 symnames = {} 327 328 def __init__(self, package, source, tags=None, add_tags=None): 329 '''specify the python package, the source file, and any tags''' 330 super(PySource, self).__init__(source, tags, add_tags) 331 332 modname,ext = self.extname 333 assert ext == 'py' 334 335 if package: 336 path = package.split('.') 337 else: 338 path = [] 339 340 modpath = path[:] 341 if modname != '__init__': 342 modpath += [ modname ] 343 modpath = '.'.join(modpath) 344 345 arcpath = path + [ self.basename ] 346 abspath = self.snode.abspath 347 if not exists(abspath): 348 abspath = self.tnode.abspath 349 350 self.package = package 351 self.modname = modname 352 self.modpath = modpath 353 self.arcname = joinpath(*arcpath) 354 self.abspath = abspath 355 self.compiled = File(self.filename + 'c') 356 self.cpp = File(self.filename + '.cc') 357 self.symname = PySource.invalid_sym_char.sub('_', modpath) 358 359 PySource.modules[modpath] = self 360 PySource.tnodes[self.tnode] = self 361 PySource.symnames[self.symname] = self 362 363class SimObject(PySource): 364 '''Add a SimObject python file as a python source object and add 365 it to a list of sim object modules''' 366 367 fixed = False 368 modnames = [] 369 370 def __init__(self, source, tags=None, add_tags=None): 371 '''Specify the source file and any tags (automatically in 372 the m5.objects package)''' 373 super(SimObject, self).__init__('m5.objects', source, tags, add_tags) 374 if self.fixed: 375 raise AttributeError, "Too late to call SimObject now." 376 377 bisect.insort_right(SimObject.modnames, self.modname) 378 379class ProtoBuf(SourceFile): 380 '''Add a Protocol Buffer to build''' 381 382 def __init__(self, source, tags=None, add_tags=None): 383 '''Specify the source file, and any tags''' 384 super(ProtoBuf, self).__init__(source, tags, add_tags) 385 386 # Get the file name and the extension 387 modname,ext = self.extname 388 assert ext == 'proto' 389 390 # Currently, we stick to generating the C++ headers, so we 391 # only need to track the source and header. 392 self.cc_file = File(modname + '.pb.cc') 393 self.hh_file = File(modname + '.pb.h') 394 395 396exectuable_classes = [] 397class ExecutableMeta(type): 398 '''Meta class for Executables.''' 399 all = [] 400 401 def __init__(cls, name, bases, d): 402 if not d.pop('abstract', False): 403 ExecutableMeta.all.append(cls) 404 super(ExecutableMeta, cls).__init__(name, bases, d) 405 406 cls.all = [] 407 408class Executable(object): 409 '''Base class for creating an executable from sources.''' 410 __metaclass__ = ExecutableMeta 411 412 abstract = True 413 414 def __init__(self, target, *srcs_and_filts): 415 '''Specify the target name and any sources. Sources that are 416 not SourceFiles are evalued with Source().''' 417 super(Executable, self).__init__() 418 self.all.append(self) 419 self.target = target 420 421 isFilter = lambda arg: isinstance(arg, SourceFilter) 422 self.filters = filter(isFilter, srcs_and_filts) 423 sources = filter(lambda a: not isFilter(a), srcs_and_filts) 424 425 srcs = SourceList() 426 for src in sources: 427 if not isinstance(src, SourceFile): 428 src = Source(src, tags=[]) 429 srcs.append(src) 430 431 self.sources = srcs 432 self.dir = Dir('.') 433 434 def path(self, env): 435 return self.dir.File(self.target + '.' + env['EXE_SUFFIX']) 436 437 def srcs_to_objs(self, env, sources): 438 return list([ s.static(env) for s in sources ]) 439 440 @classmethod 441 def declare_all(cls, env): 442 return list([ instance.declare(env) for instance in cls.all ]) 443 444 def declare(self, env, objs=None): 445 if objs is None: 446 objs = self.srcs_to_objs(env, self.sources) 447 448 if env['STRIP_EXES']: 449 stripped = self.path(env) 450 unstripped = env.File(str(stripped) + '.unstripped') 451 if sys.platform == 'sunos5': 452 cmd = 'cp $SOURCE $TARGET; strip $TARGET' 453 else: 454 cmd = 'strip $SOURCE -o $TARGET' 455 env.Program(unstripped, objs) 456 return env.Command(stripped, unstripped, 457 MakeAction(cmd, Transform("STRIP"))) 458 else: 459 return env.Program(self.path(env), objs) 460 461class UnitTest(Executable): 462 '''Create a UnitTest''' 463 def __init__(self, target, *srcs_and_filts, **kwargs): 464 super(UnitTest, self).__init__(target, *srcs_and_filts) 465 466 self.main = kwargs.get('main', False) 467 468 def declare(self, env): 469 sources = list(self.sources) 470 for f in self.filters: 471 sources = Source.all.apply_filter(f) 472 objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS'] 473 if self.main: 474 objs += env['MAIN_OBJS'] 475 return super(UnitTest, self).declare(env, objs) 476 477class GTest(Executable): 478 '''Create a unit test based on the google test framework.''' 479 all = [] 480 def __init__(self, *srcs_and_filts, **kwargs): 481 super(GTest, self).__init__(*srcs_and_filts) 482 483 self.skip_lib = kwargs.pop('skip_lib', False) 484 485 @classmethod 486 def declare_all(cls, env): 487 env = env.Clone() 488 env.Append(LIBS=env['GTEST_LIBS']) 489 env.Append(CPPFLAGS=env['GTEST_CPPFLAGS']) 490 env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib') 491 env['GTEST_OUT_DIR'] = \ 492 Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX']) 493 return super(GTest, cls).declare_all(env) 494 495 def declare(self, env): 496 sources = list(self.sources) 497 if not self.skip_lib: 498 sources += env['GTEST_LIB_SOURCES'] 499 for f in self.filters: 500 sources += Source.all.apply_filter(f) 501 objs = self.srcs_to_objs(env, sources) 502 503 binary = super(GTest, self).declare(env, objs) 504 505 out_dir = env['GTEST_OUT_DIR'] 506 xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml') 507 AlwaysBuild(env.Command(xml_file, binary, 508 "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}")) 509 510 return binary 511 512class Gem5(Executable): 513 '''Create a gem5 executable.''' 514 515 def __init__(self, target): 516 super(Gem5, self).__init__(target) 517 518 def declare(self, env): 519 objs = env['MAIN_OBJS'] + env['STATIC_OBJS'] 520 return super(Gem5, self).declare(env, objs) 521 522 523# Children should have access 524Export('Blob') 525Export('Source') 526Export('PySource') 527Export('SimObject') 528Export('ProtoBuf') 529Export('Executable') 530Export('UnitTest') 531Export('GTest') 532 533######################################################################## 534# 535# Debug Flags 536# 537debug_flags = {} 538def DebugFlag(name, desc=None): 539 if name in debug_flags: 540 raise AttributeError, "Flag %s already specified" % name 541 debug_flags[name] = (name, (), desc) 542 543def CompoundFlag(name, flags, desc=None): 544 if name in debug_flags: 545 raise AttributeError, "Flag %s already specified" % name 546 547 compound = tuple(flags) 548 debug_flags[name] = (name, compound, desc) 549 550Export('DebugFlag') 551Export('CompoundFlag') 552 553######################################################################## 554# 555# Set some compiler variables 556# 557 558# Include file paths are rooted in this directory. SCons will 559# automatically expand '.' to refer to both the source directory and 560# the corresponding build directory to pick up generated include 561# files. 562env.Append(CPPPATH=Dir('.')) 563 564for extra_dir in extras_dir_list: 565 env.Append(CPPPATH=Dir(extra_dir)) 566 567# Workaround for bug in SCons version > 0.97d20071212 568# Scons bug id: 2006 gem5 Bug id: 308 569for root, dirs, files in os.walk(base_dir, topdown=True): 570 Dir(root[len(base_dir) + 1:]) 571 572######################################################################## 573# 574# Walk the tree and execute all SConscripts in subdirectories 575# 576 577here = Dir('.').srcnode().abspath 578for root, dirs, files in os.walk(base_dir, topdown=True): 579 if root == here: 580 # we don't want to recurse back into this SConscript 581 continue 582 583 if 'SConscript' in files: 584 build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:]) 585 Source.set_group(build_dir) 586 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir) 587 588for extra_dir in extras_dir_list: 589 prefix_len = len(dirname(extra_dir)) + 1 590 591 # Also add the corresponding build directory to pick up generated 592 # include files. 593 env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:]))) 594 595 for root, dirs, files in os.walk(extra_dir, topdown=True): 596 # if build lives in the extras directory, don't walk down it 597 if 'build' in dirs: 598 dirs.remove('build') 599 600 if 'SConscript' in files: 601 build_dir = joinpath(env['BUILDDIR'], root[prefix_len:]) 602 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir) 603 604for opt in export_vars: 605 env.ConfigFile(opt) 606 607def makeTheISA(source, target, env): 608 isas = [ src.get_contents() for src in source ] 609 target_isa = env['TARGET_ISA'] 610 def define(isa): 611 return isa.upper() + '_ISA' 612 613 def namespace(isa): 614 return isa[0].upper() + isa[1:].lower() + 'ISA' 615 616 617 code = code_formatter() 618 code('''\ 619#ifndef __CONFIG_THE_ISA_HH__ 620#define __CONFIG_THE_ISA_HH__ 621 622''') 623 624 # create defines for the preprocessing and compile-time determination 625 for i,isa in enumerate(isas): 626 code('#define $0 $1', define(isa), i + 1) 627 code() 628 629 # create an enum for any run-time determination of the ISA, we 630 # reuse the same name as the namespaces 631 code('enum class Arch {') 632 for i,isa in enumerate(isas): 633 if i + 1 == len(isas): 634 code(' $0 = $1', namespace(isa), define(isa)) 635 else: 636 code(' $0 = $1,', namespace(isa), define(isa)) 637 code('};') 638 639 code(''' 640 641#define THE_ISA ${{define(target_isa)}} 642#define TheISA ${{namespace(target_isa)}} 643#define THE_ISA_STR "${{target_isa}}" 644 645#endif // __CONFIG_THE_ISA_HH__''') 646 647 code.write(str(target[0])) 648 649env.Command('config/the_isa.hh', map(Value, all_isa_list), 650 MakeAction(makeTheISA, Transform("CFG ISA", 0))) 651 652def makeTheGPUISA(source, target, env): 653 isas = [ src.get_contents() for src in source ] 654 target_gpu_isa = env['TARGET_GPU_ISA'] 655 def define(isa): 656 return isa.upper() + '_ISA' 657 658 def namespace(isa): 659 return isa[0].upper() + isa[1:].lower() + 'ISA' 660 661 662 code = code_formatter() 663 code('''\ 664#ifndef __CONFIG_THE_GPU_ISA_HH__ 665#define __CONFIG_THE_GPU_ISA_HH__ 666 667''') 668 669 # create defines for the preprocessing and compile-time determination 670 for i,isa in enumerate(isas): 671 code('#define $0 $1', define(isa), i + 1) 672 code() 673 674 # create an enum for any run-time determination of the ISA, we 675 # reuse the same name as the namespaces 676 code('enum class GPUArch {') 677 for i,isa in enumerate(isas): 678 if i + 1 == len(isas): 679 code(' $0 = $1', namespace(isa), define(isa)) 680 else: 681 code(' $0 = $1,', namespace(isa), define(isa)) 682 code('};') 683 684 code(''' 685 686#define THE_GPU_ISA ${{define(target_gpu_isa)}} 687#define TheGpuISA ${{namespace(target_gpu_isa)}} 688#define THE_GPU_ISA_STR "${{target_gpu_isa}}" 689 690#endif // __CONFIG_THE_GPU_ISA_HH__''') 691 692 code.write(str(target[0])) 693 694env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list), 695 MakeAction(makeTheGPUISA, Transform("CFG ISA", 0))) 696 697######################################################################## 698# 699# Prevent any SimObjects from being added after this point, they 700# should all have been added in the SConscripts above 701# 702SimObject.fixed = True 703 704class DictImporter(object): 705 '''This importer takes a dictionary of arbitrary module names that 706 map to arbitrary filenames.''' 707 def __init__(self, modules): 708 self.modules = modules 709 self.installed = set() 710 711 def __del__(self): 712 self.unload() 713 714 def unload(self): 715 import sys 716 for module in self.installed: 717 del sys.modules[module] 718 self.installed = set() 719 720 def find_module(self, fullname, path): 721 if fullname == 'm5.defines': 722 return self 723 724 if fullname == 'm5.objects': 725 return self 726 727 if fullname.startswith('_m5'): 728 return None 729 730 source = self.modules.get(fullname, None) 731 if source is not None and fullname.startswith('m5.objects'): 732 return self 733 734 return None 735 736 def load_module(self, fullname): 737 mod = imp.new_module(fullname) 738 sys.modules[fullname] = mod 739 self.installed.add(fullname) 740 741 mod.__loader__ = self 742 if fullname == 'm5.objects': 743 mod.__path__ = fullname.split('.') 744 return mod 745 746 if fullname == 'm5.defines': 747 mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env) 748 return mod 749 750 source = self.modules[fullname] 751 if source.modname == '__init__': 752 mod.__path__ = source.modpath 753 mod.__file__ = source.abspath 754 755 exec file(source.abspath, 'r') in mod.__dict__ 756 757 return mod 758 759import m5.SimObject 760import m5.params 761from m5.util import code_formatter 762 763m5.SimObject.clear() 764m5.params.clear() 765 766# install the python importer so we can grab stuff from the source 767# tree itself. We can't have SimObjects added after this point or 768# else we won't know about them for the rest of the stuff. 769importer = DictImporter(PySource.modules) 770sys.meta_path[0:0] = [ importer ] 771 772# import all sim objects so we can populate the all_objects list 773# make sure that we're working with a list, then let's sort it 774for modname in SimObject.modnames: 775 exec('from m5.objects import %s' % modname) 776 777# we need to unload all of the currently imported modules so that they 778# will be re-imported the next time the sconscript is run 779importer.unload() 780sys.meta_path.remove(importer) 781 782sim_objects = m5.SimObject.allClasses 783all_enums = m5.params.allEnums 784 785for name,obj in sorted(sim_objects.iteritems()): 786 for param in obj._params.local.values(): 787 # load the ptype attribute now because it depends on the 788 # current version of SimObject.allClasses, but when scons 789 # actually uses the value, all versions of 790 # SimObject.allClasses will have been loaded 791 param.ptype 792 793######################################################################## 794# 795# calculate extra dependencies 796# 797module_depends = ["m5", "m5.SimObject", "m5.params"] 798depends = [ PySource.modules[dep].snode for dep in module_depends ] 799depends.sort(key = lambda x: x.name) 800 801######################################################################## 802# 803# Commands for the basic automatically generated python files 804# 805 806# Generate Python file containing a dict specifying the current 807# buildEnv flags. 808def makeDefinesPyFile(target, source, env): 809 build_env = source[0].get_contents() 810 811 code = code_formatter() 812 code(""" 813import _m5.core 814import m5.util 815 816buildEnv = m5.util.SmartDict($build_env) 817 818compileDate = _m5.core.compileDate 819_globals = globals() 820for key,val in _m5.core.__dict__.iteritems(): 821 if key.startswith('flag_'): 822 flag = key[5:] 823 _globals[flag] = val 824del _globals 825""") 826 code.write(target[0].abspath) 827 828defines_info = Value(build_env) 829# Generate a file with all of the compile options in it 830env.Command('python/m5/defines.py', defines_info, 831 MakeAction(makeDefinesPyFile, Transform("DEFINES", 0))) 832PySource('m5', 'python/m5/defines.py') 833 834# Generate python file containing info about the M5 source code 835def makeInfoPyFile(target, source, env): 836 code = code_formatter() 837 for src in source: 838 data = ''.join(file(src.srcnode().abspath, 'r').xreadlines()) 839 code('$src = ${{repr(data)}}') 840 code.write(str(target[0])) 841 842# Generate a file that wraps the basic top level files 843env.Command('python/m5/info.py', 844 [ '#/COPYING', '#/LICENSE', '#/README', ], 845 MakeAction(makeInfoPyFile, Transform("INFO"))) 846PySource('m5', 'python/m5/info.py') 847 848######################################################################## 849# 850# Create all of the SimObject param headers and enum headers 851# 852 853def createSimObjectParamStruct(target, source, env): 854 assert len(target) == 1 and len(source) == 1 855 856 name = source[0].get_text_contents() 857 obj = sim_objects[name] 858 859 code = code_formatter() 860 obj.cxx_param_decl(code) 861 code.write(target[0].abspath) 862 863def createSimObjectCxxConfig(is_header): 864 def body(target, source, env): 865 assert len(target) == 1 and len(source) == 1 866 867 name = str(source[0].get_contents()) 868 obj = sim_objects[name] 869 870 code = code_formatter() 871 obj.cxx_config_param_file(code, is_header) 872 code.write(target[0].abspath) 873 return body 874 875def createEnumStrings(target, source, env): 876 assert len(target) == 1 and len(source) == 2 877 878 name = source[0].get_text_contents() 879 use_python = source[1].read() 880 obj = all_enums[name] 881 882 code = code_formatter() 883 obj.cxx_def(code) 884 if use_python: 885 obj.pybind_def(code) 886 code.write(target[0].abspath) 887 888def createEnumDecls(target, source, env): 889 assert len(target) == 1 and len(source) == 1 890 891 name = source[0].get_text_contents() 892 obj = all_enums[name] 893 894 code = code_formatter() 895 obj.cxx_decl(code) 896 code.write(target[0].abspath) 897 898def createSimObjectPyBindWrapper(target, source, env): 899 name = source[0].get_text_contents() 900 obj = sim_objects[name] 901 902 code = code_formatter() 903 obj.pybind_decl(code) 904 code.write(target[0].abspath) 905 906# Generate all of the SimObject param C++ struct header files 907params_hh_files = [] 908for name,simobj in sorted(sim_objects.iteritems()): 909 py_source = PySource.modules[simobj.__module__] 910 extra_deps = [ py_source.tnode ] 911 912 hh_file = File('params/%s.hh' % name) 913 params_hh_files.append(hh_file) 914 env.Command(hh_file, Value(name), 915 MakeAction(createSimObjectParamStruct, Transform("SO PARAM"))) 916 env.Depends(hh_file, depends + extra_deps) 917 918# C++ parameter description files 919if GetOption('with_cxx_config'): 920 for name,simobj in sorted(sim_objects.iteritems()): 921 py_source = PySource.modules[simobj.__module__] 922 extra_deps = [ py_source.tnode ] 923 924 cxx_config_hh_file = File('cxx_config/%s.hh' % name) 925 cxx_config_cc_file = File('cxx_config/%s.cc' % name) 926 env.Command(cxx_config_hh_file, Value(name), 927 MakeAction(createSimObjectCxxConfig(True), 928 Transform("CXXCPRHH"))) 929 env.Command(cxx_config_cc_file, Value(name), 930 MakeAction(createSimObjectCxxConfig(False), 931 Transform("CXXCPRCC"))) 932 env.Depends(cxx_config_hh_file, depends + extra_deps + 933 [File('params/%s.hh' % name), File('sim/cxx_config.hh')]) 934 env.Depends(cxx_config_cc_file, depends + extra_deps + 935 [cxx_config_hh_file]) 936 Source(cxx_config_cc_file) 937 938 cxx_config_init_cc_file = File('cxx_config/init.cc') 939 940 def createCxxConfigInitCC(target, source, env): 941 assert len(target) == 1 and len(source) == 1 942 943 code = code_formatter() 944 945 for name,simobj in sorted(sim_objects.iteritems()): 946 if not hasattr(simobj, 'abstract') or not simobj.abstract: 947 code('#include "cxx_config/${name}.hh"') 948 code() 949 code('void cxxConfigInit()') 950 code('{') 951 code.indent() 952 for name,simobj in sorted(sim_objects.iteritems()): 953 not_abstract = not hasattr(simobj, 'abstract') or \ 954 not simobj.abstract 955 if not_abstract and 'type' in simobj.__dict__: 956 code('cxx_config_directory["${name}"] = ' 957 '${name}CxxConfigParams::makeDirectoryEntry();') 958 code.dedent() 959 code('}') 960 code.write(target[0].abspath) 961 962 py_source = PySource.modules[simobj.__module__] 963 extra_deps = [ py_source.tnode ] 964 env.Command(cxx_config_init_cc_file, Value(name), 965 MakeAction(createCxxConfigInitCC, Transform("CXXCINIT"))) 966 cxx_param_hh_files = ["cxx_config/%s.hh" % simobj 967 for name,simobj in sorted(sim_objects.iteritems()) 968 if not hasattr(simobj, 'abstract') or not simobj.abstract] 969 Depends(cxx_config_init_cc_file, cxx_param_hh_files + 970 [File('sim/cxx_config.hh')]) 971 Source(cxx_config_init_cc_file) 972 973# Generate all enum header files 974for name,enum in sorted(all_enums.iteritems()): 975 py_source = PySource.modules[enum.__module__] 976 extra_deps = [ py_source.tnode ] 977 978 cc_file = File('enums/%s.cc' % name) 979 env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])], 980 MakeAction(createEnumStrings, Transform("ENUM STR"))) 981 env.Depends(cc_file, depends + extra_deps) 982 Source(cc_file) 983 984 hh_file = File('enums/%s.hh' % name) 985 env.Command(hh_file, Value(name), 986 MakeAction(createEnumDecls, Transform("ENUMDECL"))) 987 env.Depends(hh_file, depends + extra_deps) 988 989# Generate SimObject Python bindings wrapper files 990if env['USE_PYTHON']: 991 for name,simobj in sorted(sim_objects.iteritems()): 992 py_source = PySource.modules[simobj.__module__] 993 extra_deps = [ py_source.tnode ] 994 cc_file = File('python/_m5/param_%s.cc' % name) 995 env.Command(cc_file, Value(name), 996 MakeAction(createSimObjectPyBindWrapper, 997 Transform("SO PyBind"))) 998 env.Depends(cc_file, depends + extra_deps) 999 Source(cc_file) 1000 1001# Build all protocol buffers if we have got protoc and protobuf available 1002if env['HAVE_PROTOBUF']: 1003 for proto in ProtoBuf.all: 1004 # Use both the source and header as the target, and the .proto 1005 # file as the source. When executing the protoc compiler, also 1006 # specify the proto_path to avoid having the generated files 1007 # include the path. 1008 env.Command([proto.cc_file, proto.hh_file], proto.tnode, 1009 MakeAction('$PROTOC --cpp_out ${TARGET.dir} ' 1010 '--proto_path ${SOURCE.dir} $SOURCE', 1011 Transform("PROTOC"))) 1012 1013 # Add the C++ source file 1014 Source(proto.cc_file, tags=proto.tags) 1015elif ProtoBuf.all: 1016 print('Got protobuf to build, but lacks support!') 1017 Exit(1) 1018 1019# 1020# Handle debug flags 1021# 1022def makeDebugFlagCC(target, source, env): 1023 assert(len(target) == 1 and len(source) == 1) 1024 1025 code = code_formatter() 1026 1027 # delay definition of CompoundFlags until after all the definition 1028 # of all constituent SimpleFlags 1029 comp_code = code_formatter() 1030 1031 # file header 1032 code(''' 1033/* 1034 * DO NOT EDIT THIS FILE! Automatically generated by SCons. 1035 */ 1036 1037#include "base/debug.hh" 1038 1039namespace Debug { 1040 1041''') 1042 1043 for name, flag in sorted(source[0].read().iteritems()): 1044 n, compound, desc = flag 1045 assert n == name 1046 1047 if not compound: 1048 code('SimpleFlag $name("$name", "$desc");') 1049 else: 1050 comp_code('CompoundFlag $name("$name", "$desc",') 1051 comp_code.indent() 1052 last = len(compound) - 1 1053 for i,flag in enumerate(compound): 1054 if i != last: 1055 comp_code('&$flag,') 1056 else: 1057 comp_code('&$flag);') 1058 comp_code.dedent() 1059 1060 code.append(comp_code) 1061 code() 1062 code('} // namespace Debug') 1063 1064 code.write(str(target[0])) 1065 1066def makeDebugFlagHH(target, source, env): 1067 assert(len(target) == 1 and len(source) == 1) 1068 1069 val = eval(source[0].get_contents()) 1070 name, compound, desc = val 1071 1072 code = code_formatter() 1073 1074 # file header boilerplate 1075 code('''\ 1076/* 1077 * DO NOT EDIT THIS FILE! Automatically generated by SCons. 1078 */ 1079 1080#ifndef __DEBUG_${name}_HH__ 1081#define __DEBUG_${name}_HH__ 1082 1083namespace Debug { 1084''') 1085 1086 if compound: 1087 code('class CompoundFlag;') 1088 code('class SimpleFlag;') 1089 1090 if compound: 1091 code('extern CompoundFlag $name;') 1092 for flag in compound: 1093 code('extern SimpleFlag $flag;') 1094 else: 1095 code('extern SimpleFlag $name;') 1096 1097 code(''' 1098} 1099 1100#endif // __DEBUG_${name}_HH__ 1101''') 1102 1103 code.write(str(target[0])) 1104 1105for name,flag in sorted(debug_flags.iteritems()): 1106 n, compound, desc = flag 1107 assert n == name 1108 1109 hh_file = 'debug/%s.hh' % name 1110 env.Command(hh_file, Value(flag), 1111 MakeAction(makeDebugFlagHH, Transform("TRACING", 0))) 1112 1113env.Command('debug/flags.cc', Value(debug_flags), 1114 MakeAction(makeDebugFlagCC, Transform("TRACING", 0))) 1115Source('debug/flags.cc') 1116 1117# version tags 1118tags = \ 1119env.Command('sim/tags.cc', None, 1120 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET', 1121 Transform("VER TAGS"))) 1122env.AlwaysBuild(tags) 1123 1124# Embed python files. All .py files that have been indicated by a 1125# PySource() call in a SConscript need to be embedded into the M5 1126# library. To do that, we compile the file to byte code, marshal the 1127# byte code, compress it, and then generate a c++ file that 1128# inserts the result into an array. 1129def embedPyFile(target, source, env): 1130 def c_str(string): 1131 if string is None: 1132 return "0" 1133 return '"%s"' % string 1134 1135 '''Action function to compile a .py into a code object, marshal 1136 it, compress it, and stick it into an asm file so the code appears 1137 as just bytes with a label in the data section''' 1138 1139 src = file(str(source[0]), 'r').read() 1140 1141 pysource = PySource.tnodes[source[0]] 1142 compiled = compile(src, pysource.abspath, 'exec') 1143 marshalled = marshal.dumps(compiled) 1144 compressed = zlib.compress(marshalled) 1145 data = compressed 1146 sym = pysource.symname 1147 1148 code = code_formatter() 1149 code('''\ 1150#include "sim/init.hh" 1151 1152namespace { 1153 1154''') 1155 blobToCpp(data, 'data_' + sym, code) 1156 code('''\ 1157 1158 1159EmbeddedPython embedded_${sym}( 1160 ${{c_str(pysource.arcname)}}, 1161 ${{c_str(pysource.abspath)}}, 1162 ${{c_str(pysource.modpath)}}, 1163 data_${sym}, 1164 ${{len(data)}}, 1165 ${{len(marshalled)}}); 1166 1167} // anonymous namespace 1168''') 1169 code.write(str(target[0])) 1170 1171for source in PySource.all: 1172 env.Command(source.cpp, source.tnode, 1173 MakeAction(embedPyFile, Transform("EMBED PY"))) 1174 Source(source.cpp, tags=source.tags, add_tags='python') 1175 1176######################################################################## 1177# 1178# Define binaries. Each different build type (debug, opt, etc.) gets 1179# a slightly different build environment. 1180# 1181 1182# List of constructed environments to pass back to SConstruct 1183date_source = Source('base/date.cc', tags=[]) 1184 1185gem5_binary = Gem5('gem5') 1186 1187# Function to create a new build environment as clone of current 1188# environment 'env' with modified object suffix and optional stripped 1189# binary. Additional keyword arguments are appended to corresponding 1190# build environment vars. 1191def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs): 1192 # SCons doesn't know to append a library suffix when there is a '.' in the 1193 # name. Use '_' instead. 1194 libname = 'gem5_' + label 1195 secondary_exename = 'm5.' + label 1196 1197 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's') 1198 new_env.Label = label 1199 new_env.Append(**kwargs) 1200 1201 lib_sources = Source.all.with_tag('gem5 lib') 1202 1203 # Without Python, leave out all Python content from the library 1204 # builds. The option doesn't affect gem5 built as a program 1205 if GetOption('without_python'): 1206 lib_sources = lib_sources.without_tag('python') 1207 1208 static_objs = [] 1209 shared_objs = [] 1210 1211 for s in lib_sources.with_tag(Source.ungrouped_tag): 1212 static_objs.append(s.static(new_env)) 1213 shared_objs.append(s.shared(new_env)) 1214 1215 for group in Source.source_groups: 1216 srcs = lib_sources.with_tag(Source.link_group_tag(group)) 1217 if not srcs: 1218 continue 1219 1220 group_static = [ s.static(new_env) for s in srcs ] 1221 group_shared = [ s.shared(new_env) for s in srcs ] 1222 1223 # If partial linking is disabled, add these sources to the build 1224 # directly, and short circuit this loop. 1225 if disable_partial: 1226 static_objs.extend(group_static) 1227 shared_objs.extend(group_shared) 1228 continue 1229 1230 # Set up the static partially linked objects. 1231 file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial") 1232 target = File(joinpath(group, file_name)) 1233 partial = env.PartialStatic(target=target, source=group_static) 1234 static_objs.extend(partial) 1235 1236 # Set up the shared partially linked objects. 1237 file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial") 1238 target = File(joinpath(group, file_name)) 1239 partial = env.PartialShared(target=target, source=group_shared) 1240 shared_objs.extend(partial) 1241 1242 static_date = date_source.static(new_env) 1243 new_env.Depends(static_date, static_objs) 1244 static_objs.extend(static_date) 1245 1246 shared_date = date_source.shared(new_env) 1247 new_env.Depends(shared_date, shared_objs) 1248 shared_objs.extend(shared_date) 1249 1250 main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ] 1251 1252 # First make a library of everything but main() so other programs can 1253 # link against m5. 1254 static_lib = new_env.StaticLibrary(libname, static_objs) 1255 shared_lib = new_env.SharedLibrary(libname, shared_objs) 1256 1257 # Keep track of the object files generated so far so Executables can 1258 # include them. 1259 new_env['STATIC_OBJS'] = static_objs 1260 new_env['SHARED_OBJS'] = shared_objs 1261 new_env['MAIN_OBJS'] = main_objs 1262 1263 new_env['STATIC_LIB'] = static_lib 1264 new_env['SHARED_LIB'] = shared_lib 1265 1266 # Record some settings for building Executables. 1267 new_env['EXE_SUFFIX'] = label 1268 new_env['STRIP_EXES'] = strip 1269 1270 for cls in ExecutableMeta.all: 1271 cls.declare_all(new_env) 1272 1273 new_env.M5Binary = File(gem5_binary.path(new_env)) 1274 1275 new_env.Command(secondary_exename, new_env.M5Binary, 1276 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK"))) 1277 1278 # Set up regression tests. 1279 SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'), 1280 variant_dir=Dir('tests').Dir(new_env.Label), 1281 exports={ 'env' : new_env }, duplicate=False) 1282 1283# Start out with the compiler flags common to all compilers, 1284# i.e. they all use -g for opt and -g -pg for prof 1285ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'], 1286 'perf' : ['-g']} 1287 1288# Start out with the linker flags common to all linkers, i.e. -pg for 1289# prof, and -lprofiler for perf. The -lprofile flag is surrounded by 1290# no-as-needed and as-needed as the binutils linker is too clever and 1291# simply doesn't link to the library otherwise. 1292ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'], 1293 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']} 1294 1295# For Link Time Optimization, the optimisation flags used to compile 1296# individual files are decoupled from those used at link time 1297# (i.e. you can compile with -O3 and perform LTO with -O0), so we need 1298# to also update the linker flags based on the target. 1299if env['GCC']: 1300 if sys.platform == 'sunos5': 1301 ccflags['debug'] += ['-gstabs+'] 1302 else: 1303 ccflags['debug'] += ['-ggdb3'] 1304 ldflags['debug'] += ['-O0'] 1305 # opt, fast, prof and perf all share the same cc flags, also add 1306 # the optimization to the ldflags as LTO defers the optimization 1307 # to link time 1308 for target in ['opt', 'fast', 'prof', 'perf']: 1309 ccflags[target] += ['-O3'] 1310 ldflags[target] += ['-O3'] 1311 1312 ccflags['fast'] += env['LTO_CCFLAGS'] 1313 ldflags['fast'] += env['LTO_LDFLAGS'] 1314elif env['CLANG']: 1315 ccflags['debug'] += ['-g', '-O0'] 1316 # opt, fast, prof and perf all share the same cc flags 1317 for target in ['opt', 'fast', 'prof', 'perf']: 1318 ccflags[target] += ['-O3'] 1319else: 1320 print('Unknown compiler, please fix compiler options') 1321 Exit(1) 1322 1323 1324# To speed things up, we only instantiate the build environments we 1325# need. We try to identify the needed environment for each target; if 1326# we can't, we fall back on instantiating all the environments just to 1327# be safe. 1328target_types = ['debug', 'opt', 'fast', 'prof', 'perf'] 1329obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof', 1330 'gpo' : 'perf'} 1331 1332def identifyTarget(t): 1333 ext = t.split('.')[-1] 1334 if ext in target_types: 1335 return ext 1336 if obj2target.has_key(ext): 1337 return obj2target[ext] 1338 match = re.search(r'/tests/([^/]+)/', t) 1339 if match and match.group(1) in target_types: 1340 return match.group(1) 1341 return 'all' 1342 1343needed_envs = [identifyTarget(target) for target in BUILD_TARGETS] 1344if 'all' in needed_envs: 1345 needed_envs += target_types 1346 1347disable_partial = False 1348if env['PLATFORM'] == 'darwin': 1349 # Up until Apple LLVM version 10.0.0 (clang-1000.11.45.5), partial 1350 # linked objects do not expose symbols that are marked with the 1351 # hidden visibility and consequently building gem5 on Mac OS 1352 # fails. As a workaround, we disable partial linking, however, we 1353 # may want to revisit in the future. 1354 disable_partial = True 1355 1356# Debug binary 1357if 'debug' in needed_envs: 1358 makeEnv(env, 'debug', '.do', 1359 CCFLAGS = Split(ccflags['debug']), 1360 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'], 1361 LINKFLAGS = Split(ldflags['debug']), 1362 disable_partial=disable_partial) 1363 1364# Optimized binary 1365if 'opt' in needed_envs: 1366 makeEnv(env, 'opt', '.o', 1367 CCFLAGS = Split(ccflags['opt']), 1368 CPPDEFINES = ['TRACING_ON=1'], 1369 LINKFLAGS = Split(ldflags['opt']), 1370 disable_partial=disable_partial) 1371 1372# "Fast" binary 1373if 'fast' in needed_envs: 1374 disable_partial = disable_partial and \ 1375 env.get('BROKEN_INCREMENTAL_LTO', False) and \ 1376 GetOption('force_lto') 1377 makeEnv(env, 'fast', '.fo', strip = True, 1378 CCFLAGS = Split(ccflags['fast']), 1379 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1380 LINKFLAGS = Split(ldflags['fast']), 1381 disable_partial=disable_partial) 1382 1383# Profiled binary using gprof 1384if 'prof' in needed_envs: 1385 makeEnv(env, 'prof', '.po', 1386 CCFLAGS = Split(ccflags['prof']), 1387 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1388 LINKFLAGS = Split(ldflags['prof']), 1389 disable_partial=disable_partial) 1390 1391# Profiled binary using google-pprof 1392if 'perf' in needed_envs: 1393 makeEnv(env, 'perf', '.gpo', 1394 CCFLAGS = Split(ccflags['perf']), 1395 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1396 LINKFLAGS = Split(ldflags['perf']), 1397 disable_partial=disable_partial) 1398