# Copyright 2020 Google, Inc. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer; # redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution; # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import copy import os main = Environment() gem5_root = Dir('..').Dir('..') # Includes which are shared with gem5 itself. common_include = gem5_root.Dir('include') ext_dir = gem5_root.Dir('ext') googletest_dir = ext_dir.Dir('googletest') src_dir = Dir('src') build_dir = Dir('build') def abspath(d): return os.path.abspath(str(d)) AddOption('--debug-build', dest='debug_build', action='store_true', help='Build with debug info, and disable optimizations.') AddOption('--run-tests', dest='run_tests', action='store_true', help='Enable test output xml files as build targets.') AddOption('--verbose', dest='verbose', action='store_true') # Universal settings. if GetOption('debug_build'): main.Append(CXXFLAGS=[ '-O0', '-g' ]) main.Append(CCFLAGS=[ '-O0', '-g' ]) else: main.Append(CXXFLAGS=[ '-O2' ]) main.Append(CCFLAGS=[ '-O2' ]) main.Append(CPPPATH=[ common_include ]) if not GetOption('verbose'): # A functor which returns a shorter summary string to replace the normal # scons output when running a command. class ComStr(object): def __init__(self, cmd): self.cmd = cmd def __call__(self, target, source, env, for_signature=None): tgts = list([str(t).strip() for t in target]) return self.cmd + ' ' + ', '.join(tgts) main['CXXCOMSTR'] = ComStr('CXX') main['SHCXXCOMSTR'] = ComStr('SHCXX') main['CCCOMSTR'] = ComStr('CC') main['SHCCCOMSTR'] = ComStr('SHCC') main['LINKCOMSTR'] = ComStr('LINK') main['SHLINKCOMSTR'] = ComStr('SHLINK') main['ASCOMSTR'] = ComStr('AS') main['ASPPCOMSTR'] = ComStr('ASPP') main['ARCOMSTR'] = ComStr('AR') main['RANLIBCOMSTR'] = ComStr('RANLIB') def MakeAction(action, string, *args, **kwargs): def func(target, source, env, executor): tgts = list([str(t).strip() for t in target]) return string + ' ' + ', '.join(tgts) return Action(action, func, *args, **kwargs) else: def MakeAction(action, string, *args, **kwargs): return Action(action, *args, **kwargs) # Propogate the environment's PATH setting. main['ENV']['PATH'] = os.environ['PATH'] # Pass through terminal information to, for instance, enable color output. main['ENV']['TERM'] = os.environ['TERM'] # Pass through the java CLASSPATH (if it exists) so we can find libraries. main['ENV']['CLASSPATH'] = os.environ.get('CLASSPATH', '') # Detect some dependencies of some forms of the m5 utility/library. def CheckForJavaPkg(context, pkg_name): context.Message('Checking for java package %s...' % pkg_name) result = main['HAVE_JAVA'] and \ context.TryAction('${JAVAC} ${JAVACFLAGS} ${SOURCES}', 'import %s.*;' % pkg_name, '.java')[0] context.Result(result) return result def CheckForPkgConfigPackage(context, package): context.Message('Checking for pkg-config package %s...' % package) result = main['HAVE_PKG_CONFIG'] and \ os.system('pkg-config --exists %s' % package) == 0 context.Result(result) return result; conf = Configure(main, conf_dir=build_dir.Dir('.scons_config'), log_file=build_dir.File('scons_config.log'), custom_tests={ 'CheckForJavaPkg' : CheckForJavaPkg, 'CheckForPkgConfigPackage' : CheckForPkgConfigPackage }) main['HAVE_JAVA'] = all(key in main for key in ('JAVAC', 'JAR')) if not main['HAVE_JAVA']: print('javac and/or jar not detected, not building java wrapper.') main['HAVE_JUNIT'] = conf.CheckForJavaPkg('org.junit') if main['HAVE_JAVA'] and not main['HAVE_JUNIT']: print('junit test framework not found, not build java wrapper test') main['HAVE_PKG_CONFIG'] = conf.CheckProg('pkg-config') if not main['HAVE_PKG_CONFIG']: print("pkg-config not detected, can't check for lua51.") main['HAVE_LUA51'] = conf.CheckForPkgConfigPackage('lua51') if not main['HAVE_LUA51']: print('lua 5.1 not detected, not building lua wrapper.') conf.Finish() # Put the sconsign file in the build dir so everything can be deleted at once. main.SConsignFile(os.path.join(abspath(build_dir), 'sconsign')) # Use soft links instead of hard links when setting up a build directory. main.SetOption('duplicate', 'soft-copy') def GTest(env, name, *srcs, **kwargs): if 'GTEST_ENV' not in env: gtest_env = env.Clone(OBJSUFFIX='.to', SHOBJSUFFIX='.sto') gtest_env.Append(CPPFLAGS=[ '${GTEST_CPPFLAGS}' ]) gtest_env.Append(LIBS=[ '${GTEST_LIBS}' ]) env['GTEST_ENV'] = gtest_env if not srcs: srcs = [ name + '.cc', name + '.test.cc' ] test_bin = env['GTEST_ENV'].Program('test/bin/%s' % name, srcs, **kwargs) # The native environment doesn't need QEMU, and doesn't define HAVE_QEMU. need_qemu_to_run = 'HAVE_QEMU' in env; # If we can run this test... if GetOption('run_tests') and (not need_qemu_to_run or env['HAVE_QEMU']): # An XML file which holds the results of the test. xml = Dir('test').Dir('result').File('%s.xml' % name) # The basic command line for the test. cmd = '${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}' cmd_str = 'TEST' if need_qemu_to_run: # A prefix that runs it in QEMU if necessary. cmd = '${QEMU} -L ${QEMU_SYSROOT} -- ' + cmd cmd_str = 'QEMU_TEST' AlwaysBuild(env.Command(xml, test_bin, MakeAction(cmd, cmd_str))) Export('MakeAction') main.AddMethod(GTest) native = main.Clone() native_dir = build_dir.Dir('native') # Bring in the googletest sources. native.SConscript(googletest_dir.File('SConscript'), variant_dir=native_dir.Dir('googletest'), exports={ 'main': native }) native.SConscript(src_dir.File('SConscript.native'), variant_dir=native_dir, exports={ 'env': native }) main['CC'] = '${CROSS_COMPILE}gcc' main['CXX'] = '${CROSS_COMPILE}g++' main['AS'] = '${CROSS_COMPILE}as' main['LD'] = '${CROSS_COMPILE}ld' main['AR'] = '${CROSS_COMPILE}ar' main['QEMU'] = 'qemu-${QEMU_ARCH}' class CallType(object): def __init__(self, name): self.name = name self.impl_file = None self.enabled = False self.verifier = None self.default = False def impl(self, impl, verifier=None, default=False): self.impl_file = impl self.enabled = True self.verifier = verifier self.default = default # Being the default can be disabled for testing purposes, so we can tell if # a call type was selected because it was chosen, or because nobody else # was. def setup_env(self, env, allow_default=True): env = env.Clone() is_default = 'true' if self.default and allow_default else 'false' env.Append(CXXFLAGS=[ '-DCALL_TYPE_IS_DEFAULT=%s' % is_default ]) return env call_types = { # Magic instruction. 'inst': CallType('inst'), # Magic address. 'addr': CallType('addr'), # Semihosting extension. 'semi': CallType('semi'), } for root, dirs, files in os.walk(abspath(src_dir)): # Each SConsopts file describes an ABI of the m5 utility. if 'SConsopts' in files: env = main.Clone() env['CALL_TYPE'] = copy.deepcopy(call_types) # The user may override ABI settings by setting environment # variables of the form ${ABI}.${OPTION}. For instance, to set the # CROSS_COMPILE prefix for abi foo to bar-, the user would set an # environment variable foo.CROSS_COMPILE=bar-. # # This also considers scons command line settings which may look like # environment variables, but are set after "scons" on the command line. def get_abi_opt(name, default): var_name = env.subst('${ABI}.%s' % name) env[name] = os.environ.get( var_name, ARGUMENTS.get(var_name, default)) # Process the ABI's settings in the SConsopts file, storing them # in a copy of the primary environment. env.SConscript(Dir(root).File('SConsopts'), exports=[ 'env', 'get_abi_opt' ]) # Check if this version of QEMU is available for running unit tests. env['HAVE_QEMU'] = env.Detect('${QEMU}') is not None if env['HAVE_QEMU'] and env.Detect('${CC}'): sysroot_cmd = env.subst('${CC} -print-sysroot') sysroot = os.popen(sysroot_cmd).read().strip() env['QEMU_SYSROOT'] = sysroot # Once all the options have been configured, set up build targets for # this abi. abi_dir = build_dir.Dir(env.subst('${ABI}')) # Bring in the googletest sources. env.SConscript(googletest_dir.File('SConscript'), variant_dir=abi_dir.Dir('googletest'), exports={ 'main': env }) env.SConscript(src_dir.File('SConscript'), variant_dir=abi_dir, exports='env')