blob: 0a9a0115f9d13d688d6b4937e82524a3781bcd78 [file] [log] [blame]
Oliver Smith85c2eff2018-09-13 15:39:44 +02001# SPDX-License-Identifier: GPL-2.0-or-later
2# Copyright 2018 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
3
4import atexit
5import collections
6import sys
7import os
8import shutil
9import subprocess
10import tempfile
11
12
13def next_buildable(depends, done):
14 """ Find the next program that can be built, because it has all
15 dependencies satisfied. Initially this would be libosmocore, as it has
16 no dependencies, then the only library that depends on libosmocore and
17 so on.
18
19 :param depends: return value of dependencies.generate()
20 :param done: ordered dict of programs that would already have been
21 built at this point.
22 Example: {"lib-a": "0.11.0", "lib-b": "0.5.0"}
23 """
24 # Iterate over dependencies
25 for program, data in depends.items():
26 # Skip what's already done
27 if program in done:
28 continue
29
30 # Check for missing dependencies
31 depends_done = True
32 for depend in data["depends"]:
33 if depend not in done:
34 depends_done = False
35 break
36
37 # All dependencies satisfied: we have a winner!
38 if depends_done:
39 return program, data["version"]
40
41 # Impossible to build the dependency tree
42 print_dict(done)
43 print("ERROR: can't figure out how to build the rest!")
44 sys.exit(1)
45
46
47def generate(depends):
48 """ Generate an ordered dictionary with the right build order.
49
50 :param depends: return value of dependencies.generate()
51 :returns: an ordered dict like the following:
52 {"libosmocore": "0.11.0",
53 "libosmo-abis": "0.5.0",
54 "osmo-bts": "master"} """
55 # Iterate over dependencies
56 ret = collections.OrderedDict()
57 count = len(depends.keys())
58 while len(ret) != count:
59 # Continue with the one without unsatisfied dependencies
60 program, version = next_buildable(depends, ret)
61 ret[program] = version
62 return ret
63
64
65def print_dict(stack):
66 """ Print the whole build stack.
67 :param stack: return value from generate() above """
68 print("Build order:")
69 for program, version in stack.items():
70 print(" * " + program + ":" + version)
71
72
Oliver Smitha99a4ef2018-09-21 10:29:51 +020073def set_environment(jobs, prefix):
Oliver Smith85c2eff2018-09-13 15:39:44 +020074 """ Configure the environment variables before running configure, make etc.
75
76 :param jobs: parallel build jobs (for make)
Oliver Smitha99a4ef2018-09-21 10:29:51 +020077 :param prefix: installation folder
Oliver Smith85c2eff2018-09-13 15:39:44 +020078 """
Oliver Smitha99a4ef2018-09-21 10:29:51 +020079 # Add prefix to PKG_CONFIG_PATH and LD_LIBRARY_PATH
80 extend = {"PKG_CONFIG_PATH": prefix + "/lib/pkgconfig",
81 "LD_LIBRARY_PATH": prefix + "/lib"}
Oliver Smith85c2eff2018-09-13 15:39:44 +020082 for env_var, folder in extend.items():
83 old = os.environ[env_var] if env_var in os.environ else ""
84 os.environ[env_var] = old + ":" + folder
85
86 # Set JOBS for make
87 os.environ["JOBS"] = str(jobs)
88
89
Oliver Smitha99a4ef2018-09-21 10:29:51 +020090def build(workdir, jobs, stack):
Oliver Smith85c2eff2018-09-13 15:39:44 +020091 """ Build one program with all its dependencies.
92
Oliver Smitha99a4ef2018-09-21 10:29:51 +020093 :param workdir: path to where all data (git, build, install) is stored
Oliver Smith85c2eff2018-09-13 15:39:44 +020094 :param jobs: parallel build jobs (for make)
95 :param stack: the build stack as returned by generate() above
96
97 The dependencies.clone() function has already downloaded missing
98 sources and checked out the right version tags. So in this function we
99 can directly enter the source folder and run the build commands.
100
101 Notes about the usage of 'make clean' and 'make distclean':
102 * Without 'make clean' we might have files in the build directory with
103 a different prefix hardcoded (e.g. from a previous run of
104 osmo-depcheck):
105 <https://lists.gnu.org/archive/html/libtool/2006-12/msg00011.html>
106 * 'make distclean' gets used to remove everything that mentioned the
107 prefix set by osmo-depcheck. That way the user won't have it set
108 anymore in case they decide to compile the code again manually from
109 the source folder. """
110 # Prepare the install folder and environment
Oliver Smitha99a4ef2018-09-21 10:29:51 +0200111 prefix = workdir + "/install"
112 unitdir = prefix + "/lib/systemd/system/"
113 set_environment(jobs, prefix)
Oliver Smith85c2eff2018-09-13 15:39:44 +0200114
115 # Iterate over stack
116 for program, version in stack.items():
117 print("Building " + program + ":" + version)
Oliver Smitha99a4ef2018-09-21 10:29:51 +0200118
119 # Create and enter the build folder
120 builddir = workdir + "/build/" + program
121 os.mkdir(builddir)
122 os.chdir(builddir)
Oliver Smith85c2eff2018-09-13 15:39:44 +0200123
124 # Run the build commands
Oliver Smitha99a4ef2018-09-21 10:29:51 +0200125 gitdir = workdir + "/git/" + program
126 commands = [["autoreconf", "-fi", gitdir],
127 [gitdir + "/configure", "--prefix", prefix,
Oliver Smith85c2eff2018-09-13 15:39:44 +0200128 "--with-systemdsystemunitdir=" + unitdir],
129 ["make", "clean"],
130 ["make"],
131 ["make", "install"],
132 ["make", "distclean"]]
133 for command in commands:
134 print("+ " + " ".join(command))
135 subprocess.run(command, check=True)