X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2FSConscript;h=361479d574141fb78e52bd30980aff94f6608590;hb=30746da58f3dbcb37df6214999ad48cb7df1cc4a;hp=61af95b5534f78f0733f32cbd529a6e1708d0ae1;hpb=82f76b8df0f4e6f46f5b0322dd30e5585c8eb9f9;p=gem5.git diff --git a/src/SConscript b/src/SConscript index 61af95b55..361479d57 100755 --- a/src/SConscript +++ b/src/SConscript @@ -28,8 +28,11 @@ # # Authors: Nathan Binkert +from __future__ import print_function + import array import bisect +import functools import imp import marshal import os @@ -62,32 +65,68 @@ from m5.util import code_formatter, compareVersions # When specifying a source file of some type, a set of tags can be # specified for that file. +class SourceFilter(object): + def __init__(self, predicate): + self.predicate = predicate + + def __or__(self, other): + return SourceFilter(lambda tags: self.predicate(tags) or + other.predicate(tags)) + + def __and__(self, other): + return SourceFilter(lambda tags: self.predicate(tags) and + other.predicate(tags)) + +def with_tags_that(predicate): + '''Return a list of sources with tags that satisfy a predicate.''' + return SourceFilter(predicate) + +def with_any_tags(*tags): + '''Return a list of sources with any of the supplied tags.''' + return SourceFilter(lambda stags: len(set(tags) & stags) > 0) + +def with_all_tags(*tags): + '''Return a list of sources with all of the supplied tags.''' + return SourceFilter(lambda stags: set(tags) <= stags) + +def with_tag(tag): + '''Return a list of sources with the supplied tag.''' + return SourceFilter(lambda stags: tag in stags) + +def without_tags(*tags): + '''Return a list of sources without any of the supplied tags.''' + return SourceFilter(lambda stags: len(set(tags) & stags) == 0) + +def without_tag(tag): + '''Return a list of sources with the supplied tag.''' + return SourceFilter(lambda stags: tag not in stags) + +source_filter_factories = { + 'with_tags_that': with_tags_that, + 'with_any_tags': with_any_tags, + 'with_all_tags': with_all_tags, + 'with_tag': with_tag, + 'without_tags': without_tags, + 'without_tag': without_tag, +} + +Export(source_filter_factories) + class SourceList(list): - def with_tags_that(self, predicate): - '''Return a list of sources with tags that satisfy a predicate.''' + def apply_filter(self, f): def match(source): - return predicate(source.tags) + return f.predicate(source.tags) return SourceList(filter(match, self)) - def with_any_tags(self, *tags): - '''Return a list of sources with any of the supplied tags.''' - return self.with_tags_that(lambda stags: len(tags & stags) > 0) - - def with_all_tags(self, *tags): - '''Return a list of sources with all of the supplied tags.''' - return self.with_tags_that(lambda stags: tags <= stags) - - def with_tag(self, tag): - '''Return a list of sources with the supplied tag.''' - return self.with_tags_that(lambda stags: tag in stags) - - def without_tags(self, *tags): - '''Return a list of sources without any of the supplied tags.''' - return self.with_tags_that(lambda stags: len(tags & stags) == 0) + def __getattr__(self, name): + func = source_filter_factories.get(name, None) + if not func: + raise AttributeError - def without_tag(self, tag): - '''Return a list of sources with the supplied tag.''' - return self.with_tags_that(lambda stags: tag not in stags) + @functools.wraps(func) + def wrapper(*args, **kwargs): + return self.apply_filter(func(*args, **kwargs)) + return wrapper class SourceMeta(type): '''Meta class for source files that keeps track of all files of a @@ -102,16 +141,25 @@ class SourceFile(object): of those. A source file also specifies a set of tags which describing arbitrary properties of the source file.''' __metaclass__ = SourceMeta + + static_objs = {} + shared_objs = {} + def __init__(self, source, tags=None, add_tags=None): if tags is None: tags='gem5 lib' if isinstance(tags, basestring): tags = set([tags]) - if isinstance(add_tags, basestring): - add_tags = set([add_tags]) + if not isinstance(tags, set): + tags = set(tags) + self.tags = tags + if add_tags: - tags = tags | add_tags - self.tags = set(tags) + if isinstance(add_tags, basestring): + add_tags = set([add_tags]) + if not isinstance(add_tags, set): + add_tags = set(add_tags) + self.tags |= add_tags tnode = source if not isinstance(source, SCons.Node.FS.File): @@ -124,6 +172,18 @@ class SourceFile(object): if issubclass(base, SourceFile): base.all.append(self) + def static(self, env): + key = (self.tnode, env['OBJSUFFIX']) + if not key in self.static_objs: + self.static_objs[key] = env.StaticObject(self.tnode) + return self.static_objs[key] + + def shared(self, env): + key = (self.tnode, env['OBJSUFFIX']) + if not key in self.shared_objs: + self.shared_objs[key] = env.SharedObject(self.tnode) + return self.shared_objs[key] + @property def filename(self): return str(self.tnode) @@ -251,32 +311,142 @@ class ProtoBuf(SourceFile): self.cc_file = File(modname + '.pb.cc') self.hh_file = File(modname + '.pb.h') -class UnitTest(object): - '''Create a UnitTest''' +exectuable_classes = [] +class ExecutableMeta(type): + '''Meta class for Executables.''' all = [] - def __init__(self, target, *sources, **kwargs): - '''Specify the target name and any sources. Sources that are - not SourceFiles are evalued with Source(). All files are - tagged with the name of the UnitTest target.''' + + def __init__(cls, name, bases, d): + if not d.pop('abstract', False): + ExecutableMeta.all.append(cls) + super(ExecutableMeta, cls).__init__(name, bases, d) + + cls.all = [] + +class Executable(object): + '''Base class for creating an executable from sources.''' + __metaclass__ = ExecutableMeta + + abstract = True + + def __init__(self, target, *srcs_and_filts): + '''Specify the target name and any sources. Sources that are + not SourceFiles are evalued with Source().''' + super(Executable, self).__init__() + self.all.append(self) + self.target = target + + isFilter = lambda arg: isinstance(arg, SourceFilter) + self.filters = filter(isFilter, srcs_and_filts) + sources = filter(lambda a: not isFilter(a), srcs_and_filts) srcs = SourceList() for src in sources: if not isinstance(src, SourceFile): - src = Source(src, tags=str(target)) + src = Source(src, tags=[]) srcs.append(src) self.sources = srcs - self.target = target + self.dir = Dir('.') + + def path(self, env): + return self.dir.File(self.target + '.' + env['EXE_SUFFIX']) + + def srcs_to_objs(self, env, sources): + return list([ s.static(env) for s in sources ]) + + @classmethod + def declare_all(cls, env): + return list([ instance.declare(env) for instance in cls.all ]) + + def declare(self, env, objs=None): + if objs is None: + objs = self.srcs_to_objs(env, self.sources) + + if env['STRIP_EXES']: + stripped = self.path(env) + unstripped = env.File(str(stripped) + '.unstripped') + if sys.platform == 'sunos5': + cmd = 'cp $SOURCE $TARGET; strip $TARGET' + else: + cmd = 'strip $SOURCE -o $TARGET' + env.Program(unstripped, objs) + return env.Command(stripped, unstripped, + MakeAction(cmd, Transform("STRIP"))) + else: + return env.Program(self.path(env), objs) + +class UnitTest(Executable): + '''Create a UnitTest''' + def __init__(self, target, *srcs_and_filts, **kwargs): + super(UnitTest, self).__init__(target, *srcs_and_filts) + self.main = kwargs.get('main', False) - UnitTest.all.append(self) + + def declare(self, env): + sources = list(self.sources) + for f in self.filters: + sources = Source.all.apply_filter(f) + objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS'] + if self.main: + objs += env['MAIN_OBJS'] + return super(UnitTest, self).declare(env, objs) + +class GTest(Executable): + '''Create a unit test based on the google test framework.''' + all = [] + def __init__(self, *srcs_and_filts, **kwargs): + super(GTest, self).__init__(*srcs_and_filts) + + self.skip_lib = kwargs.pop('skip_lib', False) + + @classmethod + def declare_all(cls, env): + env = env.Clone() + env.Append(LIBS=env['GTEST_LIBS']) + env.Append(CPPFLAGS=env['GTEST_CPPFLAGS']) + env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib') + env['GTEST_OUT_DIR'] = \ + Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX']) + return super(GTest, cls).declare_all(env) + + def declare(self, env): + sources = list(self.sources) + if not self.skip_lib: + sources += env['GTEST_LIB_SOURCES'] + for f in self.filters: + sources += Source.all.apply_filter(f) + objs = self.srcs_to_objs(env, sources) + + binary = super(GTest, self).declare(env, objs) + + out_dir = env['GTEST_OUT_DIR'] + xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml') + AlwaysBuild(env.Command(xml_file, binary, + "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}")) + + return binary + +class Gem5(Executable): + '''Create a gem5 executable.''' + + def __init__(self, target): + super(Gem5, self).__init__(target) + + def declare(self, env): + objs = env['MAIN_OBJS'] + env['STATIC_OBJS'] + return super(Gem5, self).declare(env, objs) + # Children should have access Export('Source') Export('PySource') Export('SimObject') Export('ProtoBuf') +Export('Executable') Export('UnitTest') +Export('GTest') ######################################################################## # @@ -761,7 +931,7 @@ if env['HAVE_PROTOBUF']: # Add the C++ source file Source(proto.cc_file, tags=proto.tags) elif ProtoBuf.all: - print 'Got protobuf to build, but lacks support!' + print('Got protobuf to build, but lacks support!') Exit(1) # @@ -936,6 +1106,8 @@ for source in PySource.all: # List of constructed environments to pass back to SConstruct date_source = Source('base/date.cc', tags=[]) +gem5_binary = Gem5('gem5') + # Function to create a new build environment as clone of current # environment 'env' with modified object suffix and optional stripped # binary. Additional keyword arguments are appended to corresponding @@ -944,20 +1116,12 @@ def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs): # SCons doesn't know to append a library suffix when there is a '.' in the # name. Use '_' instead. libname = 'gem5_' + label - exename = 'gem5.' + label secondary_exename = 'm5.' + label new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's') new_env.Label = label new_env.Append(**kwargs) - def make_obj(source, static): - '''This function creates a scons node of the requested type.''' - if static: - return new_env.StaticObject(source.tnode) - else: - return new_env.SharedObject(source.tnode) - lib_sources = Source.all.with_tag('gem5 lib') # Without Python, leave out all Python content from the library @@ -969,81 +1133,72 @@ def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs): shared_objs = [] for s in lib_sources.with_tag(Source.ungrouped_tag): - static_objs.append(make_obj(s, True)) - shared_objs.append(make_obj(s, False)) - - partial_objs = [] + static_objs.append(s.static(new_env)) + shared_objs.append(s.shared(new_env)) for group in Source.source_groups: srcs = lib_sources.with_tag(Source.link_group_tag(group)) if not srcs: continue + group_static = [ s.static(new_env) for s in srcs ] + group_shared = [ s.shared(new_env) for s in srcs ] + # If partial linking is disabled, add these sources to the build # directly, and short circuit this loop. if disable_partial: - for s in srcs: - static_objs.append(make_obj(s, True)) - shared_objs.append(make_obj(s, False)) + static_objs.extend(group_static) + shared_objs.extend(group_shared) continue # Set up the static partially linked objects. - source_objs = [ make_obj(s, True) for s in srcs ] file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial") target = File(joinpath(group, file_name)) - partial = env.PartialStatic(target=target, source=source_objs) - static_objs.append(partial) + partial = env.PartialStatic(target=target, source=group_static) + static_objs.extend(partial) # Set up the shared partially linked objects. - source_objs = [ make_obj(s, False) for s in srcs ] file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial") target = File(joinpath(group, file_name)) - partial = env.PartialShared(target=target, source=source_objs) - shared_objs.append(partial) + partial = env.PartialShared(target=target, source=group_shared) + shared_objs.extend(partial) - static_date = make_obj(date_source, static=True) + static_date = date_source.static(new_env) new_env.Depends(static_date, static_objs) - static_objs.append(static_date) + static_objs.extend(static_date) - shared_date = make_obj(date_source, static=False) + shared_date = date_source.shared(new_env) new_env.Depends(shared_date, shared_objs) - shared_objs.append(shared_date) + shared_objs.extend(shared_date) + + main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ] # First make a library of everything but main() so other programs can # link against m5. static_lib = new_env.StaticLibrary(libname, static_objs) shared_lib = new_env.SharedLibrary(libname, shared_objs) - # Now link a stub with main() and the static library. - main_objs = [ make_obj(s, True) for s in Source.all.with_tag('main') ] + # Keep track of the object files generated so far so Executables can + # include them. + new_env['STATIC_OBJS'] = static_objs + new_env['SHARED_OBJS'] = shared_objs + new_env['MAIN_OBJS'] = main_objs - for test in UnitTest.all: - test_sources = Source.all.with_tag(str(test.target)) - test_objs = [ make_obj(s, static=True) for s in test_sources ] - if test.main: - test_objs += main_objs - path = 'unittest/%s.%s' % (test.target, label) - new_env.Program(path, test_objs + static_objs) + new_env['STATIC_LIB'] = static_lib + new_env['SHARED_LIB'] = shared_lib - progname = exename - if strip: - progname += '.unstripped' + # Record some settings for building Executables. + new_env['EXE_SUFFIX'] = label + new_env['STRIP_EXES'] = strip - targets = new_env.Program(progname, main_objs + static_objs) + for cls in ExecutableMeta.all: + cls.declare_all(new_env) - if strip: - if sys.platform == 'sunos5': - cmd = 'cp $SOURCE $TARGET; strip $TARGET' - else: - cmd = 'strip $SOURCE -o $TARGET' - targets = new_env.Command(exename, progname, - MakeAction(cmd, Transform("STRIP"))) + new_env.M5Binary = File(gem5_binary.path(new_env)) - new_env.Command(secondary_exename, exename, + new_env.Command(secondary_exename, new_env.M5Binary, MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK"))) - new_env.M5Binary = targets[0] - # Set up regression tests. SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'), variant_dir=Dir('tests').Dir(new_env.Label), @@ -1086,7 +1241,7 @@ elif env['CLANG']: for target in ['opt', 'fast', 'prof', 'perf']: ccflags[target] += ['-O3'] else: - print 'Unknown compiler, please fix compiler options' + print('Unknown compiler, please fix compiler options') Exit(1)