From 0a1bdfff8fb2f2ec25b1d14dd0d6cee256d99ecc Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Sun, 13 Aug 2017 03:22:42 +0200 Subject: [PATCH] initial gen_makefile.py with config --- 3G+2G.deps | 14 ++++ README | 38 +++++++++ all_enabled.opts | 4 + gen_makefile.py | 202 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 258 insertions(+) create mode 100644 3G+2G.deps create mode 100644 README create mode 100644 all_enabled.opts create mode 100755 gen_makefile.py diff --git a/3G+2G.deps b/3G+2G.deps new file mode 100644 index 0000000..b905d4b --- /dev/null +++ b/3G+2G.deps @@ -0,0 +1,14 @@ +# project build these first +libosmocore +libosmo-abis libosmocore +libosmo-netif libosmo-abis +libosmo-sccp libosmo-netif +libsmpp34 +libasn1c +openggsn libosmocore +osmo-iuh libosmo-sccp libasn1c +osmo-hlr libosmo-abis +osmo-mgw libosmo-netif +osmo-msc osmo-iuh osmo-mgw +osmo-bsc libosmo-sccp osmo-mgw +osmo-sgsn osmo-iuh diff --git a/README b/README new file mode 100644 index 0000000..9efce66 --- /dev/null +++ b/README @@ -0,0 +1,38 @@ +This provides a set of top-level makefiles to build variants of the Osmocom +source trees. It is inteded for the core network components and related +projects, but works generically. + +The idea is to have all your Osmocom git clones in ./src, while keeping one or +more separate build trees in ./make-*. + +Run ./gen_makefile.py with a choice of projects (2G only or also 3G?) +and a choice of configure options, for example: + + ./gen_makefile.py 3G+2G.deps all_enabled.opts + +This generates a new dir containing a Makefile. When you run make in it, this +will clone the source trees (if not present yet) and build all of them in the +right order: + + cd make-3G+2G.deps-all_enabled.opts/ + make + +If you make modifications in one of the source trees, this Makefile will pick +it up, rebuild the project and also rebuild all dependencies (according to the +*.deps file the Makefile was generated from). + +If you modify the *.deps or *.opts file, you can easily run 'make regen' in a +make-* subdir to regenerate the Makefile from the same files. + +In your make-* subdir there are empty status files that are touched for every +completed make target. From these, 'make' can detect what needs to be rebuilt. +You can manually remove them to force a rebuild of a specific target. + +For example, if you 'rm .make.libosmocore.autoconf', libosmocore and all +projects depending on libosmocore will be rebuilt from scratch. + +For more details on the *.opts and *.deps syntax, read the docs at the top of +./gen_makefile.py. + +It is also easily possible to keep sources and build trees in various +configurations, see the command line options of ./gen_makefile.py. diff --git a/all_enabled.opts b/all_enabled.opts new file mode 100644 index 0000000..ad7fa6a --- /dev/null +++ b/all_enabled.opts @@ -0,0 +1,4 @@ +osmo-mgw --enable-mgcp-transcoding +osmo-msc --enable-smpp --enable-iu --enable-mgcp-transcoding --enable-external-tests +osmo-bsc --enable-osmo-bsc --enable-nat --enable-mgcp-transcoding --enable-external-tests +osmo-sgsn --enable-iu --enable-external-tests diff --git a/gen_makefile.py b/gen_makefile.py new file mode 100755 index 0000000..a68bc00 --- /dev/null +++ b/gen_makefile.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +''' +Generate a top-level makefile that builds the Osmocom 2G + 3G network components. + + ./gen_makefile.py projects.deps [configuration.opts] [-o Makefile.output] + +Configured by text files: + + *.deps: whitespace-separated listing of + project_name depends_on_project_1 depends_on_project_2 ... + + *.opts: whitespace-separated listing of + project_name --config-opt-1 --config-opt-2 ... + +Thus it is possible to choose between e.g. +- 2G+3G or 2G-only by picking a different projects_and_deps.conf, +- and between building each of those with or without mgcp transcoding support + by picking a different configure_opts.conf. + +From the Makefile nature, the dependencies extend, no need to repeat common deps. + +When this script is done, a Makefile has been generated that allows you to +build all projects at once by issuing 'make', but also to refresh only parts of +it when some bits in the middle have changed. The makefile keeps local progress +marker files like .make.libosmocore.configure; if such progress marker is +removed or becomes outdated, that step and all dependent ones are re-run. +This is helpful in daily hacking across several repositories. +''' + +import sys +import os +import argparse + +parser = argparse.ArgumentParser(epilog=__doc__, formatter_class=argparse.RawTextHelpFormatter) + +parser.add_argument('projects_and_deps_file', + help='''Config file containing projects to build and +dependencies between those''') + +parser.add_argument('configure_opts_file', + help='''Config file containing project name and +./configure options''', + default=None, nargs='?') + +parser.add_argument('-m', '--make-dir', dest='make_dir', + help='''Place Makefile in this dir (default: create +a new dir named after deps and opts files).''') + +parser.add_argument('-s', '--src-dir', dest='src_dir', default='./src', + help='Parent dir for all git clones.') + +parser.add_argument('-b', '--build-dir', dest='build_dir', + help='''Parent dir for all build trees (default: +directly in the make-dir).''') + +parser.add_argument('-u', '--url', dest='url', default='ssh://go', + help='''git clone base URL. Default is 'ssh://go', +e.g. add this to your ~/.ssh/config: + host go + hostname gerrit.osmocom.org + port 29418 +Alternatively pass '-u git://git.osmocom.org'.''') + +parser.add_argument('-o', '--output', dest='output', default='Makefile', + help='''Makefile filename (default: 'Makefile').''') + +parser.add_argument('-j', '--jobs', dest='jobs', default='9', + help='''-j option to pass to 'make'.''') + +args = parser.parse_args() + + +def read_projects_deps(path): + 'Read deps config and return tuples of (project_name, which-other-to-build-first).' + l = [] + for line in open(path): + line = line.strip() + if not line or line.startswith('#'): + continue + tokens = line.split() + l.append((tokens[0], tokens[1:])) + return l + +def read_configure_opts(path): + 'Read config opts file and return tuples of (project_name, config-opts).' + if not path: + return {} + return dict(read_projects_deps(path)) + +def gen_make(proj, deps, configure_opts, jobs, make_dir, src_dir, build_dir, url): + src_proj = os.path.join(src_dir, proj) + build_proj = os.path.join(build_dir, proj) + + make_to_src = os.path.relpath(src_dir, make_dir) + make_to_src_proj = os.path.relpath(src_proj, make_dir) + make_to_build_proj = os.path.relpath(build_proj, make_dir) + build_to_src = os.path.relpath(src_proj, build_proj) + + if configure_opts: + configure_opts_str = ' '.join(configure_opts) + else: + configure_opts_str = '' + + # special hack for libsmpp34: cannot build in parallel + if proj == 'libsmpp34': + jobs = 1 + + return r''' +### {proj} ### + +.make.{proj}.clone: + @echo "\n\n\n===== $@\n" + test -d {src} || mkdir -p {src} + test -d {src_proj} || git -C {src} clone {url}/{proj} + touch $@ + +.make.{proj}.autoconf: .make.{proj}.clone {src_proj}/configure.ac + @echo "\n\n\n===== $@\n" + cd {src_proj}; autoreconf -fi + touch $@ + +.make.{proj}.configure: .make.{proj}.autoconf {deps_installed} + @echo "\n\n\n===== $@\n" + -chmod -R ug+w {build_proj} + -rm -rf {build_proj} + mkdir -p {build_proj} + cd {build_proj}; {build_to_src}/configure {configure_opts} + touch $@ + +.make.{proj}.last_edited: + touch $@ + +.PHONY: .make.{proj}.detect_edits +.make.{proj}.detect_edits: + @test -z "$(shell find {src_proj} -newer .make.{proj}.last_edited -name "*.[hc]")" || (touch .make.{proj}.last_edited; echo {proj} edited) + +.make.{proj}.build: .make.{proj}.configure .make.{proj}.last_edited + @echo "\n\n\n===== $@\n" + $(MAKE) -C {build_proj} -j {jobs} check + touch $@ + +.make.{proj}.install: .make.{proj}.build + @echo "\n\n\n===== $@\n" + $(MAKE) -C {build_proj} install + touch $@ +'''.format( + url=url, + proj=proj, + jobs=jobs, + src=make_to_src, + src_proj=make_to_src_proj, + build_proj=make_to_build_proj, + build_to_src=build_to_src, + deps_installed=' '.join(['.make.%s.install' % d for d in deps]), + configure_opts=configure_opts_str) + + +projects_deps = read_projects_deps(args.projects_and_deps_file) +configure_opts = read_configure_opts(args.configure_opts_file) + +make_dir = args.make_dir +if not make_dir: + make_dir = 'make-%s-%s' % (args.projects_and_deps_file, args.configure_opts_file) + +if not os.path.isdir(make_dir): + os.makedirs(make_dir) + +build_dir = args.build_dir +if not build_dir: + build_dir = make_dir + +output = os.path.join(make_dir, args.output) +print('Writing to %r' % output) + +with open(output, 'w') as out: + out.write('# This Makefile was generated by %s\n' % os.path.basename(sys.argv[0])) + + # convenience: add a regen target that updates the generated makefile itself + out.write(r''' +default: all + +# regenerate this Makefile, in case the deps or opts changed +.PHONY: regen +regen: + {script} {projects_and_deps} {configure_opts} -m {make_dir} -o {makefile} -s {src_dir} -b {build_dir} + +'''.format( + script=os.path.relpath(sys.argv[0], make_dir), + projects_and_deps=os.path.relpath(args.projects_and_deps_file, make_dir), + configure_opts=os.path.relpath(args.configure_opts_file, make_dir), + make_dir='.', + makefile=args.output, + src_dir=os.path.relpath(args.src_dir, make_dir), + build_dir=os.path.relpath(build_dir, make_dir), + )) + + # now the actual useful build rules + out.write('all: \\\n\t' + ' \\\n\t'.join([ '.make.%s.detect_edits .make.%s.install' % (p,p) for p, d in projects_deps ]) + '\n\n') + + for proj, deps in projects_deps: + out.write(gen_make(proj, deps, configure_opts.get(proj), args.jobs, + make_dir, args.src_dir, build_dir, args.url))