SConscript revision 12077
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 45# This file defines how to build a particular configuration of gem5 46# based on variable settings in the 'env' build environment. 47 48Import('*') 49 50# Children need to see the environment 51Export('env') 52 53build_env = [(opt, env[opt]) for opt in export_vars] 54 55from m5.util import code_formatter, compareVersions 56 57######################################################################## 58# Code for adding source files of various types 59# 60# When specifying a source file of some type, a set of guards can be 61# specified for that file. When get() is used to find the files, if 62# get specifies a set of filters, only files that match those filters 63# will be accepted (unspecified filters on files are assumed to be 64# false). Current filters are: 65# main -- specifies the gem5 main() function 66# skip_lib -- do not put this file into the gem5 library 67# skip_no_python -- do not put this file into a no_python library 68# as it embeds compiled Python 69# <unittest> -- unit tests use filters based on the unit test name 70# 71# A parent can now be specified for a source file and default filter 72# values will be retrieved recursively from parents (children override 73# parents). 74# 75def guarded_source_iterator(sources, **guards): 76 '''Iterate over a set of sources, gated by a set of guards.''' 77 for src in sources: 78 for flag,value in guards.iteritems(): 79 # if the flag is found and has a different value, skip 80 # this file 81 if src.all_guards.get(flag, False) != value: 82 break 83 else: 84 yield src 85 86class SourceMeta(type): 87 '''Meta class for source files that keeps track of all files of a 88 particular type and has a get function for finding all functions 89 of a certain type that match a set of guards''' 90 def __init__(cls, name, bases, dict): 91 super(SourceMeta, cls).__init__(name, bases, dict) 92 cls.all = [] 93 94 def get(cls, **guards): 95 '''Find all files that match the specified guards. If a source 96 file does not specify a flag, the default is False''' 97 for s in guarded_source_iterator(cls.all, **guards): 98 yield s 99 100class SourceFile(object): 101 '''Base object that encapsulates the notion of a source file. 102 This includes, the source node, target node, various manipulations 103 of those. A source file also specifies a set of guards which 104 describing which builds the source file applies to. A parent can 105 also be specified to get default guards from''' 106 __metaclass__ = SourceMeta 107 def __init__(self, source, parent=None, **guards): 108 self.guards = guards 109 self.parent = parent 110 111 tnode = source 112 if not isinstance(source, SCons.Node.FS.File): 113 tnode = File(source) 114 115 self.tnode = tnode 116 self.snode = tnode.srcnode() 117 118 for base in type(self).__mro__: 119 if issubclass(base, SourceFile): 120 base.all.append(self) 121 122 @property 123 def filename(self): 124 return str(self.tnode) 125 126 @property 127 def dirname(self): 128 return dirname(self.filename) 129 130 @property 131 def basename(self): 132 return basename(self.filename) 133 134 @property 135 def extname(self): 136 index = self.basename.rfind('.') 137 if index <= 0: 138 # dot files aren't extensions 139 return self.basename, None 140 141 return self.basename[:index], self.basename[index+1:] 142 143 @property 144 def all_guards(self): 145 '''find all guards for this object getting default values 146 recursively from its parents''' 147 guards = {} 148 if self.parent: 149 guards.update(self.parent.guards) 150 guards.update(self.guards) 151 return guards 152 153 def __lt__(self, other): return self.filename < other.filename 154 def __le__(self, other): return self.filename <= other.filename 155 def __gt__(self, other): return self.filename > other.filename 156 def __ge__(self, other): return self.filename >= other.filename 157 def __eq__(self, other): return self.filename == other.filename 158 def __ne__(self, other): return self.filename != other.filename 159 160 @staticmethod 161 def done(): 162 def disabled(cls, name, *ignored): 163 raise RuntimeError("Additional SourceFile '%s'" % name,\ 164 "declared, but targets deps are already fixed.") 165 SourceFile.__init__ = disabled 166 167 168class Source(SourceFile): 169 current_group = None 170 source_groups = { None : [] } 171 172 @classmethod 173 def set_group(cls, group): 174 if not group in Source.source_groups: 175 Source.source_groups[group] = [] 176 Source.current_group = group 177 178 '''Add a c/c++ source file to the build''' 179 def __init__(self, source, Werror=True, **guards): 180 '''specify the source file, and any guards''' 181 super(Source, self).__init__(source, **guards) 182 183 self.Werror = Werror 184 185 Source.source_groups[Source.current_group].append(self) 186 187class PySource(SourceFile): 188 '''Add a python source file to the named package''' 189 invalid_sym_char = re.compile('[^A-z0-9_]') 190 modules = {} 191 tnodes = {} 192 symnames = {} 193 194 def __init__(self, package, source, **guards): 195 '''specify the python package, the source file, and any guards''' 196 super(PySource, self).__init__(source, **guards) 197 198 modname,ext = self.extname 199 assert ext == 'py' 200 201 if package: 202 path = package.split('.') 203 else: 204 path = [] 205 206 modpath = path[:] 207 if modname != '__init__': 208 modpath += [ modname ] 209 modpath = '.'.join(modpath) 210 211 arcpath = path + [ self.basename ] 212 abspath = self.snode.abspath 213 if not exists(abspath): 214 abspath = self.tnode.abspath 215 216 self.package = package 217 self.modname = modname 218 self.modpath = modpath 219 self.arcname = joinpath(*arcpath) 220 self.abspath = abspath 221 self.compiled = File(self.filename + 'c') 222 self.cpp = File(self.filename + '.cc') 223 self.symname = PySource.invalid_sym_char.sub('_', modpath) 224 225 PySource.modules[modpath] = self 226 PySource.tnodes[self.tnode] = self 227 PySource.symnames[self.symname] = self 228 229class SimObject(PySource): 230 '''Add a SimObject python file as a python source object and add 231 it to a list of sim object modules''' 232 233 fixed = False 234 modnames = [] 235 236 def __init__(self, source, **guards): 237 '''Specify the source file and any guards (automatically in 238 the m5.objects package)''' 239 super(SimObject, self).__init__('m5.objects', source, **guards) 240 if self.fixed: 241 raise AttributeError, "Too late to call SimObject now." 242 243 bisect.insort_right(SimObject.modnames, self.modname) 244 245class ProtoBuf(SourceFile): 246 '''Add a Protocol Buffer to build''' 247 248 def __init__(self, source, **guards): 249 '''Specify the source file, and any guards''' 250 super(ProtoBuf, self).__init__(source, **guards) 251 252 # Get the file name and the extension 253 modname,ext = self.extname 254 assert ext == 'proto' 255 256 # Currently, we stick to generating the C++ headers, so we 257 # only need to track the source and header. 258 self.cc_file = File(modname + '.pb.cc') 259 self.hh_file = File(modname + '.pb.h') 260 261class UnitTest(object): 262 '''Create a UnitTest''' 263 264 all = [] 265 def __init__(self, target, *sources, **kwargs): 266 '''Specify the target name and any sources. Sources that are 267 not SourceFiles are evalued with Source(). All files are 268 guarded with a guard of the same name as the UnitTest 269 target.''' 270 271 srcs = [] 272 for src in sources: 273 if not isinstance(src, SourceFile): 274 src = Source(src, skip_lib=True) 275 src.guards[target] = True 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, **proto.guards) 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, skip_no_python=True) 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', skip_lib=True) 947 948# Capture this directory for the closure makeEnv, otherwise when it is 949# called, it won't know what directory it should use. 950variant_dir = Dir('.').path 951def variant(*path): 952 return os.path.join(variant_dir, *path) 953def variantd(*path): 954 return variant(*path)+'/' 955 956# Function to create a new build environment as clone of current 957# environment 'env' with modified object suffix and optional stripped 958# binary. Additional keyword arguments are appended to corresponding 959# build environment vars. 960def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs): 961 # SCons doesn't know to append a library suffix when there is a '.' in the 962 # name. Use '_' instead. 963 libname = variant('gem5_' + label) 964 exename = variant('gem5.' + label) 965 secondary_exename = variant('m5.' + label) 966 967 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's') 968 new_env.Label = label 969 new_env.Append(**kwargs) 970 971 if env['GCC']: 972 # The address sanitizer is available for gcc >= 4.8 973 if GetOption('with_asan'): 974 if GetOption('with_ubsan') and \ 975 compareVersions(env['GCC_VERSION'], '4.9') >= 0: 976 new_env.Append(CCFLAGS=['-fsanitize=address,undefined', 977 '-fno-omit-frame-pointer']) 978 new_env.Append(LINKFLAGS='-fsanitize=address,undefined') 979 else: 980 new_env.Append(CCFLAGS=['-fsanitize=address', 981 '-fno-omit-frame-pointer']) 982 new_env.Append(LINKFLAGS='-fsanitize=address') 983 # Only gcc >= 4.9 supports UBSan, so check both the version 984 # and the command-line option before adding the compiler and 985 # linker flags. 986 elif GetOption('with_ubsan') and \ 987 compareVersions(env['GCC_VERSION'], '4.9') >= 0: 988 new_env.Append(CCFLAGS='-fsanitize=undefined') 989 new_env.Append(LINKFLAGS='-fsanitize=undefined') 990 991 992 if env['CLANG']: 993 # We require clang >= 3.1, so there is no need to check any 994 # versions here. 995 if GetOption('with_ubsan'): 996 if GetOption('with_asan'): 997 new_env.Append(CCFLAGS=['-fsanitize=address,undefined', 998 '-fno-omit-frame-pointer']) 999 new_env.Append(LINKFLAGS='-fsanitize=address,undefined') 1000 else: 1001 new_env.Append(CCFLAGS='-fsanitize=undefined') 1002 new_env.Append(LINKFLAGS='-fsanitize=undefined') 1003 1004 elif GetOption('with_asan'): 1005 new_env.Append(CCFLAGS=['-fsanitize=address', 1006 '-fno-omit-frame-pointer']) 1007 new_env.Append(LINKFLAGS='-fsanitize=address') 1008 1009 werror_env = new_env.Clone() 1010 # Treat warnings as errors but white list some warnings that we 1011 # want to allow (e.g., deprecation warnings). 1012 werror_env.Append(CCFLAGS=['-Werror', 1013 '-Wno-error=deprecated-declarations', 1014 '-Wno-error=deprecated', 1015 ]) 1016 1017 def make_obj(source, static, extra_deps = None): 1018 '''This function adds the specified source to the correct 1019 build environment, and returns the corresponding SCons Object 1020 nodes''' 1021 1022 if source.Werror: 1023 env = werror_env 1024 else: 1025 env = new_env 1026 1027 if static: 1028 obj = env.StaticObject(source.tnode) 1029 else: 1030 obj = env.SharedObject(source.tnode) 1031 1032 if extra_deps: 1033 env.Depends(obj, extra_deps) 1034 1035 return obj 1036 1037 lib_guards = {'main': False, 'skip_lib': False} 1038 1039 # Without Python, leave out all Python content from the library 1040 # builds. The option doesn't affect gem5 built as a program 1041 if GetOption('without_python'): 1042 lib_guards['skip_no_python'] = False 1043 1044 static_objs = [] 1045 shared_objs = [] 1046 for s in guarded_source_iterator(Source.source_groups[None], **lib_guards): 1047 static_objs.append(make_obj(s, True)) 1048 shared_objs.append(make_obj(s, False)) 1049 1050 partial_objs = [] 1051 for group, all_srcs in Source.source_groups.iteritems(): 1052 # If these are the ungrouped source files, skip them. 1053 if not group: 1054 continue 1055 1056 # Get a list of the source files compatible with the current guards. 1057 srcs = [ s for s in guarded_source_iterator(all_srcs, **lib_guards) ] 1058 # If there aren't any left, skip this group. 1059 if not srcs: 1060 continue 1061 1062 # If partial linking is disabled, add these sources to the build 1063 # directly, and short circuit this loop. 1064 if disable_partial: 1065 for s in srcs: 1066 static_objs.append(make_obj(s, True)) 1067 shared_objs.append(make_obj(s, False)) 1068 continue 1069 1070 # Set up the static partially linked objects. 1071 source_objs = [ make_obj(s, True) for s in srcs ] 1072 file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial") 1073 target = File(joinpath(group, file_name)) 1074 partial = env.PartialStatic(target=target, source=source_objs) 1075 static_objs.append(partial) 1076 1077 # Set up the shared partially linked objects. 1078 source_objs = [ make_obj(s, False) for s in srcs ] 1079 file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial") 1080 target = File(joinpath(group, file_name)) 1081 partial = env.PartialShared(target=target, source=source_objs) 1082 shared_objs.append(partial) 1083 1084 static_date = make_obj(date_source, static=True, extra_deps=static_objs) 1085 static_objs.append(static_date) 1086 1087 shared_date = make_obj(date_source, static=False, extra_deps=shared_objs) 1088 shared_objs.append(shared_date) 1089 1090 # First make a library of everything but main() so other programs can 1091 # link against m5. 1092 static_lib = new_env.StaticLibrary(libname, static_objs) 1093 shared_lib = new_env.SharedLibrary(libname, shared_objs) 1094 1095 # Now link a stub with main() and the static library. 1096 main_objs = [ make_obj(s, True) for s in Source.get(main=True) ] 1097 1098 for test in UnitTest.all: 1099 flags = { test.target : True } 1100 test_sources = Source.get(**flags) 1101 test_objs = [ make_obj(s, static=True) for s in test_sources ] 1102 if test.main: 1103 test_objs += main_objs 1104 path = variant('unittest/%s.%s' % (test.target, label)) 1105 new_env.Program(path, test_objs + static_objs) 1106 1107 progname = exename 1108 if strip: 1109 progname += '.unstripped' 1110 1111 targets = new_env.Program(progname, main_objs + static_objs) 1112 1113 if strip: 1114 if sys.platform == 'sunos5': 1115 cmd = 'cp $SOURCE $TARGET; strip $TARGET' 1116 else: 1117 cmd = 'strip $SOURCE -o $TARGET' 1118 targets = new_env.Command(exename, progname, 1119 MakeAction(cmd, Transform("STRIP"))) 1120 1121 new_env.Command(secondary_exename, exename, 1122 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK"))) 1123 1124 new_env.M5Binary = targets[0] 1125 1126 # Set up regression tests. 1127 SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'), 1128 variant_dir=variantd('tests', new_env.Label), 1129 exports={ 'env' : new_env }, duplicate=False) 1130 1131# Start out with the compiler flags common to all compilers, 1132# i.e. they all use -g for opt and -g -pg for prof 1133ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'], 1134 'perf' : ['-g']} 1135 1136# Start out with the linker flags common to all linkers, i.e. -pg for 1137# prof, and -lprofiler for perf. The -lprofile flag is surrounded by 1138# no-as-needed and as-needed as the binutils linker is too clever and 1139# simply doesn't link to the library otherwise. 1140ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'], 1141 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']} 1142 1143# For Link Time Optimization, the optimisation flags used to compile 1144# individual files are decoupled from those used at link time 1145# (i.e. you can compile with -O3 and perform LTO with -O0), so we need 1146# to also update the linker flags based on the target. 1147if env['GCC']: 1148 if sys.platform == 'sunos5': 1149 ccflags['debug'] += ['-gstabs+'] 1150 else: 1151 ccflags['debug'] += ['-ggdb3'] 1152 ldflags['debug'] += ['-O0'] 1153 # opt, fast, prof and perf all share the same cc flags, also add 1154 # the optimization to the ldflags as LTO defers the optimization 1155 # to link time 1156 for target in ['opt', 'fast', 'prof', 'perf']: 1157 ccflags[target] += ['-O3'] 1158 ldflags[target] += ['-O3'] 1159 1160 ccflags['fast'] += env['LTO_CCFLAGS'] 1161 ldflags['fast'] += env['LTO_LDFLAGS'] 1162elif env['CLANG']: 1163 ccflags['debug'] += ['-g', '-O0'] 1164 # opt, fast, prof and perf all share the same cc flags 1165 for target in ['opt', 'fast', 'prof', 'perf']: 1166 ccflags[target] += ['-O3'] 1167else: 1168 print 'Unknown compiler, please fix compiler options' 1169 Exit(1) 1170 1171 1172# To speed things up, we only instantiate the build environments we 1173# need. We try to identify the needed environment for each target; if 1174# we can't, we fall back on instantiating all the environments just to 1175# be safe. 1176target_types = ['debug', 'opt', 'fast', 'prof', 'perf'] 1177obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof', 1178 'gpo' : 'perf'} 1179 1180def identifyTarget(t): 1181 ext = t.split('.')[-1] 1182 if ext in target_types: 1183 return ext 1184 if obj2target.has_key(ext): 1185 return obj2target[ext] 1186 match = re.search(r'/tests/([^/]+)/', t) 1187 if match and match.group(1) in target_types: 1188 return match.group(1) 1189 return 'all' 1190 1191needed_envs = [identifyTarget(target) for target in BUILD_TARGETS] 1192if 'all' in needed_envs: 1193 needed_envs += target_types 1194 1195def makeEnvirons(target, source, env): 1196 # cause any later Source() calls to be fatal, as a diagnostic. 1197 Source.done() 1198 1199 # Debug binary 1200 if 'debug' in needed_envs: 1201 makeEnv(env, 'debug', '.do', 1202 CCFLAGS = Split(ccflags['debug']), 1203 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'], 1204 LINKFLAGS = Split(ldflags['debug'])) 1205 1206 # Optimized binary 1207 if 'opt' in needed_envs: 1208 makeEnv(env, 'opt', '.o', 1209 CCFLAGS = Split(ccflags['opt']), 1210 CPPDEFINES = ['TRACING_ON=1'], 1211 LINKFLAGS = Split(ldflags['opt'])) 1212 1213 # "Fast" binary 1214 if 'fast' in needed_envs: 1215 disable_partial = \ 1216 env.get('BROKEN_INCREMENTAL_LTO', False) and \ 1217 GetOption('force_lto') 1218 makeEnv(env, 'fast', '.fo', strip = True, 1219 CCFLAGS = Split(ccflags['fast']), 1220 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1221 LINKFLAGS = Split(ldflags['fast']), 1222 disable_partial=disable_partial) 1223 1224 # Profiled binary using gprof 1225 if 'prof' in needed_envs: 1226 makeEnv(env, 'prof', '.po', 1227 CCFLAGS = Split(ccflags['prof']), 1228 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1229 LINKFLAGS = Split(ldflags['prof'])) 1230 1231 # Profiled binary using google-pprof 1232 if 'perf' in needed_envs: 1233 makeEnv(env, 'perf', '.gpo', 1234 CCFLAGS = Split(ccflags['perf']), 1235 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1236 LINKFLAGS = Split(ldflags['perf'])) 1237 1238# The MakeEnvirons Builder defers the full dependency collection until 1239# after processing the ISA definition (due to dynamically generated 1240# source files). Add this dependency to all targets so they will wait 1241# until the environments are completely set up. Otherwise, a second 1242# process (e.g. -j2 or higher) will try to compile the requested target, 1243# not know how, and fail. 1244env.Append(BUILDERS = {'MakeEnvirons' : 1245 Builder(action=MakeAction(makeEnvirons, 1246 Transform("ENVIRONS", 1)))}) 1247 1248isa_target = '#${VARIANT_NAME}-deps' 1249environs = '#${VARIANT_NAME}-environs' 1250env.Depends('#all-deps', isa_target) 1251env.Depends('#all-environs', environs) 1252env.ScanISA(isa_target, File('arch/%s/generated/inc.d' % env['TARGET_ISA'])) 1253envSetup = env.MakeEnvirons(environs, isa_target) 1254 1255# make sure no -deps targets occur before all ISAs are complete 1256env.Depends(isa_target, '#all-isas') 1257# likewise for -environs targets and all the -deps targets 1258env.Depends(environs, '#all-deps') 1259