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