1#!/usr/bin/env python 2# 3# USAGE: gen-build.py TYPE 4# 5# where TYPE is one of: make, dsp, vcproj 6# 7# It reads build.conf from the current directory, and produces its output 8# into the current directory. 9# 10 11 12import os 13import ConfigParser 14import getopt 15import string 16import glob 17import re 18 19#import ezt 20 21# 22# legal platforms: aix, beos, netware, os2, os390, unix, win32 23# 'make' users: aix, beos, os2, os390, unix, win32 (mingw) 24# 25PLATFORMS = [ 'aix', 'beos', 'netware', 'os2', 'os390', 'unix', 'win32' ] 26MAKE_PLATFORMS = [ 27 ('unix', None), 28 ('aix', 'unix'), 29 ('beos', 'unix'), 30 ('os2', 'unix'), 31 ('os390', 'unix'), 32 ('win32', 'unix'), 33 ] 34# note: MAKE_PLATFORMS is an ordered set. we want to generate unix symbols 35# first, so that the later platforms can reference them. 36 37 38def main(): 39 parser = ConfigParser.ConfigParser() 40 parser.read('build.conf') 41 42 if parser.has_option('options', 'dsp'): 43 dsp_file = parser.get('options', 'dsp') 44 else: 45 dsp_file = None 46 47 headers = get_files(parser.get('options', 'headers')) 48 49 # compute the relevant headers, along with the implied includes 50 legal_deps = { } 51 for fname in headers: 52 legal_deps[os.path.basename(fname)] = fname 53 54 h_deps = { } 55 for fname in headers: 56 h_deps[os.path.basename(fname)] = extract_deps(fname, legal_deps) 57 resolve_deps(h_deps) 58 59 f = open('build-outputs.mk', 'w') 60 f.write('# DO NOT EDIT. AUTOMATICALLY GENERATED.\n\n') 61 62 # write out the platform-independent files 63 files = get_files(parser.get('options', 'paths')) 64 objects, dirs = write_objects(f, legal_deps, h_deps, files) 65 f.write('\nOBJECTS_all = %s\n\n' % string.join(objects)) 66 67 # for each platform and each subdirectory holding platform-specific files, 68 # write out their compilation rules, and an OBJECT_<subdir>_<plat> symbol. 69 for platform, parent in MAKE_PLATFORMS: 70 71 # record the object symbols to build for each platform 72 group = [ '$(OBJECTS_all)' ] 73 74 # If we're doing win32, we're going to look in the libapr.dsp file 75 # for those files that we have to manually add to our list. 76 inherit_parent = { } 77 if platform == 'win32' and dsp_file: 78 for line in open(dsp_file).readlines(): 79 if line[:7] != 'SOURCE=': 80 continue 81 if line[7:].find('unix') != -1: 82 # skip the leading .\ and split it out 83 inherit_files = line[9:].strip().split('\\') 84 # change the .c to .lo 85 assert inherit_files[-1][-2:] == '.c' 86 inherit_files[-1] = inherit_files[-1][:-2] + '.lo' 87 # replace the \\'s with /'s 88 inherit_line = '/'.join(inherit_files) 89 if not inherit_parent.has_key(inherit_files[0]): 90 inherit_parent[inherit_files[0]] = [] 91 inherit_parent[inherit_files[0]].append(inherit_line) 92 93 for subdir in string.split(parser.get('options', 'platform_dirs')): 94 path = '%s/%s' % (subdir, platform) 95 if not os.path.exists(path): 96 # this subdir doesn't have a subdir for this platform, so we'll 97 # use the parent-platform's set of symbols 98 if parent: 99 group.append('$(OBJECTS_%s_%s)' % (subdir, parent)) 100 continue 101 102 # remember that this directory has files/objects 103 dirs[path] = None 104 105 # write out the compilation lines for this subdir 106 files = get_files(path + '/*.c') 107 objects, _unused = write_objects(f, legal_deps, h_deps, files) 108 109 if inherit_parent.has_key(subdir): 110 objects = objects + inherit_parent[subdir] 111 112 symname = 'OBJECTS_%s_%s' % (subdir, platform) 113 114 objects.sort() 115 116 # and write the symbol for the whole group 117 f.write('\n%s = %s\n\n' % (symname, string.join(objects))) 118 119 # and include that symbol in the group 120 group.append('$(%s)' % symname) 121 122 group.sort() 123 124 # write out a symbol which contains the necessary files 125 f.write('OBJECTS_%s = %s\n\n' % (platform, string.join(group))) 126 127 f.write('HEADERS = $(top_srcdir)/%s\n\n' % string.join(headers, ' $(top_srcdir)/')) 128 f.write('SOURCE_DIRS = %s $(EXTRA_SOURCE_DIRS)\n\n' % string.join(dirs.keys())) 129 130 if parser.has_option('options', 'modules'): 131 modules = parser.get('options', 'modules') 132 133 for mod in string.split(modules): 134 files = get_files(parser.get(mod, 'paths')) 135 objects, _unused = write_objects(f, legal_deps, h_deps, files) 136 flat_objects = string.join(objects) 137 f.write('OBJECTS_%s = %s\n' % (mod, flat_objects)) 138 139 if parser.has_option(mod, 'target'): 140 target = parser.get(mod, 'target') 141 f.write('MODULE_%s = %s\n' % (mod, target)) 142 f.write('%s: %s\n' % (target, flat_objects)) 143 f.write('\t$(LINK_MODULE) -o $@ $(OBJECTS_%s) $(LDADD_%s)\n' % (mod, mod)) 144 145 f.write('\n') 146 147 # Build a list of all necessary directories in build tree 148 alldirs = { } 149 for dir in dirs.keys(): 150 d = dir 151 while d: 152 alldirs[d] = None 153 d = os.path.dirname(d) 154 155 # Sort so 'foo' is before 'foo/bar' 156 keys = alldirs.keys() 157 keys.sort() 158 f.write('BUILD_DIRS = %s\n\n' % string.join(keys)) 159 160 f.write('.make.dirs: $(srcdir)/build-outputs.mk\n' \ 161 '\t@for d in $(BUILD_DIRS); do test -d $$d || mkdir $$d; done\n' \ 162 '\t@echo timestamp > $@\n') 163 164 165def write_objects(f, legal_deps, h_deps, files): 166 dirs = { } 167 objects = [ ] 168 169 for file in files: 170 if file[-10:] == '/apr_app.c': 171 continue 172 assert file[-2:] == '.c' 173 obj = file[:-2] + '.lo' 174 objects.append(obj) 175 176 dirs[os.path.dirname(file)] = None 177 178 # what headers does this file include, along with the implied headers 179 deps = extract_deps(file, legal_deps) 180 for hdr in deps.keys(): 181 deps.update(h_deps.get(hdr, {})) 182 183 vals = deps.values() 184 vals.sort() 185 f.write('%s: %s .make.dirs %s\n' % (obj, file, string.join(vals))) 186 187 objects.sort() 188 189 return objects, dirs 190 191 192def extract_deps(fname, legal_deps): 193 "Extract the headers this file includes." 194 deps = { } 195 for line in open(fname).readlines(): 196 if line[:8] != '#include': 197 continue 198 inc = _re_include.match(line).group(1) 199 if inc in legal_deps.keys(): 200 deps[inc] = legal_deps[inc] 201 return deps 202_re_include = re.compile('#include *["<](.*)[">]') 203 204 205def resolve_deps(header_deps): 206 "Alter the provided dictionary to flatten includes-of-includes." 207 altered = 1 208 while altered: 209 altered = 0 210 for hdr, deps in header_deps.items(): 211 # print hdr, deps 212 start = len(deps) 213 for dep in deps.keys(): 214 deps.update(header_deps.get(dep, {})) 215 if len(deps) != start: 216 altered = 1 217 218def clean_path(path): 219 return path.replace("\\", "/") 220 221def get_files(patterns): 222 files = [ ] 223 for pat in string.split(patterns): 224 files.extend(map(clean_path, glob.glob(pat))) 225 files.sort() 226 return files 227 228 229if __name__ == '__main__': 230 main() 231