| # DExTer : Debugging Experience Tester |
| # ~~~~~~ ~ ~~ ~ ~~ |
| # |
| # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| # See https://llvm.org/LICENSE.txt for license information. |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| """Deals with the processing execution of shell or batch build scripts.""" |
| |
| import os |
| import subprocess |
| import unittest |
| |
| from dex.dextIR import BuilderIR |
| from dex.utils import Timer |
| from dex.utils.Exceptions import BuildScriptException |
| |
| |
| def _quotify(text): |
| if '"' in text or ' ' not in text: |
| return text |
| return '"{}"'.format(text) |
| |
| |
| def _get_script_environment(source_files, compiler_options, |
| linker_options, executable_file): |
| |
| source_files = [_quotify(f) for f in source_files] |
| object_files = [ |
| _quotify('{}.o'.format(os.path.basename(f))) for f in source_files |
| ] |
| source_indexes = ['{:02d}'.format(i + 1) for i in range(len(source_files))] |
| |
| env_variables = {} |
| env_variables['SOURCE_INDEXES'] = ' '.join(source_indexes) |
| env_variables['SOURCE_FILES'] = ' '.join(source_files) |
| env_variables['OBJECT_FILES'] = ' '.join(object_files) |
| env_variables['LINKER_OPTIONS'] = linker_options |
| |
| for i, _ in enumerate(source_files): |
| index = source_indexes[i] |
| env_variables['SOURCE_FILE_{}'.format(index)] = source_files[i] |
| env_variables['OBJECT_FILE_{}'.format(index)] = object_files[i] |
| env_variables['COMPILER_OPTIONS_{}'.format(index)] = compiler_options[i] |
| |
| env_variables['EXECUTABLE_FILE'] = executable_file |
| |
| return env_variables |
| |
| |
| def run_external_build_script(context, script_path, source_files, |
| compiler_options, linker_options, |
| executable_file): |
| """Build an executable using a builder script. |
| |
| The executable is saved to `context.working_directory.path`. |
| |
| Returns: |
| ( stdout (str), stderr (str), builder (BuilderIR) ) |
| """ |
| |
| builderIR = BuilderIR( |
| name=context.options.builder, |
| cflags=compiler_options, |
| ldflags=linker_options, |
| ) |
| assert len(source_files) == len(compiler_options), (source_files, |
| compiler_options) |
| |
| script_environ = _get_script_environment(source_files, compiler_options, |
| linker_options, executable_file) |
| env = dict(os.environ) |
| env.update(script_environ) |
| try: |
| with Timer('running build script'): |
| process = subprocess.Popen( |
| [script_path], |
| cwd=context.working_directory.path, |
| env=env, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| out, err = process.communicate() |
| returncode = process.returncode |
| out = out.decode('utf-8') |
| err = err.decode('utf-8') |
| if returncode != 0: |
| raise BuildScriptException( |
| '{}: failed with returncode {}.\nstdout:\n{}\n\nstderr:\n{}\n'. |
| format(script_path, returncode, out, err), |
| script_error=err) |
| return out, err, builderIR |
| except OSError as e: |
| raise BuildScriptException('{}: {}'.format(e.strerror, script_path)) |
| |
| |
| class TestBuilder(unittest.TestCase): |
| def test_get_script_environment(self): |
| source_files = ['a.a', 'b.b'] |
| compiler_options = ['-option1 value1', '-option2 value2'] |
| linker_options = '-optionX valueX' |
| executable_file = 'exe.exe' |
| env = _get_script_environment(source_files, compiler_options, |
| linker_options, executable_file) |
| |
| assert env['SOURCE_FILES'] == 'a.a b.b' |
| assert env['OBJECT_FILES'] == 'a.a.o b.b.o' |
| |
| assert env['SOURCE_INDEXES'] == '01 02' |
| assert env['LINKER_OPTIONS'] == '-optionX valueX' |
| |
| assert env['SOURCE_FILE_01'] == 'a.a' |
| assert env['SOURCE_FILE_02'] == 'b.b' |
| |
| assert env['OBJECT_FILE_01'] == 'a.a.o' |
| assert env['OBJECT_FILE_02'] == 'b.b.o' |
| |
| assert env['EXECUTABLE_FILE'] == 'exe.exe' |
| |
| assert env['COMPILER_OPTIONS_01'] == '-option1 value1' |
| assert env['COMPILER_OPTIONS_02'] == '-option2 value2' |