Initial import.
[profile/ivi/apr.git] / build / gen-build.py
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
12 import os
13 import ConfigParser
14 import getopt
15 import string
16 import glob
17 import 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 #
25 PLATFORMS = [ 'aix', 'beos', 'netware', 'os2', 'os390', 'unix', 'win32' ]
26 MAKE_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
38 def 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
165 def 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
192 def 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
205 def 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
218 def clean_path(path):
219     return path.replace("\\", "/")
220
221 def 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
229 if __name__ == '__main__':
230   main()