blob: e6a9480054d2b499a58eccb2faa1a340d2b309de [file] [log] [blame]
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +02001#!/usr/bin/env python3
2'''
3Generate a top-level makefile that builds the Osmocom 2G + 3G network components.
4
Oliver Smith0a4d8ea2021-07-29 14:32:29 +02005 ./gen_makefile.py [configure.opts [more.opts]] [-o Makefile.output]
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +02006
7Configured by text files:
8
Oliver Smith0a4d8ea2021-07-29 14:32:29 +02009 all.deps: whitespace-separated listing of
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020010 project_name depends_on_project_1 depends_on_project_2 ...
11
12 *.opts: whitespace-separated listing of
13 project_name --config-opt-1 --config-opt-2 ...
14
15Thus it is possible to choose between e.g.
Oliver Smith0a4d8ea2021-07-29 14:32:29 +020016- building each of those with or without mgcp transcoding support by adding or
17 removing "transcoding.opts" from the command line
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020018
19From the Makefile nature, the dependencies extend, no need to repeat common deps.
20
21When this script is done, a Makefile has been generated that allows you to
22build all projects at once by issuing 'make', but also to refresh only parts of
23it when some bits in the middle have changed. The makefile keeps local progress
24marker files like .make.libosmocore.configure; if such progress marker is
25removed or becomes outdated, that step and all dependent ones are re-run.
26This is helpful in daily hacking across several repositories.
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +010027
28Note that by default, this includes 'sudo ldconfig' calls following each
29installation. You may want to permit your user to run 'sudo ldconfig' without
30needing a password, e.g. by
31
32 sudo sh -c "echo '$USER ALL= NOPASSWD: /sbin/ldconfig' > /etc/sudoers.d/${USER}_ldconfig"
33
34You can skip the 'sudo ldconfig' by issuing the --no-ldconfig option.
35
36You can run 'ldconfig' without sudo by issuing the --ldconfig-without-sudo option.
37
38By default, it is assumed that your user has write permission to /usr/local. If you
39need sudo to install there, you may issue the --sudo-make-install option.
Neels Hofmeyr2535a262018-01-16 16:34:32 +010040
41EXAMPLE:
42
Oliver Smith0a4d8ea2021-07-29 14:32:29 +020043 ./gen_makefile.py default.opts iu.opts -I -m build
Neels Hofmeyr2535a262018-01-16 16:34:32 +010044 cd build
45 make
46
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020047'''
48
49import sys
50import os
51import argparse
52
Oliver Smith0a4d8ea2021-07-29 14:32:29 +020053topdir = os.path.dirname(os.path.realpath(__file__))
54all_deps_file = os.path.join(topdir, "all.deps")
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020055parser = argparse.ArgumentParser(epilog=__doc__, formatter_class=argparse.RawTextHelpFormatter)
56
Neels Hofmeyr450dac72017-08-22 19:27:08 +020057parser.add_argument('configure_opts_files',
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020058 help='''Config file containing project name and
59./configure options''',
Neels Hofmeyr450dac72017-08-22 19:27:08 +020060 nargs='*')
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020061
62parser.add_argument('-m', '--make-dir', dest='make_dir',
63 help='''Place Makefile in this dir (default: create
Oliver Smith0a4d8ea2021-07-29 14:32:29 +020064a new dir named after opts files).''')
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020065
66parser.add_argument('-s', '--src-dir', dest='src_dir', default='./src',
67 help='Parent dir for all git clones.')
68
69parser.add_argument('-b', '--build-dir', dest='build_dir',
70 help='''Parent dir for all build trees (default:
71directly in the make-dir).''')
72
Neels Hofmeyrbffdc302017-12-06 00:31:49 +010073parser.add_argument('-u', '--url', dest='url', default='git://git.osmocom.org',
74 help='''git clone base URL. Default is 'git://git.osmocom.org'.
75e.g. with a config like this in your ~/.ssh/config:
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020076 host go
77 hostname gerrit.osmocom.org
78 port 29418
Neels Hofmeyrbffdc302017-12-06 00:31:49 +010079you may pass '-u ssh://go' to be able to submit to gerrit.''')
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020080
Neels Hofmeyr972c2942018-03-16 03:49:58 +010081parser.add_argument('-p', '--push-url', dest='push_url', default='',
Neels Hofmeyr28d4be52018-03-16 03:44:07 +010082 help='''git push-URL. Default is to not configure a separate push-URL.''')
83
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020084parser.add_argument('-o', '--output', dest='output', default='Makefile',
85 help='''Makefile filename (default: 'Makefile').''')
86
87parser.add_argument('-j', '--jobs', dest='jobs', default='9',
88 help='''-j option to pass to 'make'.''')
89
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +010090parser.add_argument('-I', '--sudo-make-install', dest='sudo_make_install',
91 action='store_true',
92 help='''run 'make install' step with 'sudo'.''')
93
94parser.add_argument('-L', '--no-ldconfig', dest='no_ldconfig',
95 action='store_true',
96 help='''omit the 'sudo ldconfig' step.''')
97
98parser.add_argument('--ldconfig-without-sudo', dest='ldconfig_without_sudo',
99 action='store_true',
100 help='''call just 'ldconfig', without sudo, which implies
101root privileges (not recommended)''')
102
Neels Hofmeyrf6078c42018-09-04 14:34:33 +0200103parser.add_argument('-c', '--no-make-check', dest='make_check',
104 default=True, action='store_false',
105 help='''do not 'make check', just 'make' to build.''')
106
Oliver Smithc47eafb2021-08-12 12:45:36 +0200107parser.add_argument('--docker-cmd',
108 help='''prefix configure/make/make install calls with this command (used by ttcn3.sh)''')
109
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200110args = parser.parse_args()
111
Neels Hofmeyr450dac72017-08-22 19:27:08 +0200112class listdict(dict):
113 'a dict of lists { "a": [1, 2, 3], "b": [1, 2] }'
114
115 def add(self, name, item):
116 l = self.get(name)
117 if not l:
118 l = []
119 self[name] = l
120 l.append(item)
121
122 def extend(self, name, l):
123 for v in l:
124 self.add(name, v)
125
126 def add_dict(self, d):
127 for k,v in d.items():
128 self.add(k, v)
129
130 def extend_dict(self, d):
131 for k,v in d.items():
132 l = self.extend(k, v)
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200133
134def read_projects_deps(path):
135 'Read deps config and return tuples of (project_name, which-other-to-build-first).'
136 l = []
137 for line in open(path):
138 line = line.strip()
139 if not line or line.startswith('#'):
140 continue
141 tokens = line.split()
142 l.append((tokens[0], tokens[1:]))
143 return l
144
145def read_configure_opts(path):
146 'Read config opts file and return tuples of (project_name, config-opts).'
147 if not path:
148 return {}
149 return dict(read_projects_deps(path))
150
Neels Hofmeyrf6078c42018-09-04 14:34:33 +0200151def gen_make(proj, deps, configure_opts, jobs, make_dir, src_dir, build_dir, url, push_url, sudo_make_install, no_ldconfig, ldconfig_without_sudo, make_check):
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200152 src_proj = os.path.join(src_dir, proj)
Neels Hofmeyr29fde6f2018-04-01 15:57:02 +0200153 if proj == 'openbsc':
154 src_proj = os.path.join(src_proj, 'openbsc')
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200155 build_proj = os.path.join(build_dir, proj)
156
157 make_to_src = os.path.relpath(src_dir, make_dir)
158 make_to_src_proj = os.path.relpath(src_proj, make_dir)
159 make_to_build_proj = os.path.relpath(build_proj, make_dir)
160 build_to_src = os.path.relpath(src_proj, build_proj)
161
162 if configure_opts:
163 configure_opts_str = ' '.join(configure_opts)
164 else:
165 configure_opts_str = ''
166
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200167 return r'''
168### {proj} ###
169
Oliver Smith505f60d2021-10-04 12:55:44 +0200170{proj}_configure_files := $(shell find {src_proj} \
171 -name "Makefile.am" \
172 -or -name "*.in" \
173 -and -not -name "Makefile.in" \
174 -and -not -name "config.h.in" )
175{proj}_files := $(shell find {src_proj} \
176 -name "*.[hc]" \
177 -or -name "*.py" \
178 -or -name "*.cpp" \
179 -or -name "*.tpl" \
180 -or -name "*.map")
Neels Hofmeyr1c1e4d22017-09-11 01:32:50 +0200181
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200182.make.{proj}.clone:
Vadim Yanitskiyd9cdec62020-06-01 00:52:12 +0700183 @echo -e "\n\n\n===== $@\n"
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200184 test -d {src} || mkdir -p {src}
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100185 test -d {src_proj} || ( git -C {src} clone "{url}/{proj}" "{proj}" && git -C "{src}/{proj}" remote set-url --push origin "{push_url}/{proj}" )
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200186 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200187 touch $@
188
189.make.{proj}.autoconf: .make.{proj}.clone {src_proj}/configure.ac
Vadim Yanitskiyd9cdec62020-06-01 00:52:12 +0700190 @echo -e "\n\n\n===== $@\n"
Neels Hofmeyr1fa9b292018-04-23 17:04:25 +0200191 -rm -f {src_proj}/.version
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200192 cd {src_proj}; autoreconf -fi
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200193 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200194 touch $@
195
Neels Hofmeyr367cb972017-12-15 04:01:13 +0100196.make.{proj}.configure: .make.{proj}.autoconf {deps_installed} $({proj}_configure_files)
Vadim Yanitskiyd9cdec62020-06-01 00:52:12 +0700197 @echo -e "\n\n\n===== $@\n"
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200198 -chmod -R ug+w {build_proj}
199 -rm -rf {build_proj}
200 mkdir -p {build_proj}
Oliver Smithc47eafb2021-08-12 12:45:36 +0200201 cd {build_proj}; {docker_cmd}{build_to_src}/configure {configure_opts}
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200202 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200203 touch $@
204
Neels Hofmeyr367cb972017-12-15 04:01:13 +0100205.make.{proj}.build: .make.{proj}.configure $({proj}_files)
Vadim Yanitskiyd9cdec62020-06-01 00:52:12 +0700206 @echo -e "\n\n\n===== $@\n"
Oliver Smithc47eafb2021-08-12 12:45:36 +0200207 {docker_cmd}$(MAKE) -C {build_proj} -j {jobs} {check}
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200208 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200209 touch $@
210
211.make.{proj}.install: .make.{proj}.build
Vadim Yanitskiyd9cdec62020-06-01 00:52:12 +0700212 @echo -e "\n\n\n===== $@\n"
Oliver Smithc47eafb2021-08-12 12:45:36 +0200213 {docker_cmd}{sudo_make_install}$(MAKE) -C {build_proj} install
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100214 {no_ldconfig}{sudo_ldconfig}ldconfig
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200215 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200216 touch $@
Neels Hofmeyr277f4792017-08-29 12:30:32 +0200217
Neels Hofmeyr6d16ae92018-09-04 14:36:15 +0200218.PHONY: {proj}
Neels Hofmeyr3f934122017-08-29 12:31:59 +0200219{proj}: .make.{proj}.install
220
Neels Hofmeyr4d38c1e2018-04-23 17:04:15 +0200221.PHONY: {proj}-reinstall
222{proj}-reinstall: {deps_reinstall}
223 {sudo_make_install}$(MAKE) -C {build_proj} install
224
Neels Hofmeyr277f4792017-08-29 12:30:32 +0200225.PHONY: {proj}-clean
226{proj}-clean:
Vadim Yanitskiyd9cdec62020-06-01 00:52:12 +0700227 @echo -e "\n\n\n===== $@\n"
Neels Hofmeyr277f4792017-08-29 12:30:32 +0200228 -chmod -R ug+w {build_proj}
229 -rm -rf {build_proj}
230 -rm -rf .make.{proj}.*
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200231'''.format(
232 url=url,
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100233 push_url=push_url or url,
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200234 proj=proj,
235 jobs=jobs,
236 src=make_to_src,
237 src_proj=make_to_src_proj,
238 build_proj=make_to_build_proj,
239 build_to_src=build_to_src,
240 deps_installed=' '.join(['.make.%s.install' % d for d in deps]),
Neels Hofmeyr4d38c1e2018-04-23 17:04:15 +0200241 deps_reinstall=' '.join(['%s-reinstall' %d for d in deps]),
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100242 configure_opts=configure_opts_str,
243 sudo_make_install='sudo ' if sudo_make_install else '',
244 no_ldconfig='#' if no_ldconfig else '',
Neels Hofmeyrf6078c42018-09-04 14:34:33 +0200245 sudo_ldconfig='' if ldconfig_without_sudo else 'sudo ',
246 check='check' if make_check else '',
Oliver Smithc47eafb2021-08-12 12:45:36 +0200247 docker_cmd=f'{args.docker_cmd} ' if args.docker_cmd else '',
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100248 )
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200249
250
Oliver Smith0a4d8ea2021-07-29 14:32:29 +0200251projects_deps = read_projects_deps(all_deps_file)
Neels Hofmeyr450dac72017-08-22 19:27:08 +0200252configure_opts = listdict()
253configure_opts_files = sorted(args.configure_opts_files or [])
254for configure_opts_file in configure_opts_files:
Oliver Smith0a4d8ea2021-07-29 14:32:29 +0200255 if configure_opts_file.endswith(".deps"):
256 print(f"WARNING: using {all_deps_file} instead of {configure_opts_file}")
257 continue
Neels Hofmeyr450dac72017-08-22 19:27:08 +0200258 r = read_configure_opts(configure_opts_file)
259 configure_opts.extend_dict(read_configure_opts(configure_opts_file))
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200260
261make_dir = args.make_dir
262if not make_dir:
Neels Hofmeyr450dac72017-08-22 19:27:08 +0200263 opts_names = '+'.join([f.replace('.opts', '') for f in configure_opts_files])
Oliver Smith0a4d8ea2021-07-29 14:32:29 +0200264 make_dir = 'make-%s' % opts_names
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200265
266if not os.path.isdir(make_dir):
267 os.makedirs(make_dir)
268
269build_dir = args.build_dir
270if not build_dir:
271 build_dir = make_dir
272
273output = os.path.join(make_dir, args.output)
274print('Writing to %r' % output)
275
276with open(output, 'w') as out:
277 out.write('# This Makefile was generated by %s\n' % os.path.basename(sys.argv[0]))
278
Oliver Smith0a4d8ea2021-07-29 14:32:29 +0200279 configure_opts_args = ""
280 for f in configure_opts_files:
281 if not f.endswith(".deps"):
282 configure_opts_args += f' \\\n\t\t{os.path.relpath(f, make_dir)}'
283
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200284 # convenience: add a regen target that updates the generated makefile itself
285 out.write(r'''
Oliver Smith0a4d8ea2021-07-29 14:32:29 +0200286default: usrp
287
288cn: \
289 osmo-ggsn \
290 osmo-hlr \
291 osmo-iuh \
292 osmo-mgw \
293 osmo-msc \
294 osmo-sgsn \
295 osmo-sip-connector \
296 osmo-smlc \
297 $(NULL)
298
299cn-bsc: \
300 cn \
301 osmo-bsc \
302 $(NULL)
303
304usrp: \
305 cn-bsc \
306 osmo-bts \
307 osmo-trx \
308 $(NULL)
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200309
Neels Hofmeyr1b0d34f2018-03-16 03:46:08 +0100310.PHONY: all_debug
311all_debug:
312 $(MAKE) --dry-run -d all | grep "is newer than target"
313 $(MAKE) all
314
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200315# regenerate this Makefile, in case the deps or opts changed
316.PHONY: regen
317regen:
Oliver Smithb3ae4b62021-01-28 11:20:04 +0100318 {script} \
Oliver Smithb3ae4b62021-01-28 11:20:04 +0100319 {configure_opts} \
320 -m {make_dir} \
321 -o {makefile} \
322 -s {src_dir} \
323 -b {build_dir} \
Oliver Smithc47eafb2021-08-12 12:45:36 +0200324 -u "{url}"{push_url}{sudo_make_install}{no_ldconfig}{ldconfig_without_sudo}{make_check}{docker_cmd}
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200325
326'''.format(
327 script=os.path.relpath(sys.argv[0], make_dir),
Oliver Smith0a4d8ea2021-07-29 14:32:29 +0200328 configure_opts=configure_opts_args,
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200329 make_dir='.',
330 makefile=args.output,
331 src_dir=os.path.relpath(args.src_dir, make_dir),
332 build_dir=os.path.relpath(build_dir, make_dir),
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100333 url=args.url,
Oliver Smithb3ae4b62021-01-28 11:20:04 +0100334 push_url=(" \\\n\t\t-p '%s'"%args.push_url) if args.push_url else '',
335 sudo_make_install=' \\\n\t\t-I' if args.sudo_make_install else '',
336 no_ldconfig=' \\\n\t\t-L' if args.no_ldconfig else '',
337 ldconfig_without_sudo=' \\\n\t\t--ldconfig-without-sudo' if args.ldconfig_without_sudo else '',
338 make_check='' if args.make_check else " \\\n\t\t--no-make-check",
Oliver Smithc47eafb2021-08-12 12:45:36 +0200339 docker_cmd=f' \\\n\t\t--docker-cmd "{args.docker_cmd}"' if args.docker_cmd else ''
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200340 ))
341
Neels Hofmeyre274d352017-08-22 17:31:03 +0200342 # convenience target: clone all repositories first
343 out.write('clone: \\\n\t' + ' \\\n\t'.join([ '.make.%s.clone' % p for p, d in projects_deps ]) + '\n\n')
344
Neels Hofmeyr277f4792017-08-29 12:30:32 +0200345 # convenience target: clean all
346 out.write('clean: \\\n\t' + ' \\\n\t'.join([ '%s-clean' % p for p, d in projects_deps ]) + '\n\n')
347
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200348 # now the actual useful build rules
Neels Hofmeyr1c1e4d22017-09-11 01:32:50 +0200349 out.write('all: clone all-install\n\n')
350
351 out.write('all-install: \\\n\t' + ' \\\n\t'.join([ '.make.%s.install' % p for p, d in projects_deps ]) + '\n\n')
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200352
353 for proj, deps in projects_deps:
Neels Hofmeyra007eaa2018-09-04 14:37:13 +0200354 all_config_opts = []
355 all_config_opts.extend(configure_opts.get('ALL') or [])
356 all_config_opts.extend(configure_opts.get(proj) or [])
357 out.write(gen_make(proj, deps, all_config_opts, args.jobs,
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100358 make_dir, args.src_dir, build_dir, args.url, args.push_url,
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100359 args.sudo_make_install, args.no_ldconfig,
Neels Hofmeyrf6078c42018-09-04 14:34:33 +0200360 args.ldconfig_without_sudo, args.make_check))
Neels Hofmeyrd4d88482017-08-22 19:27:28 +0200361
362# vim: expandtab tabstop=2 shiftwidth=2