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