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