SConscript revision 13706:4c1d26d1766e
12SN/A# -*- mode:python -*- 21762SN/A 32SN/A# Copyright (c) 2018 ARM Limited 42SN/A# 52SN/A# The license below extends only to copyright in the software and shall 62SN/A# not be construed as granting a license to any other intellectual 72SN/A# property including but not limited to intellectual property relating 82SN/A# to a hardware implementation of the functionality of the software 92SN/A# licensed hereunder. You may use the software subject to the license 102SN/A# terms below provided that you ensure that this notice is replicated 112SN/A# unmodified and in its entirety in all distributions of the software, 122SN/A# modified or unmodified, in source code or in binary form. 132SN/A# 142SN/A# Copyright (c) 2004-2005 The Regents of The University of Michigan 152SN/A# All rights reserved. 162SN/A# 172SN/A# Redistribution and use in source and binary forms, with or without 182SN/A# modification, are permitted provided that the following conditions are 192SN/A# met: redistributions of source code must retain the above copyright 202SN/A# notice, this list of conditions and the following disclaimer; 212SN/A# redistributions in binary form must reproduce the above copyright 222SN/A# notice, this list of conditions and the following disclaimer in the 232SN/A# documentation and/or other materials provided with the distribution; 242SN/A# neither the name of the copyright holders nor the names of its 252SN/A# contributors may be used to endorse or promote products derived from 262SN/A# this software without specific prior written permission. 272665Ssaidi@eecs.umich.edu# 282665Ssaidi@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 292665Ssaidi@eecs.umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 302SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 312SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 321078SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 331078SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 341078SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 351114SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 361078SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 371114SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 381114SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 391114SN/A# 401114SN/A# Authors: Nathan Binkert 416216Snate@binkert.org 421114SN/Afrom __future__ import print_function 431078SN/A 441078SN/Aimport array 451078SN/Aimport bisect 461078SN/Aimport functools 471078SN/Aimport imp 481078SN/Aimport marshal 491078SN/Aimport os 501078SN/Aimport re 511078SN/Aimport subprocess 521078SN/Aimport sys 531078SN/Aimport zlib 541078SN/A 551078SN/Afrom os.path import basename, dirname, exists, isdir, isfile, join as joinpath 561078SN/A 571078SN/Aimport SCons 582SN/A 591114SN/Afrom gem5_scons import Transform 602SN/A 611114SN/A# This file defines how to build a particular configuration of gem5 621114SN/A# based on variable settings in the 'env' build environment. 631114SN/A 641114SN/AImport('*') 651114SN/A 661114SN/A# Children need to see the environment 671114SN/AExport('env') 681078SN/A 691114SN/Abuild_env = [(opt, env[opt]) for opt in export_vars] 701114SN/A 711114SN/Afrom m5.util import code_formatter, compareVersions 721114SN/A 731114SN/A######################################################################## 741114SN/A# Code for adding source files of various types 751114SN/A# 761079SN/A# When specifying a source file of some type, a set of tags can be 771114SN/A# specified for that file. 781114SN/A 791114SN/Aclass SourceFilter(object): 801114SN/A def __init__(self, predicate): 811114SN/A self.predicate = predicate 821114SN/A 831114SN/A def __or__(self, other): 841114SN/A return SourceFilter(lambda tags: self.predicate(tags) or 851114SN/A other.predicate(tags)) 861114SN/A 871137SN/A def __and__(self, other): 881137SN/A return SourceFilter(lambda tags: self.predicate(tags) and 891137SN/A other.predicate(tags)) 901137SN/A 911137SN/Adef with_tags_that(predicate): 921137SN/A '''Return a list of sources with tags that satisfy a predicate.''' 931137SN/A return SourceFilter(predicate) 941137SN/A 951137SN/Adef with_any_tags(*tags): 961137SN/A '''Return a list of sources with any of the supplied tags.''' 971137SN/A return SourceFilter(lambda stags: len(set(tags) & stags) > 0) 981137SN/A 991137SN/Adef with_all_tags(*tags): 1001114SN/A '''Return a list of sources with all of the supplied tags.''' 1011114SN/A return SourceFilter(lambda stags: set(tags) <= stags) 1021114SN/A 1031114SN/Adef with_tag(tag): 1041114SN/A '''Return a list of sources with the supplied tag.''' 1051114SN/A return SourceFilter(lambda stags: tag in stags) 1061078SN/A 1071078SN/Adef without_tags(*tags): 1081114SN/A '''Return a list of sources without any of the supplied tags.''' 1091114SN/A return SourceFilter(lambda stags: len(set(tags) & stags) == 0) 1101079SN/A 1111114SN/Adef without_tag(tag): 1121079SN/A '''Return a list of sources with the supplied tag.''' 1131079SN/A return SourceFilter(lambda stags: tag not in stags) 1141079SN/A 1151079SN/Asource_filter_factories = { 1161079SN/A 'with_tags_that': with_tags_that, 1171078SN/A 'with_any_tags': with_any_tags, 1181078SN/A 'with_all_tags': with_all_tags, 1191114SN/A 'with_tag': with_tag, 1201114SN/A 'without_tags': without_tags, 1211114SN/A 'without_tag': without_tag, 1221114SN/A} 1232566SN/A 1241114SN/AExport(source_filter_factories) 1251114SN/A 1261114SN/Aclass SourceList(list): 1272566SN/A def apply_filter(self, f): 1281114SN/A def match(source): 1291114SN/A return f.predicate(source.tags) 1301114SN/A return SourceList(filter(match, self)) 1311114SN/A 1321114SN/A def __getattr__(self, name): 1331114SN/A func = source_filter_factories.get(name, None) 1341114SN/A if not func: 1351114SN/A raise AttributeError 1361114SN/A 1372566SN/A @functools.wraps(func) 1381114SN/A def wrapper(*args, **kwargs): 1392566SN/A return self.apply_filter(func(*args, **kwargs)) 1402566SN/A return wrapper 1411114SN/A 1421114SN/Aclass SourceMeta(type): 1435782Ssaidi@eecs.umich.edu '''Meta class for source files that keeps track of all files of a 1445782Ssaidi@eecs.umich.edu particular type.''' 1451114SN/A def __init__(cls, name, bases, dict): 1461114SN/A super(SourceMeta, cls).__init__(name, bases, dict) 1471114SN/A cls.all = SourceList() 1481114SN/A 1491114SN/Aclass SourceFile(object): 1507777Sgblack@eecs.umich.edu '''Base object that encapsulates the notion of a source file. 1517777Sgblack@eecs.umich.edu This includes, the source node, target node, various manipulations 1527777Sgblack@eecs.umich.edu of those. A source file also specifies a set of tags which 1537777Sgblack@eecs.umich.edu describing arbitrary properties of the source file.''' 1547777Sgblack@eecs.umich.edu __metaclass__ = SourceMeta 1557777Sgblack@eecs.umich.edu 1567777Sgblack@eecs.umich.edu static_objs = {} 1577777Sgblack@eecs.umich.edu shared_objs = {} 1587777Sgblack@eecs.umich.edu 1597777Sgblack@eecs.umich.edu def __init__(self, source, tags=None, add_tags=None): 1607777Sgblack@eecs.umich.edu if tags is None: 1617777Sgblack@eecs.umich.edu tags='gem5 lib' 1627777Sgblack@eecs.umich.edu if isinstance(tags, basestring): 1637777Sgblack@eecs.umich.edu tags = set([tags]) 1647777Sgblack@eecs.umich.edu if not isinstance(tags, set): 1657777Sgblack@eecs.umich.edu tags = set(tags) 1667777Sgblack@eecs.umich.edu self.tags = tags 1677777Sgblack@eecs.umich.edu 1687777Sgblack@eecs.umich.edu if add_tags: 1697777Sgblack@eecs.umich.edu if isinstance(add_tags, basestring): 1707777Sgblack@eecs.umich.edu add_tags = set([add_tags]) 1717777Sgblack@eecs.umich.edu if not isinstance(add_tags, set): 1727777Sgblack@eecs.umich.edu add_tags = set(add_tags) 1737777Sgblack@eecs.umich.edu self.tags |= add_tags 1747777Sgblack@eecs.umich.edu 1757777Sgblack@eecs.umich.edu tnode = source 1767777Sgblack@eecs.umich.edu if not isinstance(source, SCons.Node.FS.File): 1777777Sgblack@eecs.umich.edu tnode = File(source) 1787777Sgblack@eecs.umich.edu 1797777Sgblack@eecs.umich.edu self.tnode = tnode 1807777Sgblack@eecs.umich.edu self.snode = tnode.srcnode() 1817777Sgblack@eecs.umich.edu 1827777Sgblack@eecs.umich.edu for base in type(self).__mro__: 1837777Sgblack@eecs.umich.edu if issubclass(base, SourceFile): 1847777Sgblack@eecs.umich.edu base.all.append(self) 1857777Sgblack@eecs.umich.edu 1867777Sgblack@eecs.umich.edu def static(self, env): 1877777Sgblack@eecs.umich.edu key = (self.tnode, env['OBJSUFFIX']) 1887777Sgblack@eecs.umich.edu if not key in self.static_objs: 1897777Sgblack@eecs.umich.edu self.static_objs[key] = env.StaticObject(self.tnode) 1907777Sgblack@eecs.umich.edu return self.static_objs[key] 1917777Sgblack@eecs.umich.edu 1927777Sgblack@eecs.umich.edu def shared(self, env): 1937777Sgblack@eecs.umich.edu key = (self.tnode, env['OBJSUFFIX']) 1947777Sgblack@eecs.umich.edu if not key in self.shared_objs: 1957777Sgblack@eecs.umich.edu self.shared_objs[key] = env.SharedObject(self.tnode) 1967777Sgblack@eecs.umich.edu return self.shared_objs[key] 1977777Sgblack@eecs.umich.edu 1987777Sgblack@eecs.umich.edu @property 1997777Sgblack@eecs.umich.edu def filename(self): 2007777Sgblack@eecs.umich.edu return str(self.tnode) 2017777Sgblack@eecs.umich.edu 2027777Sgblack@eecs.umich.edu @property 2037777Sgblack@eecs.umich.edu def dirname(self): 2047777Sgblack@eecs.umich.edu return dirname(self.filename) 2057777Sgblack@eecs.umich.edu 2067777Sgblack@eecs.umich.edu @property 2077777Sgblack@eecs.umich.edu def basename(self): 2087777Sgblack@eecs.umich.edu return basename(self.filename) 2091114SN/A 2101114SN/A @property 2111078SN/A def extname(self): 2121078SN/A index = self.basename.rfind('.') 2131092SN/A if index <= 0: 2141078SN/A # dot files aren't extensions 2151078SN/A return self.basename, None 2161078SN/A 2171078SN/A return self.basename[:index], self.basename[index+1:] 2181078SN/A 2191078SN/A def __lt__(self, other): return self.filename < other.filename 2201078SN/A def __le__(self, other): return self.filename <= other.filename 2211092SN/A def __gt__(self, other): return self.filename > other.filename 2221078SN/A def __ge__(self, other): return self.filename >= other.filename 2231078SN/A def __eq__(self, other): return self.filename == other.filename 2241078SN/A def __ne__(self, other): return self.filename != other.filename 2251092SN/A 2265761Ssaidi@eecs.umich.edudef blobToCpp(data, symbol, cpp_code, hpp_code=None, namespace=None): 2275761Ssaidi@eecs.umich.edu ''' 2285761Ssaidi@eecs.umich.edu Convert bytes data into C++ .cpp and .hh uint8_t byte array 2291078SN/A code containing that binary data. 2301114SN/A 2311079SN/A :param data: binary data to be converted to C++ 2321079SN/A :param symbol: name of the symbol 2331079SN/A :param cpp_code: append the generated cpp_code to this object 2341079SN/A :param hpp_code: append the generated hpp_code to this object 2351079SN/A If None, ignore it. Otherwise, also include it 2361079SN/A in the .cpp file. 2371078SN/A :param namespace: namespace to put the symbol into. If None, 2381078SN/A don't put the symbols into any namespace. 2391114SN/A ''' 2401114SN/A symbol_len_declaration = 'const std::size_t {}_len'.format(symbol) 2411114SN/A symbol_declaration = 'const std::uint8_t {}[]'.format(symbol) 2421114SN/A if hpp_code is not None: 2431114SN/A cpp_code('''\ 2442566SN/A#include "blobs/{}.hh" 2451114SN/A'''.format(symbol)) 2462566SN/A hpp_code('''\ 2471114SN/A#include <cstddef> 2485484Snate@binkert.org#include <cstdint> 2495484Snate@binkert.org''') 2505484Snate@binkert.org if namespace is not None: 2515484Snate@binkert.org hpp_code('namespace {} {{'.format(namespace)) 2525484Snate@binkert.org hpp_code('extern ' + symbol_len_declaration + ';') 2535484Snate@binkert.org hpp_code('extern ' + symbol_declaration + ';') 2545484Snate@binkert.org if namespace is not None: 2551114SN/A hpp_code('}') 2561114SN/A if namespace is not None: 2571114SN/A cpp_code('namespace {} {{'.format(namespace)) 2585484Snate@binkert.org if hpp_code is not None: 2595484Snate@binkert.org cpp_code(symbol_len_declaration + ' = {};'.format(len(data))) 2605484Snate@binkert.org cpp_code(symbol_declaration + ' = {') 2611114SN/A cpp_code.indent() 2621114SN/A step = 16 2635484Snate@binkert.org for i in xrange(0, len(data), step): 2645484Snate@binkert.org x = array.array('B', data[i:i+step]) 2655484Snate@binkert.org cpp_code(''.join('%d,' % d for d in x)) 2661114SN/A cpp_code.dedent() 2675484Snate@binkert.org cpp_code('};') 2685484Snate@binkert.org if namespace is not None: 2695484Snate@binkert.org cpp_code('}') 2705484Snate@binkert.org 2711114SN/Adef Blob(blob_path, symbol): 2722566SN/A ''' 2731114SN/A Embed an arbitrary blob into the gem5 executable, 2741114SN/A and make it accessible to C++ as a byte array. 2751114SN/A ''' 2762566SN/A blob_path = os.path.abspath(blob_path) 2772566SN/A blob_out_dir = os.path.join(env['BUILDDIR'], 'blobs') 2781114SN/A path_noext = joinpath(blob_out_dir, symbol) 2791114SN/A cpp_path = path_noext + '.cc' 2805782Ssaidi@eecs.umich.edu hpp_path = path_noext + '.hh' 2815782Ssaidi@eecs.umich.edu def embedBlob(target, source, env): 2821114SN/A data = file(str(source[0]), 'r').read() 2831114SN/A cpp_code = code_formatter() 2841114SN/A hpp_code = code_formatter() 2851114SN/A blobToCpp(data, symbol, cpp_code, hpp_code, namespace='Blobs') 2861114SN/A cpp_path = str(target[0]) 2871114SN/A hpp_path = str(target[1]) 2881114SN/A cpp_dir = os.path.split(cpp_path)[0] 2891114SN/A if not os.path.exists(cpp_dir): 2901114SN/A os.makedirs(cpp_dir) 2911114SN/A cpp_code.write(cpp_path) 2921114SN/A hpp_code.write(hpp_path) 2931114SN/A env.Command([cpp_path, hpp_path], blob_path, 2941114SN/A MakeAction(embedBlob, Transform("EMBED BLOB"))) 2951114SN/A Source(cpp_path) 2961114SN/A 2971114SN/Adef GdbXml(xml_id, symbol): 2981114SN/A Blob(joinpath(gdb_xml_dir, xml_id), symbol) 2991114SN/A 3001114SN/Aclass Source(SourceFile): 3011114SN/A ungrouped_tag = 'No link group' 3021114SN/A source_groups = set() 3031114SN/A 3041114SN/A _current_group_tag = ungrouped_tag 3051114SN/A 3061114SN/A @staticmethod 3071114SN/A def link_group_tag(group): 3081114SN/A return 'link group: %s' % group 3091114SN/A 3101114SN/A @classmethod 3111114SN/A def set_group(cls, group): 3121114SN/A new_tag = Source.link_group_tag(group) 3131114SN/A Source._current_group_tag = new_tag 3141114SN/A Source.source_groups.add(group) 3151114SN/A 3161114SN/A def _add_link_group_tag(self): 3171078SN/A self.tags.add(Source._current_group_tag) 3181078SN/A 3191078SN/A '''Add a c/c++ source file to the build''' 3201078SN/A def __init__(self, source, tags=None, add_tags=None): 3211078SN/A '''specify the source file, and any tags''' 3221078SN/A super(Source, self).__init__(source, tags, add_tags) 3231078SN/A self._add_link_group_tag() 3241078SN/A 3251092SN/Aclass PySource(SourceFile): 3261078SN/A '''Add a python source file to the named package''' 3271078SN/A invalid_sym_char = re.compile('[^A-z0-9_]') 3281092SN/A modules = {} 3295761Ssaidi@eecs.umich.edu tnodes = {} 3305761Ssaidi@eecs.umich.edu symnames = {} 3311078SN/A 3321114SN/A def __init__(self, package, source, tags=None, add_tags=None): 3331114SN/A '''specify the python package, the source file, and any tags''' 3341079SN/A super(PySource, self).__init__(source, tags, add_tags) 3351079SN/A 3361079SN/A modname,ext = self.extname 3371079SN/A assert ext == 'py' 3381079SN/A 3391078SN/A if package: 3401078SN/A path = package.split('.') 3411114SN/A else: 3421114SN/A path = [] 3431114SN/A 3442566SN/A modpath = path[:] 3455782Ssaidi@eecs.umich.edu if modname != '__init__': 3461114SN/A modpath += [ modname ] 3475782Ssaidi@eecs.umich.edu modpath = '.'.join(modpath) 3481114SN/A 3491114SN/A arcpath = path + [ self.basename ] 3505484Snate@binkert.org abspath = self.snode.abspath 3511114SN/A if not exists(abspath): 3521114SN/A abspath = self.tnode.abspath 3531114SN/A 3541114SN/A self.package = package 3551114SN/A self.modname = modname 3561114SN/A self.modpath = modpath 3575782Ssaidi@eecs.umich.edu self.arcname = joinpath(*arcpath) 3585782Ssaidi@eecs.umich.edu self.abspath = abspath 3595782Ssaidi@eecs.umich.edu self.compiled = File(self.filename + 'c') 3601114SN/A self.cpp = File(self.filename + '.cc') 3615782Ssaidi@eecs.umich.edu self.symname = PySource.invalid_sym_char.sub('_', modpath) 3625484Snate@binkert.org 3635484Snate@binkert.org PySource.modules[modpath] = self 3641114SN/A PySource.tnodes[self.tnode] = self 3655782Ssaidi@eecs.umich.edu PySource.symnames[self.symname] = self 3665484Snate@binkert.org 3675484Snate@binkert.orgclass SimObject(PySource): 3681114SN/A '''Add a SimObject python file as a python source object and add 3691114SN/A it to a list of sim object modules''' 3705782Ssaidi@eecs.umich.edu 3711114SN/A fixed = False 3722566SN/A modnames = [] 3732566SN/A 3741114SN/A def __init__(self, source, tags=None, add_tags=None): 3751114SN/A '''Specify the source file and any tags (automatically in 3765782Ssaidi@eecs.umich.edu the m5.objects package)''' 3775782Ssaidi@eecs.umich.edu super(SimObject, self).__init__('m5.objects', source, tags, add_tags) 3781114SN/A if self.fixed: 3791114SN/A raise AttributeError, "Too late to call SimObject now." 3801114SN/A 3811114SN/A bisect.insort_right(SimObject.modnames, self.modname) 3821114SN/A 3831114SN/Aclass ProtoBuf(SourceFile): 3841114SN/A '''Add a Protocol Buffer to build''' 3851114SN/A 3861114SN/A def __init__(self, source, tags=None, add_tags=None): 3871114SN/A '''Specify the source file, and any tags''' 3881114SN/A super(ProtoBuf, self).__init__(source, tags, add_tags) 3891114SN/A 3901114SN/A # Get the file name and the extension 3911114SN/A modname,ext = self.extname 3921114SN/A assert ext == 'proto' 3931114SN/A 3941114SN/A # Currently, we stick to generating the C++ headers, so we 3951114SN/A # only need to track the source and header. 3961114SN/A self.cc_file = File(modname + '.pb.cc') 3971114SN/A self.hh_file = File(modname + '.pb.h') 3981114SN/A 3991114SN/A 4001114SN/Aexectuable_classes = [] 4011114SN/Aclass ExecutableMeta(type): 4021114SN/A '''Meta class for Executables.''' 4031114SN/A all = [] 4041114SN/A 4051114SN/A def __init__(cls, name, bases, d): 4061114SN/A if not d.pop('abstract', False): 4071114SN/A ExecutableMeta.all.append(cls) 4081114SN/A super(ExecutableMeta, cls).__init__(name, bases, d) 4091114SN/A 4101114SN/A cls.all = [] 4111114SN/A 4121114SN/Aclass Executable(object): 4131114SN/A '''Base class for creating an executable from sources.''' 4141078SN/A __metaclass__ = ExecutableMeta 4151078SN/A 4161078SN/A abstract = True 4171078SN/A 4181171SN/A def __init__(self, target, *srcs_and_filts): 4191078SN/A '''Specify the target name and any sources. Sources that are 4201171SN/A not SourceFiles are evalued with Source().''' 4215761Ssaidi@eecs.umich.edu super(Executable, self).__init__() 4221078SN/A self.all.append(self) 4231114SN/A self.target = target 4241079SN/A 4251079SN/A isFilter = lambda arg: isinstance(arg, SourceFilter) 4261079SN/A self.filters = filter(isFilter, srcs_and_filts) 4271079SN/A sources = filter(lambda a: not isFilter(a), srcs_and_filts) 4281078SN/A 4291078SN/A srcs = SourceList() 4301114SN/A for src in sources: 4311114SN/A if not isinstance(src, SourceFile): 4321114SN/A src = Source(src, tags=[]) 4332566SN/A srcs.append(src) 4345782Ssaidi@eecs.umich.edu 4351114SN/A self.sources = srcs 4365782Ssaidi@eecs.umich.edu self.dir = Dir('.') 4371114SN/A 4381114SN/A def path(self, env): 4395484Snate@binkert.org return self.dir.File(self.target + '.' + env['EXE_SUFFIX']) 4401114SN/A 4411114SN/A def srcs_to_objs(self, env, sources): 4421114SN/A return list([ s.static(env) for s in sources ]) 4431114SN/A 4441114SN/A @classmethod 4451114SN/A def declare_all(cls, env): 4465782Ssaidi@eecs.umich.edu return list([ instance.declare(env) for instance in cls.all ]) 4475782Ssaidi@eecs.umich.edu 4485782Ssaidi@eecs.umich.edu def declare(self, env, objs=None): 4491114SN/A if objs is None: 4505782Ssaidi@eecs.umich.edu objs = self.srcs_to_objs(env, self.sources) 4515484Snate@binkert.org 4525484Snate@binkert.org env = env.Clone() 4531114SN/A env['BIN_RPATH_PREFIX'] = os.path.relpath( 4545782Ssaidi@eecs.umich.edu env['BUILDDIR'], self.path(env).dir.abspath) 4555484Snate@binkert.org 4565484Snate@binkert.org if env['STRIP_EXES']: 4571114SN/A stripped = self.path(env) 4581114SN/A unstripped = env.File(str(stripped) + '.unstripped') 4595782Ssaidi@eecs.umich.edu if sys.platform == 'sunos5': 4601114SN/A cmd = 'cp $SOURCE $TARGET; strip $TARGET' 4612566SN/A else: 4622566SN/A cmd = 'strip $SOURCE -o $TARGET' 4631114SN/A env.Program(unstripped, objs) 4641114SN/A return env.Command(stripped, unstripped, 4655782Ssaidi@eecs.umich.edu MakeAction(cmd, Transform("STRIP"))) 4665782Ssaidi@eecs.umich.edu else: 4671114SN/A return env.Program(self.path(env), objs) 4681114SN/A 4691114SN/Aclass UnitTest(Executable): 4701114SN/A '''Create a UnitTest''' 4715782Ssaidi@eecs.umich.edu def __init__(self, target, *srcs_and_filts, **kwargs): 4725782Ssaidi@eecs.umich.edu super(UnitTest, self).__init__(target, *srcs_and_filts) 4731114SN/A 4741114SN/A self.main = kwargs.get('main', False) 4751078SN/A 476 def declare(self, env): 477 sources = list(self.sources) 478 for f in self.filters: 479 sources += Source.all.apply_filter(f) 480 objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS'] 481 if self.main: 482 objs += env['MAIN_OBJS'] 483 return super(UnitTest, self).declare(env, objs) 484 485class GTest(Executable): 486 '''Create a unit test based on the google test framework.''' 487 all = [] 488 def __init__(self, *srcs_and_filts, **kwargs): 489 super(GTest, self).__init__(*srcs_and_filts) 490 491 self.skip_lib = kwargs.pop('skip_lib', False) 492 493 @classmethod 494 def declare_all(cls, env): 495 env = env.Clone() 496 env.Append(LIBS=env['GTEST_LIBS']) 497 env.Append(CPPFLAGS=env['GTEST_CPPFLAGS']) 498 env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib') 499 env['GTEST_OUT_DIR'] = \ 500 Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX']) 501 return super(GTest, cls).declare_all(env) 502 503 def declare(self, env): 504 sources = list(self.sources) 505 if not self.skip_lib: 506 sources += env['GTEST_LIB_SOURCES'] 507 for f in self.filters: 508 sources += Source.all.apply_filter(f) 509 objs = self.srcs_to_objs(env, sources) 510 511 binary = super(GTest, self).declare(env, objs) 512 513 out_dir = env['GTEST_OUT_DIR'] 514 xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml') 515 AlwaysBuild(env.Command(xml_file, binary, 516 "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}")) 517 518 return binary 519 520class Gem5(Executable): 521 '''Create a gem5 executable.''' 522 523 def __init__(self, target): 524 super(Gem5, self).__init__(target) 525 526 def declare(self, env): 527 objs = env['MAIN_OBJS'] + env['STATIC_OBJS'] 528 return super(Gem5, self).declare(env, objs) 529 530 531# Children should have access 532Export('Blob') 533Export('GdbXml') 534Export('Source') 535Export('PySource') 536Export('SimObject') 537Export('ProtoBuf') 538Export('Executable') 539Export('UnitTest') 540Export('GTest') 541 542######################################################################## 543# 544# Debug Flags 545# 546debug_flags = {} 547def DebugFlag(name, desc=None): 548 if name in debug_flags: 549 raise AttributeError, "Flag %s already specified" % name 550 debug_flags[name] = (name, (), desc) 551 552def CompoundFlag(name, flags, desc=None): 553 if name in debug_flags: 554 raise AttributeError, "Flag %s already specified" % name 555 556 compound = tuple(flags) 557 debug_flags[name] = (name, compound, desc) 558 559Export('DebugFlag') 560Export('CompoundFlag') 561 562######################################################################## 563# 564# Set some compiler variables 565# 566 567# Include file paths are rooted in this directory. SCons will 568# automatically expand '.' to refer to both the source directory and 569# the corresponding build directory to pick up generated include 570# files. 571env.Append(CPPPATH=Dir('.')) 572 573for extra_dir in extras_dir_list: 574 env.Append(CPPPATH=Dir(extra_dir)) 575 576# Workaround for bug in SCons version > 0.97d20071212 577# Scons bug id: 2006 gem5 Bug id: 308 578for root, dirs, files in os.walk(base_dir, topdown=True): 579 Dir(root[len(base_dir) + 1:]) 580 581######################################################################## 582# 583# Walk the tree and execute all SConscripts in subdirectories 584# 585 586here = Dir('.').srcnode().abspath 587for root, dirs, files in os.walk(base_dir, topdown=True): 588 if root == here: 589 # we don't want to recurse back into this SConscript 590 continue 591 592 if 'SConscript' in files: 593 build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:]) 594 Source.set_group(build_dir) 595 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir) 596 597for extra_dir in extras_dir_list: 598 prefix_len = len(dirname(extra_dir)) + 1 599 600 # Also add the corresponding build directory to pick up generated 601 # include files. 602 env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:]))) 603 604 for root, dirs, files in os.walk(extra_dir, topdown=True): 605 # if build lives in the extras directory, don't walk down it 606 if 'build' in dirs: 607 dirs.remove('build') 608 609 if 'SConscript' in files: 610 build_dir = joinpath(env['BUILDDIR'], root[prefix_len:]) 611 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir) 612 613for opt in export_vars: 614 env.ConfigFile(opt) 615 616def makeTheISA(source, target, env): 617 isas = [ src.get_contents() for src in source ] 618 target_isa = env['TARGET_ISA'] 619 def define(isa): 620 return isa.upper() + '_ISA' 621 622 def namespace(isa): 623 return isa[0].upper() + isa[1:].lower() + 'ISA' 624 625 626 code = code_formatter() 627 code('''\ 628#ifndef __CONFIG_THE_ISA_HH__ 629#define __CONFIG_THE_ISA_HH__ 630 631''') 632 633 # create defines for the preprocessing and compile-time determination 634 for i,isa in enumerate(isas): 635 code('#define $0 $1', define(isa), i + 1) 636 code() 637 638 # create an enum for any run-time determination of the ISA, we 639 # reuse the same name as the namespaces 640 code('enum class Arch {') 641 for i,isa in enumerate(isas): 642 if i + 1 == len(isas): 643 code(' $0 = $1', namespace(isa), define(isa)) 644 else: 645 code(' $0 = $1,', namespace(isa), define(isa)) 646 code('};') 647 648 code(''' 649 650#define THE_ISA ${{define(target_isa)}} 651#define TheISA ${{namespace(target_isa)}} 652#define THE_ISA_STR "${{target_isa}}" 653 654#endif // __CONFIG_THE_ISA_HH__''') 655 656 code.write(str(target[0])) 657 658env.Command('config/the_isa.hh', map(Value, all_isa_list), 659 MakeAction(makeTheISA, Transform("CFG ISA", 0))) 660 661def makeTheGPUISA(source, target, env): 662 isas = [ src.get_contents() for src in source ] 663 target_gpu_isa = env['TARGET_GPU_ISA'] 664 def define(isa): 665 return isa.upper() + '_ISA' 666 667 def namespace(isa): 668 return isa[0].upper() + isa[1:].lower() + 'ISA' 669 670 671 code = code_formatter() 672 code('''\ 673#ifndef __CONFIG_THE_GPU_ISA_HH__ 674#define __CONFIG_THE_GPU_ISA_HH__ 675 676''') 677 678 # create defines for the preprocessing and compile-time determination 679 for i,isa in enumerate(isas): 680 code('#define $0 $1', define(isa), i + 1) 681 code() 682 683 # create an enum for any run-time determination of the ISA, we 684 # reuse the same name as the namespaces 685 code('enum class GPUArch {') 686 for i,isa in enumerate(isas): 687 if i + 1 == len(isas): 688 code(' $0 = $1', namespace(isa), define(isa)) 689 else: 690 code(' $0 = $1,', namespace(isa), define(isa)) 691 code('};') 692 693 code(''' 694 695#define THE_GPU_ISA ${{define(target_gpu_isa)}} 696#define TheGpuISA ${{namespace(target_gpu_isa)}} 697#define THE_GPU_ISA_STR "${{target_gpu_isa}}" 698 699#endif // __CONFIG_THE_GPU_ISA_HH__''') 700 701 code.write(str(target[0])) 702 703env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list), 704 MakeAction(makeTheGPUISA, Transform("CFG ISA", 0))) 705 706######################################################################## 707# 708# Prevent any SimObjects from being added after this point, they 709# should all have been added in the SConscripts above 710# 711SimObject.fixed = True 712 713class DictImporter(object): 714 '''This importer takes a dictionary of arbitrary module names that 715 map to arbitrary filenames.''' 716 def __init__(self, modules): 717 self.modules = modules 718 self.installed = set() 719 720 def __del__(self): 721 self.unload() 722 723 def unload(self): 724 import sys 725 for module in self.installed: 726 del sys.modules[module] 727 self.installed = set() 728 729 def find_module(self, fullname, path): 730 if fullname == 'm5.defines': 731 return self 732 733 if fullname == 'm5.objects': 734 return self 735 736 if fullname.startswith('_m5'): 737 return None 738 739 source = self.modules.get(fullname, None) 740 if source is not None and fullname.startswith('m5.objects'): 741 return self 742 743 return None 744 745 def load_module(self, fullname): 746 mod = imp.new_module(fullname) 747 sys.modules[fullname] = mod 748 self.installed.add(fullname) 749 750 mod.__loader__ = self 751 if fullname == 'm5.objects': 752 mod.__path__ = fullname.split('.') 753 return mod 754 755 if fullname == 'm5.defines': 756 mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env) 757 return mod 758 759 source = self.modules[fullname] 760 if source.modname == '__init__': 761 mod.__path__ = source.modpath 762 mod.__file__ = source.abspath 763 764 exec file(source.abspath, 'r') in mod.__dict__ 765 766 return mod 767 768import m5.SimObject 769import m5.params 770from m5.util import code_formatter 771 772m5.SimObject.clear() 773m5.params.clear() 774 775# install the python importer so we can grab stuff from the source 776# tree itself. We can't have SimObjects added after this point or 777# else we won't know about them for the rest of the stuff. 778importer = DictImporter(PySource.modules) 779sys.meta_path[0:0] = [ importer ] 780 781# import all sim objects so we can populate the all_objects list 782# make sure that we're working with a list, then let's sort it 783for modname in SimObject.modnames: 784 exec('from m5.objects import %s' % modname) 785 786# we need to unload all of the currently imported modules so that they 787# will be re-imported the next time the sconscript is run 788importer.unload() 789sys.meta_path.remove(importer) 790 791sim_objects = m5.SimObject.allClasses 792all_enums = m5.params.allEnums 793 794for name,obj in sorted(sim_objects.iteritems()): 795 for param in obj._params.local.values(): 796 # load the ptype attribute now because it depends on the 797 # current version of SimObject.allClasses, but when scons 798 # actually uses the value, all versions of 799 # SimObject.allClasses will have been loaded 800 param.ptype 801 802######################################################################## 803# 804# calculate extra dependencies 805# 806module_depends = ["m5", "m5.SimObject", "m5.params"] 807depends = [ PySource.modules[dep].snode for dep in module_depends ] 808depends.sort(key = lambda x: x.name) 809 810######################################################################## 811# 812# Commands for the basic automatically generated python files 813# 814 815# Generate Python file containing a dict specifying the current 816# buildEnv flags. 817def makeDefinesPyFile(target, source, env): 818 build_env = source[0].get_contents() 819 820 code = code_formatter() 821 code(""" 822import _m5.core 823import m5.util 824 825buildEnv = m5.util.SmartDict($build_env) 826 827compileDate = _m5.core.compileDate 828_globals = globals() 829for key,val in _m5.core.__dict__.iteritems(): 830 if key.startswith('flag_'): 831 flag = key[5:] 832 _globals[flag] = val 833del _globals 834""") 835 code.write(target[0].abspath) 836 837defines_info = Value(build_env) 838# Generate a file with all of the compile options in it 839env.Command('python/m5/defines.py', defines_info, 840 MakeAction(makeDefinesPyFile, Transform("DEFINES", 0))) 841PySource('m5', 'python/m5/defines.py') 842 843# Generate python file containing info about the M5 source code 844def makeInfoPyFile(target, source, env): 845 code = code_formatter() 846 for src in source: 847 data = ''.join(file(src.srcnode().abspath, 'r').xreadlines()) 848 code('$src = ${{repr(data)}}') 849 code.write(str(target[0])) 850 851# Generate a file that wraps the basic top level files 852env.Command('python/m5/info.py', 853 [ '#/COPYING', '#/LICENSE', '#/README', ], 854 MakeAction(makeInfoPyFile, Transform("INFO"))) 855PySource('m5', 'python/m5/info.py') 856 857######################################################################## 858# 859# Create all of the SimObject param headers and enum headers 860# 861 862def createSimObjectParamStruct(target, source, env): 863 assert len(target) == 1 and len(source) == 1 864 865 name = source[0].get_text_contents() 866 obj = sim_objects[name] 867 868 code = code_formatter() 869 obj.cxx_param_decl(code) 870 code.write(target[0].abspath) 871 872def createSimObjectCxxConfig(is_header): 873 def body(target, source, env): 874 assert len(target) == 1 and len(source) == 1 875 876 name = str(source[0].get_contents()) 877 obj = sim_objects[name] 878 879 code = code_formatter() 880 obj.cxx_config_param_file(code, is_header) 881 code.write(target[0].abspath) 882 return body 883 884def createEnumStrings(target, source, env): 885 assert len(target) == 1 and len(source) == 2 886 887 name = source[0].get_text_contents() 888 use_python = source[1].read() 889 obj = all_enums[name] 890 891 code = code_formatter() 892 obj.cxx_def(code) 893 if use_python: 894 obj.pybind_def(code) 895 code.write(target[0].abspath) 896 897def createEnumDecls(target, source, env): 898 assert len(target) == 1 and len(source) == 1 899 900 name = source[0].get_text_contents() 901 obj = all_enums[name] 902 903 code = code_formatter() 904 obj.cxx_decl(code) 905 code.write(target[0].abspath) 906 907def createSimObjectPyBindWrapper(target, source, env): 908 name = source[0].get_text_contents() 909 obj = sim_objects[name] 910 911 code = code_formatter() 912 obj.pybind_decl(code) 913 code.write(target[0].abspath) 914 915# Generate all of the SimObject param C++ struct header files 916params_hh_files = [] 917for name,simobj in sorted(sim_objects.iteritems()): 918 py_source = PySource.modules[simobj.__module__] 919 extra_deps = [ py_source.tnode ] 920 921 hh_file = File('params/%s.hh' % name) 922 params_hh_files.append(hh_file) 923 env.Command(hh_file, Value(name), 924 MakeAction(createSimObjectParamStruct, Transform("SO PARAM"))) 925 env.Depends(hh_file, depends + extra_deps) 926 927# C++ parameter description files 928if GetOption('with_cxx_config'): 929 for name,simobj in sorted(sim_objects.iteritems()): 930 py_source = PySource.modules[simobj.__module__] 931 extra_deps = [ py_source.tnode ] 932 933 cxx_config_hh_file = File('cxx_config/%s.hh' % name) 934 cxx_config_cc_file = File('cxx_config/%s.cc' % name) 935 env.Command(cxx_config_hh_file, Value(name), 936 MakeAction(createSimObjectCxxConfig(True), 937 Transform("CXXCPRHH"))) 938 env.Command(cxx_config_cc_file, Value(name), 939 MakeAction(createSimObjectCxxConfig(False), 940 Transform("CXXCPRCC"))) 941 env.Depends(cxx_config_hh_file, depends + extra_deps + 942 [File('params/%s.hh' % name), File('sim/cxx_config.hh')]) 943 env.Depends(cxx_config_cc_file, depends + extra_deps + 944 [cxx_config_hh_file]) 945 Source(cxx_config_cc_file) 946 947 cxx_config_init_cc_file = File('cxx_config/init.cc') 948 949 def createCxxConfigInitCC(target, source, env): 950 assert len(target) == 1 and len(source) == 1 951 952 code = code_formatter() 953 954 for name,simobj in sorted(sim_objects.iteritems()): 955 if not hasattr(simobj, 'abstract') or not simobj.abstract: 956 code('#include "cxx_config/${name}.hh"') 957 code() 958 code('void cxxConfigInit()') 959 code('{') 960 code.indent() 961 for name,simobj in sorted(sim_objects.iteritems()): 962 not_abstract = not hasattr(simobj, 'abstract') or \ 963 not simobj.abstract 964 if not_abstract and 'type' in simobj.__dict__: 965 code('cxx_config_directory["${name}"] = ' 966 '${name}CxxConfigParams::makeDirectoryEntry();') 967 code.dedent() 968 code('}') 969 code.write(target[0].abspath) 970 971 py_source = PySource.modules[simobj.__module__] 972 extra_deps = [ py_source.tnode ] 973 env.Command(cxx_config_init_cc_file, Value(name), 974 MakeAction(createCxxConfigInitCC, Transform("CXXCINIT"))) 975 cxx_param_hh_files = ["cxx_config/%s.hh" % simobj 976 for name,simobj in sorted(sim_objects.iteritems()) 977 if not hasattr(simobj, 'abstract') or not simobj.abstract] 978 Depends(cxx_config_init_cc_file, cxx_param_hh_files + 979 [File('sim/cxx_config.hh')]) 980 Source(cxx_config_init_cc_file) 981 982# Generate all enum header files 983for name,enum in sorted(all_enums.iteritems()): 984 py_source = PySource.modules[enum.__module__] 985 extra_deps = [ py_source.tnode ] 986 987 cc_file = File('enums/%s.cc' % name) 988 env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])], 989 MakeAction(createEnumStrings, Transform("ENUM STR"))) 990 env.Depends(cc_file, depends + extra_deps) 991 Source(cc_file) 992 993 hh_file = File('enums/%s.hh' % name) 994 env.Command(hh_file, Value(name), 995 MakeAction(createEnumDecls, Transform("ENUMDECL"))) 996 env.Depends(hh_file, depends + extra_deps) 997 998# Generate SimObject Python bindings wrapper files 999if env['USE_PYTHON']: 1000 for name,simobj in sorted(sim_objects.iteritems()): 1001 py_source = PySource.modules[simobj.__module__] 1002 extra_deps = [ py_source.tnode ] 1003 cc_file = File('python/_m5/param_%s.cc' % name) 1004 env.Command(cc_file, Value(name), 1005 MakeAction(createSimObjectPyBindWrapper, 1006 Transform("SO PyBind"))) 1007 env.Depends(cc_file, depends + extra_deps) 1008 Source(cc_file) 1009 1010# Build all protocol buffers if we have got protoc and protobuf available 1011if env['HAVE_PROTOBUF']: 1012 for proto in ProtoBuf.all: 1013 # Use both the source and header as the target, and the .proto 1014 # file as the source. When executing the protoc compiler, also 1015 # specify the proto_path to avoid having the generated files 1016 # include the path. 1017 env.Command([proto.cc_file, proto.hh_file], proto.tnode, 1018 MakeAction('$PROTOC --cpp_out ${TARGET.dir} ' 1019 '--proto_path ${SOURCE.dir} $SOURCE', 1020 Transform("PROTOC"))) 1021 1022 # Add the C++ source file 1023 Source(proto.cc_file, tags=proto.tags) 1024elif ProtoBuf.all: 1025 print('Got protobuf to build, but lacks support!') 1026 Exit(1) 1027 1028# 1029# Handle debug flags 1030# 1031def makeDebugFlagCC(target, source, env): 1032 assert(len(target) == 1 and len(source) == 1) 1033 1034 code = code_formatter() 1035 1036 # delay definition of CompoundFlags until after all the definition 1037 # of all constituent SimpleFlags 1038 comp_code = code_formatter() 1039 1040 # file header 1041 code(''' 1042/* 1043 * DO NOT EDIT THIS FILE! Automatically generated by SCons. 1044 */ 1045 1046#include "base/debug.hh" 1047 1048namespace Debug { 1049 1050''') 1051 1052 for name, flag in sorted(source[0].read().iteritems()): 1053 n, compound, desc = flag 1054 assert n == name 1055 1056 if not compound: 1057 code('SimpleFlag $name("$name", "$desc");') 1058 else: 1059 comp_code('CompoundFlag $name("$name", "$desc",') 1060 comp_code.indent() 1061 last = len(compound) - 1 1062 for i,flag in enumerate(compound): 1063 if i != last: 1064 comp_code('&$flag,') 1065 else: 1066 comp_code('&$flag);') 1067 comp_code.dedent() 1068 1069 code.append(comp_code) 1070 code() 1071 code('} // namespace Debug') 1072 1073 code.write(str(target[0])) 1074 1075def makeDebugFlagHH(target, source, env): 1076 assert(len(target) == 1 and len(source) == 1) 1077 1078 val = eval(source[0].get_contents()) 1079 name, compound, desc = val 1080 1081 code = code_formatter() 1082 1083 # file header boilerplate 1084 code('''\ 1085/* 1086 * DO NOT EDIT THIS FILE! Automatically generated by SCons. 1087 */ 1088 1089#ifndef __DEBUG_${name}_HH__ 1090#define __DEBUG_${name}_HH__ 1091 1092namespace Debug { 1093''') 1094 1095 if compound: 1096 code('class CompoundFlag;') 1097 code('class SimpleFlag;') 1098 1099 if compound: 1100 code('extern CompoundFlag $name;') 1101 for flag in compound: 1102 code('extern SimpleFlag $flag;') 1103 else: 1104 code('extern SimpleFlag $name;') 1105 1106 code(''' 1107} 1108 1109#endif // __DEBUG_${name}_HH__ 1110''') 1111 1112 code.write(str(target[0])) 1113 1114for name,flag in sorted(debug_flags.iteritems()): 1115 n, compound, desc = flag 1116 assert n == name 1117 1118 hh_file = 'debug/%s.hh' % name 1119 env.Command(hh_file, Value(flag), 1120 MakeAction(makeDebugFlagHH, Transform("TRACING", 0))) 1121 1122env.Command('debug/flags.cc', Value(debug_flags), 1123 MakeAction(makeDebugFlagCC, Transform("TRACING", 0))) 1124Source('debug/flags.cc') 1125 1126# version tags 1127tags = \ 1128env.Command('sim/tags.cc', None, 1129 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET', 1130 Transform("VER TAGS"))) 1131env.AlwaysBuild(tags) 1132 1133# Embed python files. All .py files that have been indicated by a 1134# PySource() call in a SConscript need to be embedded into the M5 1135# library. To do that, we compile the file to byte code, marshal the 1136# byte code, compress it, and then generate a c++ file that 1137# inserts the result into an array. 1138def embedPyFile(target, source, env): 1139 def c_str(string): 1140 if string is None: 1141 return "0" 1142 return '"%s"' % string 1143 1144 '''Action function to compile a .py into a code object, marshal 1145 it, compress it, and stick it into an asm file so the code appears 1146 as just bytes with a label in the data section''' 1147 1148 src = file(str(source[0]), 'r').read() 1149 1150 pysource = PySource.tnodes[source[0]] 1151 compiled = compile(src, pysource.abspath, 'exec') 1152 marshalled = marshal.dumps(compiled) 1153 compressed = zlib.compress(marshalled) 1154 data = compressed 1155 sym = pysource.symname 1156 1157 code = code_formatter() 1158 code('''\ 1159#include "sim/init.hh" 1160 1161namespace { 1162 1163''') 1164 blobToCpp(data, 'data_' + sym, code) 1165 code('''\ 1166 1167 1168EmbeddedPython embedded_${sym}( 1169 ${{c_str(pysource.arcname)}}, 1170 ${{c_str(pysource.abspath)}}, 1171 ${{c_str(pysource.modpath)}}, 1172 data_${sym}, 1173 ${{len(data)}}, 1174 ${{len(marshalled)}}); 1175 1176} // anonymous namespace 1177''') 1178 code.write(str(target[0])) 1179 1180for source in PySource.all: 1181 env.Command(source.cpp, source.tnode, 1182 MakeAction(embedPyFile, Transform("EMBED PY"))) 1183 Source(source.cpp, tags=source.tags, add_tags='python') 1184 1185######################################################################## 1186# 1187# Define binaries. Each different build type (debug, opt, etc.) gets 1188# a slightly different build environment. 1189# 1190 1191# List of constructed environments to pass back to SConstruct 1192date_source = Source('base/date.cc', tags=[]) 1193 1194gem5_binary = Gem5('gem5') 1195 1196# Function to create a new build environment as clone of current 1197# environment 'env' with modified object suffix and optional stripped 1198# binary. Additional keyword arguments are appended to corresponding 1199# build environment vars. 1200def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs): 1201 # SCons doesn't know to append a library suffix when there is a '.' in the 1202 # name. Use '_' instead. 1203 libname = 'gem5_' + label 1204 secondary_exename = 'm5.' + label 1205 1206 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's') 1207 new_env.Label = label 1208 new_env.Append(**kwargs) 1209 1210 lib_sources = Source.all.with_tag('gem5 lib') 1211 1212 # Without Python, leave out all Python content from the library 1213 # builds. The option doesn't affect gem5 built as a program 1214 if GetOption('without_python'): 1215 lib_sources = lib_sources.without_tag('python') 1216 1217 static_objs = [] 1218 shared_objs = [] 1219 1220 for s in lib_sources.with_tag(Source.ungrouped_tag): 1221 static_objs.append(s.static(new_env)) 1222 shared_objs.append(s.shared(new_env)) 1223 1224 for group in Source.source_groups: 1225 srcs = lib_sources.with_tag(Source.link_group_tag(group)) 1226 if not srcs: 1227 continue 1228 1229 group_static = [ s.static(new_env) for s in srcs ] 1230 group_shared = [ s.shared(new_env) for s in srcs ] 1231 1232 # If partial linking is disabled, add these sources to the build 1233 # directly, and short circuit this loop. 1234 if disable_partial: 1235 static_objs.extend(group_static) 1236 shared_objs.extend(group_shared) 1237 continue 1238 1239 # Set up the static partially linked objects. 1240 file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial") 1241 target = File(joinpath(group, file_name)) 1242 partial = env.PartialStatic(target=target, source=group_static) 1243 static_objs.extend(partial) 1244 1245 # Set up the shared partially linked objects. 1246 file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial") 1247 target = File(joinpath(group, file_name)) 1248 partial = env.PartialShared(target=target, source=group_shared) 1249 shared_objs.extend(partial) 1250 1251 static_date = date_source.static(new_env) 1252 new_env.Depends(static_date, static_objs) 1253 static_objs.extend(static_date) 1254 1255 shared_date = date_source.shared(new_env) 1256 new_env.Depends(shared_date, shared_objs) 1257 shared_objs.extend(shared_date) 1258 1259 main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ] 1260 1261 # First make a library of everything but main() so other programs can 1262 # link against m5. 1263 static_lib = new_env.StaticLibrary(libname, static_objs) 1264 shared_lib = new_env.SharedLibrary(libname, shared_objs) 1265 1266 # Keep track of the object files generated so far so Executables can 1267 # include them. 1268 new_env['STATIC_OBJS'] = static_objs 1269 new_env['SHARED_OBJS'] = shared_objs 1270 new_env['MAIN_OBJS'] = main_objs 1271 1272 new_env['STATIC_LIB'] = static_lib 1273 new_env['SHARED_LIB'] = shared_lib 1274 1275 # Record some settings for building Executables. 1276 new_env['EXE_SUFFIX'] = label 1277 new_env['STRIP_EXES'] = strip 1278 1279 for cls in ExecutableMeta.all: 1280 cls.declare_all(new_env) 1281 1282 new_env.M5Binary = File(gem5_binary.path(new_env)) 1283 1284 new_env.Command(secondary_exename, new_env.M5Binary, 1285 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK"))) 1286 1287 # Set up regression tests. 1288 SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'), 1289 variant_dir=Dir('tests').Dir(new_env.Label), 1290 exports={ 'env' : new_env }, duplicate=False) 1291 1292# Start out with the compiler flags common to all compilers, 1293# i.e. they all use -g for opt and -g -pg for prof 1294ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'], 1295 'perf' : ['-g']} 1296 1297# Start out with the linker flags common to all linkers, i.e. -pg for 1298# prof, and -lprofiler for perf. The -lprofile flag is surrounded by 1299# no-as-needed and as-needed as the binutils linker is too clever and 1300# simply doesn't link to the library otherwise. 1301ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'], 1302 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']} 1303 1304# For Link Time Optimization, the optimisation flags used to compile 1305# individual files are decoupled from those used at link time 1306# (i.e. you can compile with -O3 and perform LTO with -O0), so we need 1307# to also update the linker flags based on the target. 1308if env['GCC']: 1309 if sys.platform == 'sunos5': 1310 ccflags['debug'] += ['-gstabs+'] 1311 else: 1312 ccflags['debug'] += ['-ggdb3'] 1313 ldflags['debug'] += ['-O0'] 1314 # opt, fast, prof and perf all share the same cc flags, also add 1315 # the optimization to the ldflags as LTO defers the optimization 1316 # to link time 1317 for target in ['opt', 'fast', 'prof', 'perf']: 1318 ccflags[target] += ['-O3'] 1319 ldflags[target] += ['-O3'] 1320 1321 ccflags['fast'] += env['LTO_CCFLAGS'] 1322 ldflags['fast'] += env['LTO_LDFLAGS'] 1323elif env['CLANG']: 1324 ccflags['debug'] += ['-g', '-O0'] 1325 # opt, fast, prof and perf all share the same cc flags 1326 for target in ['opt', 'fast', 'prof', 'perf']: 1327 ccflags[target] += ['-O3'] 1328else: 1329 print('Unknown compiler, please fix compiler options') 1330 Exit(1) 1331 1332 1333# To speed things up, we only instantiate the build environments we 1334# need. We try to identify the needed environment for each target; if 1335# we can't, we fall back on instantiating all the environments just to 1336# be safe. 1337target_types = ['debug', 'opt', 'fast', 'prof', 'perf'] 1338obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof', 1339 'gpo' : 'perf'} 1340 1341def identifyTarget(t): 1342 ext = t.split('.')[-1] 1343 if ext in target_types: 1344 return ext 1345 if ext in obj2target: 1346 return obj2target[ext] 1347 match = re.search(r'/tests/([^/]+)/', t) 1348 if match and match.group(1) in target_types: 1349 return match.group(1) 1350 return 'all' 1351 1352needed_envs = [identifyTarget(target) for target in BUILD_TARGETS] 1353if 'all' in needed_envs: 1354 needed_envs += target_types 1355 1356disable_partial = False 1357if env['PLATFORM'] == 'darwin': 1358 # Up until Apple LLVM version 10.0.0 (clang-1000.11.45.5), partial 1359 # linked objects do not expose symbols that are marked with the 1360 # hidden visibility and consequently building gem5 on Mac OS 1361 # fails. As a workaround, we disable partial linking, however, we 1362 # may want to revisit in the future. 1363 disable_partial = True 1364 1365# Debug binary 1366if 'debug' in needed_envs: 1367 makeEnv(env, 'debug', '.do', 1368 CCFLAGS = Split(ccflags['debug']), 1369 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'], 1370 LINKFLAGS = Split(ldflags['debug']), 1371 disable_partial=disable_partial) 1372 1373# Optimized binary 1374if 'opt' in needed_envs: 1375 makeEnv(env, 'opt', '.o', 1376 CCFLAGS = Split(ccflags['opt']), 1377 CPPDEFINES = ['TRACING_ON=1'], 1378 LINKFLAGS = Split(ldflags['opt']), 1379 disable_partial=disable_partial) 1380 1381# "Fast" binary 1382if 'fast' in needed_envs: 1383 disable_partial = disable_partial and \ 1384 env.get('BROKEN_INCREMENTAL_LTO', False) and \ 1385 GetOption('force_lto') 1386 makeEnv(env, 'fast', '.fo', strip = True, 1387 CCFLAGS = Split(ccflags['fast']), 1388 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1389 LINKFLAGS = Split(ldflags['fast']), 1390 disable_partial=disable_partial) 1391 1392# Profiled binary using gprof 1393if 'prof' in needed_envs: 1394 makeEnv(env, 'prof', '.po', 1395 CCFLAGS = Split(ccflags['prof']), 1396 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1397 LINKFLAGS = Split(ldflags['prof']), 1398 disable_partial=disable_partial) 1399 1400# Profiled binary using google-pprof 1401if 'perf' in needed_envs: 1402 makeEnv(env, 'perf', '.gpo', 1403 CCFLAGS = Split(ccflags['perf']), 1404 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'], 1405 LINKFLAGS = Split(ldflags['perf']), 1406 disable_partial=disable_partial) 1407