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