Created
May 23, 2025 02:27
-
-
Save andresdanielmtz/2c2473a3f1034c5ec92a00b857a6ea39 to your computer and use it in GitHub Desktop.
Gen-Build (Python 3)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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