blob: 62e08e8670715fb82eadd94df3c401d9b5eb0710 [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
Neels Hofmeyr450dac72017-08-22 19:27:08 +02005 ./gen_makefile.py projects.deps [configure.opts [more.opts]] [-o Makefile.output]
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +02006
7Configured by text files:
8
9 *.deps: whitespace-separated listing of
10 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.
16- 2G+3G or 2G-only by picking a different projects_and_deps.conf,
17- and between building each of those with or without mgcp transcoding support
18 by picking a different configure_opts.conf.
19
20From the Makefile nature, the dependencies extend, no need to repeat common deps.
21
22When this script is done, a Makefile has been generated that allows you to
23build all projects at once by issuing 'make', but also to refresh only parts of
24it when some bits in the middle have changed. The makefile keeps local progress
25marker files like .make.libosmocore.configure; if such progress marker is
26removed or becomes outdated, that step and all dependent ones are re-run.
27This is helpful in daily hacking across several repositories.
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +010028
29Note that by default, this includes 'sudo ldconfig' calls following each
30installation. You may want to permit your user to run 'sudo ldconfig' without
31needing a password, e.g. by
32
33 sudo sh -c "echo '$USER ALL= NOPASSWD: /sbin/ldconfig' > /etc/sudoers.d/${USER}_ldconfig"
34
35You can skip the 'sudo ldconfig' by issuing the --no-ldconfig option.
36
37You can run 'ldconfig' without sudo by issuing the --ldconfig-without-sudo option.
38
39By default, it is assumed that your user has write permission to /usr/local. If you
40need sudo to install there, you may issue the --sudo-make-install option.
Neels Hofmeyr2535a262018-01-16 16:34:32 +010041
42EXAMPLE:
43
44 ./gen_makefile.py 3G+2G.deps default.opts iu.opts -I -m build
45 cd build
46 make
47
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020048'''
49
50import sys
51import os
52import argparse
53
54parser = argparse.ArgumentParser(epilog=__doc__, formatter_class=argparse.RawTextHelpFormatter)
55
56parser.add_argument('projects_and_deps_file',
57 help='''Config file containing projects to build and
58dependencies between those''')
59
Neels Hofmeyr450dac72017-08-22 19:27:08 +020060parser.add_argument('configure_opts_files',
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020061 help='''Config file containing project name and
62./configure options''',
Neels Hofmeyr450dac72017-08-22 19:27:08 +020063 nargs='*')
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020064
65parser.add_argument('-m', '--make-dir', dest='make_dir',
66 help='''Place Makefile in this dir (default: create
67a new dir named after deps and opts files).''')
68
69parser.add_argument('-s', '--src-dir', dest='src_dir', default='./src',
70 help='Parent dir for all git clones.')
71
72parser.add_argument('-b', '--build-dir', dest='build_dir',
73 help='''Parent dir for all build trees (default:
74directly in the make-dir).''')
75
Neels Hofmeyrbffdc302017-12-06 00:31:49 +010076parser.add_argument('-u', '--url', dest='url', default='git://git.osmocom.org',
77 help='''git clone base URL. Default is 'git://git.osmocom.org'.
78e.g. with a config like this in your ~/.ssh/config:
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020079 host go
80 hostname gerrit.osmocom.org
81 port 29418
Neels Hofmeyrbffdc302017-12-06 00:31:49 +010082you may pass '-u ssh://go' to be able to submit to gerrit.''')
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020083
Neels Hofmeyr972c2942018-03-16 03:49:58 +010084parser.add_argument('-p', '--push-url', dest='push_url', default='',
Neels Hofmeyr28d4be52018-03-16 03:44:07 +010085 help='''git push-URL. Default is to not configure a separate push-URL.''')
86
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +020087parser.add_argument('-o', '--output', dest='output', default='Makefile',
88 help='''Makefile filename (default: 'Makefile').''')
89
90parser.add_argument('-j', '--jobs', dest='jobs', default='9',
91 help='''-j option to pass to 'make'.''')
92
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +010093parser.add_argument('-I', '--sudo-make-install', dest='sudo_make_install',
94 action='store_true',
95 help='''run 'make install' step with 'sudo'.''')
96
97parser.add_argument('-L', '--no-ldconfig', dest='no_ldconfig',
98 action='store_true',
99 help='''omit the 'sudo ldconfig' step.''')
100
101parser.add_argument('--ldconfig-without-sudo', dest='ldconfig_without_sudo',
102 action='store_true',
103 help='''call just 'ldconfig', without sudo, which implies
104root privileges (not recommended)''')
105
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200106args = parser.parse_args()
107
Neels Hofmeyr450dac72017-08-22 19:27:08 +0200108class listdict(dict):
109 'a dict of lists { "a": [1, 2, 3], "b": [1, 2] }'
110
111 def add(self, name, item):
112 l = self.get(name)
113 if not l:
114 l = []
115 self[name] = l
116 l.append(item)
117
118 def extend(self, name, l):
119 for v in l:
120 self.add(name, v)
121
122 def add_dict(self, d):
123 for k,v in d.items():
124 self.add(k, v)
125
126 def extend_dict(self, d):
127 for k,v in d.items():
128 l = self.extend(k, v)
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200129
130def read_projects_deps(path):
131 'Read deps config and return tuples of (project_name, which-other-to-build-first).'
132 l = []
133 for line in open(path):
134 line = line.strip()
135 if not line or line.startswith('#'):
136 continue
137 tokens = line.split()
138 l.append((tokens[0], tokens[1:]))
139 return l
140
141def read_configure_opts(path):
142 'Read config opts file and return tuples of (project_name, config-opts).'
143 if not path:
144 return {}
145 return dict(read_projects_deps(path))
146
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100147def gen_make(proj, deps, configure_opts, jobs, make_dir, src_dir, build_dir, url, push_url, sudo_make_install, no_ldconfig, ldconfig_without_sudo):
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200148 src_proj = os.path.join(src_dir, proj)
Neels Hofmeyr29fde6f2018-04-01 15:57:02 +0200149 if proj == 'openbsc':
150 src_proj = os.path.join(src_proj, 'openbsc')
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200151 build_proj = os.path.join(build_dir, proj)
152
153 make_to_src = os.path.relpath(src_dir, make_dir)
154 make_to_src_proj = os.path.relpath(src_proj, make_dir)
155 make_to_build_proj = os.path.relpath(build_proj, make_dir)
156 build_to_src = os.path.relpath(src_proj, build_proj)
157
158 if configure_opts:
159 configure_opts_str = ' '.join(configure_opts)
160 else:
161 configure_opts_str = ''
162
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200163 return r'''
164### {proj} ###
165
Neels Hofmeyr367cb972017-12-15 04:01:13 +0100166{proj}_configure_files := $(shell find {src_proj} -name "Makefile.am" -or -name "*.in" )
167{proj}_files := $(shell find {src_proj} -name "*.[hc]" -or -name "*.py" )
Neels Hofmeyr1c1e4d22017-09-11 01:32:50 +0200168
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200169.make.{proj}.clone:
170 @echo "\n\n\n===== $@\n"
171 test -d {src} || mkdir -p {src}
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100172 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 +0200173 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200174 touch $@
175
176.make.{proj}.autoconf: .make.{proj}.clone {src_proj}/configure.ac
177 @echo "\n\n\n===== $@\n"
Neels Hofmeyr1fa9b292018-04-23 17:04:25 +0200178 -rm -f {src_proj}/.version
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200179 cd {src_proj}; autoreconf -fi
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200180 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200181 touch $@
182
Neels Hofmeyr367cb972017-12-15 04:01:13 +0100183.make.{proj}.configure: .make.{proj}.autoconf {deps_installed} $({proj}_configure_files)
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200184 @echo "\n\n\n===== $@\n"
185 -chmod -R ug+w {build_proj}
186 -rm -rf {build_proj}
187 mkdir -p {build_proj}
188 cd {build_proj}; {build_to_src}/configure {configure_opts}
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200189 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200190 touch $@
191
Neels Hofmeyr367cb972017-12-15 04:01:13 +0100192.make.{proj}.build: .make.{proj}.configure $({proj}_files)
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200193 @echo "\n\n\n===== $@\n"
194 $(MAKE) -C {build_proj} -j {jobs} check
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200195 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200196 touch $@
197
198.make.{proj}.install: .make.{proj}.build
199 @echo "\n\n\n===== $@\n"
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100200 {sudo_make_install}$(MAKE) -C {build_proj} install
201 {no_ldconfig}{sudo_ldconfig}ldconfig
Neels Hofmeyr7bd5c312018-07-26 16:43:01 +0200202 sync
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200203 touch $@
Neels Hofmeyr277f4792017-08-29 12:30:32 +0200204
Neels Hofmeyr3f934122017-08-29 12:31:59 +0200205{proj}: .make.{proj}.install
206
Neels Hofmeyr4d38c1e2018-04-23 17:04:15 +0200207.PHONY: {proj}-reinstall
208{proj}-reinstall: {deps_reinstall}
209 {sudo_make_install}$(MAKE) -C {build_proj} install
210
Neels Hofmeyr277f4792017-08-29 12:30:32 +0200211.PHONY: {proj}-clean
212{proj}-clean:
213 @echo "\n\n\n===== $@\n"
214 -chmod -R ug+w {build_proj}
215 -rm -rf {build_proj}
216 -rm -rf .make.{proj}.*
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200217'''.format(
218 url=url,
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100219 push_url=push_url or url,
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200220 proj=proj,
221 jobs=jobs,
222 src=make_to_src,
223 src_proj=make_to_src_proj,
224 build_proj=make_to_build_proj,
225 build_to_src=build_to_src,
226 deps_installed=' '.join(['.make.%s.install' % d for d in deps]),
Neels Hofmeyr4d38c1e2018-04-23 17:04:15 +0200227 deps_reinstall=' '.join(['%s-reinstall' %d for d in deps]),
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100228 configure_opts=configure_opts_str,
229 sudo_make_install='sudo ' if sudo_make_install else '',
230 no_ldconfig='#' if no_ldconfig else '',
231 sudo_ldconfig='' if ldconfig_without_sudo else 'sudo '
232 )
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200233
234
235projects_deps = read_projects_deps(args.projects_and_deps_file)
Neels Hofmeyr450dac72017-08-22 19:27:08 +0200236configure_opts = listdict()
237configure_opts_files = sorted(args.configure_opts_files or [])
238for configure_opts_file in configure_opts_files:
239 r = read_configure_opts(configure_opts_file)
240 configure_opts.extend_dict(read_configure_opts(configure_opts_file))
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200241
242make_dir = args.make_dir
243if not make_dir:
Neels Hofmeyr450dac72017-08-22 19:27:08 +0200244 deps_name = args.projects_and_deps_file.replace('.deps', '')
245 opts_names = '+'.join([f.replace('.opts', '') for f in configure_opts_files])
246 make_dir = 'make-%s-%s' % (deps_name, opts_names)
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200247
248if not os.path.isdir(make_dir):
249 os.makedirs(make_dir)
250
251build_dir = args.build_dir
252if not build_dir:
253 build_dir = make_dir
254
255output = os.path.join(make_dir, args.output)
256print('Writing to %r' % output)
257
258with open(output, 'w') as out:
259 out.write('# This Makefile was generated by %s\n' % os.path.basename(sys.argv[0]))
260
261 # convenience: add a regen target that updates the generated makefile itself
262 out.write(r'''
263default: all
264
Neels Hofmeyr1b0d34f2018-03-16 03:46:08 +0100265.PHONY: all_debug
266all_debug:
267 $(MAKE) --dry-run -d all | grep "is newer than target"
268 $(MAKE) all
269
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200270# regenerate this Makefile, in case the deps or opts changed
271.PHONY: regen
272regen:
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100273 {script} {projects_and_deps} {configure_opts} -m {make_dir} -o {makefile} -s {src_dir} -b {build_dir} -u "{url}" -p "{push_url}"{sudo_make_install}{no_ldconfig}{ldconfig_without_sudo}
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200274
275'''.format(
276 script=os.path.relpath(sys.argv[0], make_dir),
277 projects_and_deps=os.path.relpath(args.projects_and_deps_file, make_dir),
Neels Hofmeyr450dac72017-08-22 19:27:08 +0200278 configure_opts=' '.join([os.path.relpath(f, make_dir) for f in configure_opts_files]),
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200279 make_dir='.',
280 makefile=args.output,
281 src_dir=os.path.relpath(args.src_dir, make_dir),
282 build_dir=os.path.relpath(build_dir, make_dir),
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100283 url=args.url,
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100284 push_url=args.push_url,
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100285 sudo_make_install=' -I' if args.sudo_make_install else '',
286 no_ldconfig=' -L' if args.no_ldconfig else '',
287 ldconfig_without_sudo=' --ldconfig-without-sudo' if args.ldconfig_without_sudo else ''
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200288 ))
289
Neels Hofmeyre274d352017-08-22 17:31:03 +0200290 # convenience target: clone all repositories first
291 out.write('clone: \\\n\t' + ' \\\n\t'.join([ '.make.%s.clone' % p for p, d in projects_deps ]) + '\n\n')
292
Neels Hofmeyr277f4792017-08-29 12:30:32 +0200293 # convenience target: clean all
294 out.write('clean: \\\n\t' + ' \\\n\t'.join([ '%s-clean' % p for p, d in projects_deps ]) + '\n\n')
295
Neels Hofmeyr0a1bdff2017-08-13 03:22:42 +0200296 # now the actual useful build rules
Neels Hofmeyr1c1e4d22017-09-11 01:32:50 +0200297 out.write('all: clone all-install\n\n')
298
299 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 +0200300
301 for proj, deps in projects_deps:
302 out.write(gen_make(proj, deps, configure_opts.get(proj), args.jobs,
Neels Hofmeyr28d4be52018-03-16 03:44:07 +0100303 make_dir, args.src_dir, build_dir, args.url, args.push_url,
Neels Hofmeyr461c3bd2017-12-06 00:32:15 +0100304 args.sudo_make_install, args.no_ldconfig,
305 args.ldconfig_without_sudo))
Neels Hofmeyrd4d88482017-08-22 19:27:28 +0200306
307# vim: expandtab tabstop=2 shiftwidth=2