Skip to content

Instantly share code, notes, and snippets.

@andresdanielmtz
Created May 23, 2025 02:27
Show Gist options
  • Save andresdanielmtz/2c2473a3f1034c5ec92a00b857a6ea39 to your computer and use it in GitHub Desktop.
Save andresdanielmtz/2c2473a3f1034c5ec92a00b857a6ea39 to your computer and use it in GitHub Desktop.
Gen-Build (Python 3)
#!/usr/bin/env python3
#
# USAGE: gen-build.py TYPE
#
# where TYPE is one of: make, dsp, vcproj
#
# It reads build.conf from the current directory, and produces its output
# into the current directory.
#
import os
import configparser
import glob
import re
# legal platforms: aix, beos, netware, os2, os390, unix, win32
# 'make' users: aix, beos, os2, os390, unix, win32 (mingw)
PLATFORMS = ['aix', 'beos', 'netware', 'os2', 'os390', 'unix', 'win32']
MAKE_PLATFORMS = [
('unix', None),
('aix', 'unix'),
('beos', 'unix'),
('os2', 'unix'),
('os390', 'unix'),
('win32', 'unix'),
]
def main():
parser = configparser.ConfigParser()
parser.read('build.conf')
dsp_file = parser.get('options', 'dsp') if parser.has_option('options', 'dsp') else None
headers = get_files(parser.get('options', 'headers'))
# compute the relevant headers, along with the implied includes
legal_deps = {os.path.basename(fname): fname for fname in headers}
h_deps = {os.path.basename(fname): extract_deps(fname, legal_deps) for fname in headers}
resolve_deps(h_deps)
with open('build-outputs.mk', 'w') as f:
f.write('# DO NOT EDIT. AUTOMATICALLY GENERATED.\n\n')
# write out the platform-independent files
files = get_files(parser.get('options', 'paths'))
objects, dirs = write_objects(f, legal_deps, h_deps, files)
f.write('\nOBJECTS_all = %s\n\n' % ' '.join(objects))
for platform, parent in MAKE_PLATFORMS:
group = ['$(OBJECTS_all)']
inherit_parent = {}
if platform == 'win32' and dsp_file:
with open(dsp_file) as dsp:
for line in dsp:
if not line.startswith('SOURCE='):
continue
if 'unix' in line:
inherit_files = line[9:].strip().split('\\')
assert inherit_files[-1].endswith('.c')
inherit_files[-1] = inherit_files[-1][:-2] + '.lo'
inherit_line = '/'.join(inherit_files)
subdir = inherit_files[0]
inherit_parent.setdefault(subdir, []).append(inherit_line)
for subdir in parser.get('options', 'platform_dirs').split():
path = f'{subdir}/{platform}'
if not os.path.exists(path):
if parent:
group.append(f'$(OBJECTS_{subdir}_{parent})')
continue
dirs[path] = None
files = get_files(f'{path}/*.c')
objects, _ = write_objects(f, legal_deps, h_deps, files)
if subdir in inherit_parent:
objects += inherit_parent[subdir]
symname = f'OBJECTS_{subdir}_{platform}'
objects.sort()
f.write('\n%s = %s\n\n' % (symname, ' '.join(objects)))
group.append(f'$({symname})')
group.sort()
f.write('OBJECTS_%s = %s\n\n' % (platform, ' '.join(group)))
f.write('HEADERS = $(top_srcdir)/%s\n\n' % ' $(top_srcdir)/'.join(headers))
f.write('SOURCE_DIRS = %s $(EXTRA_SOURCE_DIRS)\n\n' % ' '.join(dirs.keys()))
if parser.has_option('options', 'modules'):
modules = parser.get('options', 'modules')
for mod in modules.split():
files = get_files(parser.get(mod, 'paths'))
objects, _ = write_objects(f, legal_deps, h_deps, files)
flat_objects = ' '.join(objects)
f.write('OBJECTS_%s = %s\n' % (mod, flat_objects))
if parser.has_option(mod, 'target'):
target = parser.get(mod, 'target')
f.write('MODULE_%s = %s\n' % (mod, target))
f.write('%s: %s\n' % (target, flat_objects))
f.write('\t$(LINK_MODULE) -o $@ $(OBJECTS_%s) $(LDADD_%s)\n' % (mod, mod))
f.write('\n')
# Build list of all necessary build dirs
alldirs = {}
for dir in dirs.keys():
d = dir
while d:
alldirs[d] = None
d = os.path.dirname(d)
keys = list(alldirs.keys())
keys.sort()
f.write('BUILD_DIRS = %s\n\n' % ' '.join(keys))
f.write('.make.dirs: $(srcdir)/build-outputs.mk\n' \
'\t@for d in $(BUILD_DIRS); do test -d $$d || mkdir $$d; done\n' \
'\t@echo timestamp > $@\n')
def write_objects(f, legal_deps, h_deps, files):
dirs = {}
objects = []
for file in files:
if file.endswith('/apr_app.c'):
continue
assert file.endswith('.c')
obj = file[:-2] + '.lo'
objects.append(obj)
dirs[os.path.dirname(file)] = None
deps = extract_deps(file, legal_deps)
for hdr in list(deps.keys()):
deps.update(h_deps.get(hdr, {}))
vals = list(deps.values())
vals.sort()
f.write('%s: %s .make.dirs %s\n' % (obj, file, ' '.join(vals)))
objects.sort()
return objects, dirs
def extract_deps(fname, legal_deps):
deps = {}
with open(fname) as f:
for line in f:
if not line.startswith('#include'):
continue
match = _re_include.match(line)
if match:
inc = match.group(1)
if inc in legal_deps:
deps[inc] = legal_deps[inc]
return deps
_re_include = re.compile(r'#include *["<](.*)[">]')
def resolve_deps(header_deps):
altered = True
while altered:
altered = False
for hdr, deps in header_deps.items():
start = len(deps)
for dep in list(deps.keys()):
deps.update(header_deps.get(dep, {}))
if len(deps) != start:
altered = True
def clean_path(path):
return path.replace("\\", "/")
def get_files(patterns):
files = []
for pat in patterns.split():
files.extend(map(clean_path, glob.glob(pat)))
files.sort()
return files
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment