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