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