scons: Generalize the Executable class to cover libraries too.

This way the shared and static gem5 libraries can be treated like other
top level build targets.

Change-Id: I04dd82f9be86df0a5cabd2e4934077c33235911c
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/48369
Maintainer: Gabe Black <gabe.black@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Hoa Nguyen <hoanguyen@ucdavis.edu>
This commit is contained in:
Gabe Black
2021-07-18 22:56:40 -07:00
parent 0703c968ca
commit 98b50f3f2b

View File

@@ -532,23 +532,25 @@ def GrpcProtoBuf(source, tags=None, add_tags=None):
Source(env.ProtoBufCC(source=source)[0], tags=tags, add_tags=add_tags)
exectuable_classes = []
class ExecutableMeta(type):
'''Meta class for Executables.'''
date_source = File('base/date.cc')
class TopLevelMeta(type):
'''Meta class for top level build products, ie binaries and libraries.'''
all = []
def __init__(cls, name, bases, d):
ExecutableMeta.all.append(cls)
super(ExecutableMeta, cls).__init__(name, bases, d)
TopLevelMeta.all.append(cls)
super(TopLevelMeta, cls).__init__(name, bases, d)
cls.all = []
class Executable(object, metaclass=ExecutableMeta):
'''Base class for creating an executable from sources.'''
class TopLevelBase(object, metaclass=TopLevelMeta):
'''Base class for linked build products.'''
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__()
super(TopLevelBase, self).__init__()
self.all.append(self)
self.target = target
@@ -561,12 +563,15 @@ class Executable(object, metaclass=ExecutableMeta):
if not isinstance(src, SourceFile):
src = Source(src, tags=[])
srcs.append(src)
self.srcs = srcs
self.sources = srcs
self.dir = Dir('.')
def path(self, env):
return self.dir.File(self.target + '.${EXE_SUFFIX}')
def sources(self, env):
srcs = self.srcs
for f in self.filters:
srcs += Source.all.apply_filter(env, f)
return srcs
def srcs_to_objs(self, env, sources):
return list([ s.static(env) for s in sources ])
@@ -575,15 +580,49 @@ class Executable(object, metaclass=ExecutableMeta):
def declare_all(cls, env):
return list([ instance.declare(env) for instance in cls.all ])
class StaticLib(TopLevelBase):
'''Base class for creating a static library from sources.'''
def declare(self, env):
objs = self.srcs_to_objs(env, self.sources(env))
date_obj = env.StaticObject(date_source)
env.Depends(date_obj, objs)
return env.StaticLibrary(self.target, [date_obj, objs])[0]
class SharedLib(TopLevelBase):
'''Base class for creating a shared library from sources.'''
def srcs_to_objs(self, env, sources):
return list([ s.shared(env) for s in sources ])
def declare(self, env):
objs = self.srcs_to_objs(env, self.sources(env))
date_obj = env.SharedObject(date_source)
env.Depends(date_obj, objs)
return env.SharedLibrary(self.target, [date_obj, objs])[0]
class Executable(TopLevelBase):
'''Base class for creating an executable from sources.'''
def path(self, env):
return self.dir.File(self.target + '.${ENV_LABEL}')
def declare(self, env, objs=None):
if objs is None:
objs = self.srcs_to_objs(env, self.sources)
objs = self.srcs_to_objs(env, self.sources(env))
env = env.Clone()
env['BIN_RPATH_PREFIX'] = os.path.relpath(
env['BUILDDIR'], self.path(env).dir.abspath)
executable = env.Program(self.path(env).abspath, objs)[0]
date_obj = env.StaticObject(date_source)
env.Depends(date_obj, objs)
executable = env.Program(self.path(env).abspath, [date_obj, objs])[0]
if sys.platform == 'sunos5':
cmd = 'cp $SOURCE $TARGET; strip $TARGET'
@@ -598,10 +637,10 @@ class GTest(Executable):
'''Create a unit test based on the google test framework.'''
all = []
def __init__(self, *srcs_and_filts, **kwargs):
if not kwargs.pop('skip_lib', False):
srcs_and_filts = srcs_and_filts + (with_tag('gtest lib'),)
super(GTest, self).__init__(*srcs_and_filts)
self.skip_lib = kwargs.pop('skip_lib', False)
@classmethod
def declare_all(cls, env):
env = env.Clone()
@@ -609,20 +648,12 @@ class GTest(Executable):
env['SHOBJSUFFIX'] = '.t' + env['SHOBJSUFFIX'][1:]
env.Append(LIBS=env['GTEST_LIBS'])
env.Append(CPPFLAGS=env['GTEST_CPPFLAGS'])
env['GTEST_LIB_SOURCES'] = Source.all.with_tag(env, 'gtest lib')
env['GTEST_OUT_DIR'] = \
Dir(env['BUILDDIR']).Dir('unittests.${EXE_SUFFIX}')
Dir(env['BUILDDIR']).Dir('unittests.${ENV_LABEL}')
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(env, f)
objs = self.srcs_to_objs(env, sources)
binary = super(GTest, self).declare(env, objs)
binary, stripped = super(GTest, self).declare(env)
out_dir = env['GTEST_OUT_DIR']
xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml')
@@ -631,16 +662,6 @@ class GTest(Executable):
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('GdbXml')
@@ -1240,11 +1261,6 @@ if main['USE_PYTHON']:
# a slightly different build environment.
#
# List of constructed environments to pass back to SConstruct
date_source = Source('base/date.cc', tags=[])
Gem5('gem5')
env['SHOBJSUFFIX'] = '${OBJSUFFIX}s'
envs = {
@@ -1306,50 +1322,26 @@ for ext in target_exts:
break
# SCons doesn't know to append a library suffix when there is a '.' in the
# name. Use '_' instead.
lib_name = 'gem5_${ENV_LABEL}'
lib_filter = with_tag('gem5 lib')
# Without Python, leave out all Python content from the library builds. The
# option doesn't affect gem5 built as a program.
if GetOption('without_python'):
lib_filter = lib_filter & without_tag('python')
StaticLib(lib_name, lib_filter)
SharedLib(lib_name, lib_filter)
Executable('gem5', with_any_tags('gem5 lib', 'main'))
# Function to create a new build environment as clone of current
# environment 'env' with modified object suffix and optional stripped
# binary.
for env in (envs[e] for e in needed_envs):
# SCons doesn't know to append a library suffix when there is a '.' in the
# name. Use '_' instead.
libname = 'gem5_${ENV_LABEL}'
lib_sources = Source.all.with_tag(env, 'gem5 lib')
# Without Python, leave out all Python content from the library
# builds. The option doesn't affect gem5 built as a program
if GetOption('without_python'):
lib_sources = lib_sources.without_tag(env, 'python')
static_objs = list([ s.static(env) for s in lib_sources ])
shared_objs = list([ s.shared(env) for s in lib_sources ])
static_date = date_source.static(env)
env.Depends(static_date, static_objs)
static_objs.extend(static_date)
shared_date = date_source.shared(env)
env.Depends(shared_date, shared_objs)
shared_objs.extend(shared_date)
main_objs = [ s.static(env) for s in Source.all.with_tag(env, 'main') ]
# First make a library of everything but main() so other programs can
# link against m5.
static_lib = env.StaticLibrary(libname, static_objs)
shared_lib = env.SharedLibrary(libname, shared_objs)
# Keep track of the object files generated so far so Executables can
# include them.
env['STATIC_OBJS'] = static_objs
env['SHARED_OBJS'] = shared_objs
env['MAIN_OBJS'] = main_objs
env['STATIC_LIB'] = static_lib
env['SHARED_LIB'] = shared_lib
# Record some settings for building Executables.
env['EXE_SUFFIX'] = '${ENV_LABEL}'
for cls in ExecutableMeta.all:
for cls in TopLevelMeta.all:
cls.declare_all(env)