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