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