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