| # 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') |
| |
| main.SConsignFile(build_dir.File("sconsign").abspath) |
| |
| 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 ]) |
| main.Append(CXXFLAGS=[ '-std=c++14' ]) |
| |
| 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') |
| main['JARCOMSTR'] = ComStr('JAR') |
| |
| 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 _extract_abi_opt_val(name, default): |
| var_name = env.subst('${ABI}.%s' % name) |
| return os.environ.get(var_name, ARGUMENTS.get(var_name, default)) |
| def get_abi_opt(name, default): |
| env[name] = _extract_abi_opt_val(name, default) |
| def append_abi_opt(name): |
| env.Append(**{ name: _extract_abi_opt_val(name, '') }) |
| |
| # 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' ]) |
| |
| # The user can pass extra build flags for each ABI |
| append_abi_opt('CCFLAGS') |
| append_abi_opt('CXXFLAGS') |
| append_abi_opt('LINKFLAGS') |
| |
| # 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') |