| #!/usr/bin/env python |
| # |
| # 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 |
| try: |
| import configparser |
| except ImportError: |
| import ConfigParser as configparser |
| import codecs |
| import getopt |
| import string |
| import glob |
| import re |
| |
| #import ezt |
| |
| # |
| # 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'), |
| ] |
| # note: MAKE_PLATFORMS is an ordered set. we want to generate unix symbols |
| # first, so that the later platforms can reference them. |
| |
| |
| def main(): |
| parser = configparser.ConfigParser() |
| parser.read('build.conf') |
| |
| if parser.has_option('options', 'dsp'): |
| dsp_file = parser.get('options', 'dsp') |
| else: |
| dsp_file = None |
| |
| headers = get_files(parser.get('options', 'headers')) |
| |
| # compute the relevant headers, along with the implied includes |
| legal_deps = { } |
| for fname in headers: |
| legal_deps[os.path.basename(fname)] = fname |
| |
| h_deps = { } |
| for fname in headers: |
| h_deps[os.path.basename(fname)] = extract_deps(fname, legal_deps) |
| resolve_deps(h_deps) |
| |
| f = open('build-outputs.mk', 'w') |
| 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 each platform and each subdirectory holding platform-specific files, |
| # write out their compilation rules, and an OBJECT_<subdir>_<plat> symbol. |
| for platform, parent in MAKE_PLATFORMS: |
| |
| # record the object symbols to build for each platform |
| group = [ '$(OBJECTS_all)' ] |
| |
| # If we're doing win32, we're going to look in the libapr.dsp file |
| # for those files that we have to manually add to our list. |
| inherit_parent = { } |
| if platform == 'win32' and dsp_file: |
| for line in open(dsp_file).readlines(): |
| if line[:7] != 'SOURCE=': |
| continue |
| if line[7:].find('unix') != -1: |
| # skip the leading .\ and split it out |
| inherit_files = line[9:].strip().split('\\') |
| # change the .c to .lo |
| assert inherit_files[-1][-2:] == '.c' |
| inherit_files[-1] = inherit_files[-1][:-2] + '.lo' |
| # replace the \\'s with /'s |
| inherit_line = '/'.join(inherit_files) |
| if inherit_files[0] not in inherit_parent: |
| inherit_parent[inherit_files[0]] = [] |
| inherit_parent[inherit_files[0]].append(inherit_line) |
| |
| for subdir in parser.get('options', 'platform_dirs').split(): |
| path = '%s/%s' % (subdir, platform) |
| if not os.path.exists(path): |
| # this subdir doesn't have a subdir for this platform, so we'll |
| # use the parent-platform's set of symbols |
| if parent: |
| group.append('$(OBJECTS_%s_%s)' % (subdir, parent)) |
| continue |
| |
| # remember that this directory has files/objects |
| dirs[path] = None |
| |
| # write out the compilation lines for this subdir |
| files = get_files(path + '/*.c') |
| objects, _unused = write_objects(f, legal_deps, h_deps, files) |
| |
| if subdir in inherit_parent: |
| objects = objects + inherit_parent[subdir] |
| |
| symname = 'OBJECTS_%s_%s' % (subdir, platform) |
| |
| objects.sort() |
| |
| # and write the symbol for the whole group |
| f.write('\n%s = %s\n\n' % (symname, " ".join(objects))) |
| |
| # and include that symbol in the group |
| group.append('$(%s)' % symname) |
| |
| group.sort() |
| |
| # write out a symbol which contains the necessary files |
| 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, _unused = 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') |
| |
| if parser.has_option('options', 'libraries'): |
| libs = parser.get('options', 'libraries') |
| |
| for lib in libs.split(): |
| files = get_files(parser.get(lib, 'paths')) |
| objects, _unused = write_objects(f, legal_deps, h_deps, files) |
| flat_objects = " ".join(objects) |
| f.write('OBJECTS_%s = %s\n' % (lib, flat_objects)) |
| |
| if parser.has_option(lib, 'target'): |
| target = parser.get(lib, 'target') |
| f.write('MODULE_%s = %s\n' % (lib, target)) |
| f.write('%s: $(OBJECTS_%s)\n' % (target, lib)) |
| f.write('\t$(LINK_LIBRARY) -o $@ $(OBJECTS_%s) $(LDADD_%s)\n' % (lib, lib)) |
| |
| f.write('\n') |
| |
| # Build a list of all necessary directories in build tree |
| alldirs = { } |
| for dir in dirs.keys(): |
| d = dir |
| while d: |
| alldirs[d] = None |
| d = os.path.dirname(d) |
| |
| # Sort so 'foo' is before 'foo/bar' |
| 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[-10:] == '/apr_app.c': |
| continue |
| assert file[-2:] == '.c' |
| obj = file[:-2] + '.lo' |
| objects.append(obj) |
| |
| dirs[os.path.dirname(file)] = None |
| |
| # what headers does this file include, along with the implied headers |
| 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): |
| "Extract the headers this file includes." |
| deps = { } |
| for line in codecs.open(fname, 'r', 'utf-8').readlines(): |
| if line[:8] != '#include': |
| continue |
| inc = _re_include.match(line).group(1) |
| if inc in legal_deps.keys(): |
| deps[inc] = legal_deps[inc] |
| return deps |
| _re_include = re.compile('#include *["<](.*)[">]') |
| |
| |
| def resolve_deps(header_deps): |
| "Alter the provided dictionary to flatten includes-of-includes." |
| altered = 1 |
| while altered: |
| altered = 0 |
| for hdr, deps in header_deps.items(): |
| # print hdr, deps |
| start = len(deps) |
| for dep in list(deps.keys()): |
| deps.update(header_deps.get(dep, {})) |
| if len(deps) != start: |
| altered = 1 |
| |
| 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() |