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