| # SPDX-License-Identifier: GPL-2.0-or-later |
| # Copyright 2018 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> |
| |
| import atexit |
| import collections |
| import sys |
| import os |
| import shutil |
| import subprocess |
| import tempfile |
| |
| |
| def next_buildable(depends, done): |
| """ Find the next program that can be built, because it has all |
| dependencies satisfied. Initially this would be libosmocore, as it has |
| no dependencies, then the only library that depends on libosmocore and |
| so on. |
| |
| :param depends: return value of dependencies.generate() |
| :param done: ordered dict of programs that would already have been |
| built at this point. |
| Example: {"lib-a": "0.11.0", "lib-b": "0.5.0"} |
| """ |
| # Iterate over dependencies |
| for program, data in depends.items(): |
| # Skip what's already done |
| if program in done: |
| continue |
| |
| # Check for missing dependencies |
| depends_done = True |
| for depend in data["depends"]: |
| if depend not in done: |
| depends_done = False |
| break |
| |
| # All dependencies satisfied: we have a winner! |
| if depends_done: |
| return program, data["version"] |
| |
| # Impossible to build the dependency tree |
| print_dict(done) |
| print("ERROR: can't figure out how to build the rest!") |
| sys.exit(1) |
| |
| |
| def generate(depends): |
| """ Generate an ordered dictionary with the right build order. |
| |
| :param depends: return value of dependencies.generate() |
| :returns: an ordered dict like the following: |
| {"libosmocore": "0.11.0", |
| "libosmo-abis": "0.5.0", |
| "osmo-bts": "master"} """ |
| # Iterate over dependencies |
| ret = collections.OrderedDict() |
| count = len(depends.keys()) |
| while len(ret) != count: |
| # Continue with the one without unsatisfied dependencies |
| program, version = next_buildable(depends, ret) |
| ret[program] = version |
| return ret |
| |
| |
| def print_dict(stack): |
| """ Print the whole build stack. |
| :param stack: return value from generate() above """ |
| print("Build order:") |
| for program, version in stack.items(): |
| print(" * " + program + ":" + version) |
| |
| |
| def set_environment(jobs, prefix): |
| """ Configure the environment variables before running configure, make etc. |
| |
| :param jobs: parallel build jobs (for make) |
| :param prefix: installation folder |
| """ |
| # Add prefix to PKG_CONFIG_PATH and LD_LIBRARY_PATH |
| extend = {"PKG_CONFIG_PATH": prefix + "/lib/pkgconfig", |
| "LD_LIBRARY_PATH": prefix + "/lib"} |
| for env_var, folder in extend.items(): |
| old = os.environ[env_var] if env_var in os.environ else "" |
| os.environ[env_var] = old + ":" + folder |
| |
| # Set JOBS for make |
| os.environ["JOBS"] = str(jobs) |
| |
| |
| def build(workdir, jobs, stack): |
| """ Build one program with all its dependencies. |
| |
| :param workdir: path to where all data (git, build, install) is stored |
| :param jobs: parallel build jobs (for make) |
| :param stack: the build stack as returned by generate() above |
| |
| The dependencies.clone() function has already downloaded missing |
| sources and checked out the right version tags. So in this function we |
| can directly enter the source folder and run the build commands. |
| |
| Notes about the usage of 'make clean' and 'make distclean': |
| * Without 'make clean' we might have files in the build directory with |
| a different prefix hardcoded (e.g. from a previous run of |
| osmo-depcheck): |
| <https://lists.gnu.org/archive/html/libtool/2006-12/msg00011.html> |
| * 'make distclean' gets used to remove everything that mentioned the |
| prefix set by osmo-depcheck. That way the user won't have it set |
| anymore in case they decide to compile the code again manually from |
| the source folder. """ |
| # Prepare the install folder and environment |
| prefix = workdir + "/install" |
| unitdir = prefix + "/lib/systemd/system/" |
| set_environment(jobs, prefix) |
| |
| # Iterate over stack |
| for program, version in stack.items(): |
| print("Building " + program + ":" + version) |
| |
| # Create and enter the build folder |
| builddir = workdir + "/build/" + program |
| os.mkdir(builddir) |
| os.chdir(builddir) |
| |
| # Run the build commands |
| gitdir = workdir + "/git/" + program |
| commands = [["autoreconf", "-fi", gitdir], |
| [gitdir + "/configure", "--prefix", prefix, |
| "--with-systemdsystemunitdir=" + unitdir], |
| ["make", "clean"], |
| ["make"], |
| ["make", "install"], |
| ["make", "distclean"]] |
| for command in commands: |
| print("+ " + " ".join(command)) |
| subprocess.run(command, check=True) |