| # Copyright 2014 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Unittest-only utility functions library.""" |
| |
| import os |
| |
| from chromite.lib import cros_build_lib |
| from chromite.lib import osutils |
| from chromite.lib import sysroot_lib |
| |
| |
| class BuildELFError(Exception): |
| """Generic error building an ELF file.""" |
| |
| |
| def BuildELF( |
| filename, |
| defined_symbols=None, |
| undefined_symbols=None, |
| used_libs=None, |
| executable=False, |
| static=False, |
| ): |
| """Builds a dynamic ELF with the provided import and exports. |
| |
| Compiles and links a dynamic program that exports some functions, as |
| libraries do, and requires some symbols from other libraries. Dependencies |
| should live in the same directory as the result. This function |
| |
| Args: |
| filename: The output filename where the ELF is created. |
| defined_symbols: The list of symbols this ELF exports. |
| undefined_symbols: The list of symbols this ELF requires from other |
| ELFs. |
| used_libs: The list of libraries this ELF loads dynamically, including |
| only the name of the library. For example, 'bz2' rather than |
| 'libbz2.so.1.0'. |
| executable: Whether the file has a main() function. |
| static: Whether the file is statically linked (implies executable=True). |
| """ |
| if defined_symbols is None: |
| defined_symbols = [] |
| if undefined_symbols is None: |
| undefined_symbols = [] |
| if used_libs is None: |
| used_libs = [] |
| if static and not executable: |
| raise ValueError("static requires executable to be True.") |
| |
| source = "".join("void %s();\n" % sym for sym in undefined_symbols) |
| source += """ |
| void __defined_symbols(const char*) __attribute__ ((visibility ("hidden"))); |
| void __defined_symbols(const char* sym) { |
| %s |
| } |
| """ % ( |
| "\n ".join("%s();" % sym for sym in undefined_symbols) |
| ) |
| |
| source += "".join( |
| """ |
| void %s() __attribute__ ((visibility ("default"))); |
| void %s() { __defined_symbols("%s"); } |
| """ |
| % (sym, sym, sym) |
| for sym in defined_symbols |
| ) |
| |
| if executable: |
| source += """ |
| int main() { |
| __defined_symbols("main"); |
| return 42; |
| } |
| """ |
| source_fn = filename + "_tmp.c" |
| osutils.WriteFile(source_fn, source) |
| |
| outdir = os.path.dirname(filename) |
| cmd = ["gcc", "-o", filename, source_fn] |
| if not executable: |
| cmd += ["-shared", "-fPIC"] |
| if static: |
| cmd += ["-static"] |
| cmd += ["-L.", "-Wl,-rpath=./"] |
| cmd += ["-l%s" % lib for lib in used_libs] |
| try: |
| cros_build_lib.run( |
| cmd, cwd=outdir, stdout=True, stderr=True, print_cmd=False |
| ) |
| except cros_build_lib.RunCommandError as e: |
| raise BuildELFError(f"{e}\n{e.stderr}") |
| finally: |
| os.unlink(source_fn) |
| |
| |
| def create_stub_make_conf(sysroot: os.PathLike): |
| """Create a stub sysroot_lib._MAKE_CONF for tests to correctly read configs. |
| |
| sysroot_lib expects sysroot_lib._MAKE_CONF (etc/make.conf) to exist and to |
| source sysroot_lib._MAKE_CONF_BOARD_SETUP (etc/make.conf.board_setup) to |
| read the config. For tests to read their expected config, a stub _MAKE_CONF |
| needs to be created if they are using sysroot_lib.WriteConfig(). |
| |
| Args: |
| sysroot: The path to the sysroot |
| """ |
| # pylint: disable=protected-access |
| osutils.WriteFile( |
| os.path.join(sysroot, sysroot_lib._MAKE_CONF), |
| "source make.conf.board_setup", |
| makedirs=True, |
| ) |