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