]>
Commit | Line | Data |
---|---|---|
c368d904 RD |
1 | |
2 | import sys, os, string | |
3 | ||
4 | from distutils.msvccompiler import MSVCCompiler | |
22d08289 | 5 | from distutils.bcppcompiler import BCPPCompiler |
c368d904 RD |
6 | |
7 | from distutils.errors import \ | |
8 | DistutilsExecError, DistutilsPlatformError, \ | |
9 | CompileError, LibError, LinkError | |
10 | from distutils.ccompiler import \ | |
11 | CCompiler, gen_preprocess_options, gen_lib_options | |
12 | ||
13 | #---------------------------------------------------------------------- | |
14 | ||
15 | class MyMSVCCompiler(MSVCCompiler): | |
16 | ||
17 | ##------------------------------------------------------------ | |
18 | ## Override the entire compile method just to add flags to the | |
19 | ## RC command. There should be an easier way to do this from | |
20 | ## distutils directly or in a derived class... | |
21 | ##------------------------------------------------------------ | |
22 | ||
23 | def compile (self, | |
24 | sources, | |
25 | output_dir=None, | |
26 | macros=None, | |
27 | include_dirs=None, | |
28 | debug=0, | |
29 | extra_preargs=None, | |
30 | extra_postargs=None): | |
31 | ||
32 | (output_dir, macros, include_dirs) = \ | |
33 | self._fix_compile_args (output_dir, macros, include_dirs) | |
34 | (objects, skip_sources) = self._prep_compile (sources, output_dir) | |
35 | ||
36 | if extra_postargs is None: | |
37 | extra_postargs = [] | |
38 | ||
39 | pp_opts = gen_preprocess_options (macros, include_dirs) | |
40 | compile_opts = extra_preargs or [] | |
41 | compile_opts.append ('/c') | |
42 | if debug: | |
43 | compile_opts.extend (self.compile_options_debug) | |
44 | else: | |
45 | compile_opts.extend (self.compile_options) | |
46 | ||
47 | for i in range (len (sources)): | |
48 | src = sources[i] ; obj = objects[i] | |
49 | ext = (os.path.splitext (src))[1] | |
50 | ||
51 | if skip_sources[src]: | |
52 | self.announce ("skipping %s (%s up-to-date)" % (src, obj)) | |
53 | else: | |
54 | self.mkpath (os.path.dirname (obj)) | |
55 | ||
56 | if ext in self._c_extensions: | |
185d7c3e | 57 | input_opt = "/Tc" + os.path.abspath(src) |
c368d904 | 58 | elif ext in self._cpp_extensions: |
185d7c3e | 59 | input_opt = "/Tp" + os.path.abspath(src) |
c368d904 RD |
60 | elif ext in self._rc_extensions: |
61 | # compile .RC to .RES file | |
62 | input_opt = src | |
63 | output_opt = "/fo" + obj | |
64 | try: | |
65 | self.spawn ([self.rc] + pp_opts + ### RPD changed this line only | |
66 | [output_opt] + [input_opt]) | |
67 | except DistutilsExecError, msg: | |
68 | raise CompileError, msg | |
69 | continue | |
70 | elif ext in self._mc_extensions: | |
71 | ||
72 | # Compile .MC to .RC file to .RES file. | |
73 | # * '-h dir' specifies the directory for the | |
74 | # generated include file | |
75 | # * '-r dir' specifies the target directory of the | |
76 | # generated RC file and the binary message resource | |
77 | # it includes | |
78 | # | |
79 | # For now (since there are no options to change this), | |
80 | # we use the source-directory for the include file and | |
81 | # the build directory for the RC file and message | |
82 | # resources. This works at least for win32all. | |
83 | ||
84 | h_dir = os.path.dirname (src) | |
85 | rc_dir = os.path.dirname (obj) | |
86 | try: | |
87 | # first compile .MC to .RC and .H file | |
88 | self.spawn ([self.mc] + | |
89 | ['-h', h_dir, '-r', rc_dir] + [src]) | |
90 | base, _ = os.path.splitext (os.path.basename (src)) | |
91 | rc_file = os.path.join (rc_dir, base + '.rc') | |
92 | # then compile .RC to .RES file | |
93 | self.spawn ([self.rc] + | |
94 | ["/fo" + obj] + [rc_file]) | |
95 | ||
96 | except DistutilsExecError, msg: | |
97 | raise CompileError, msg | |
98 | continue | |
99 | else: | |
100 | # how to handle this file? | |
101 | raise CompileError ( | |
102 | "Don't know how to compile %s to %s" % \ | |
103 | (src, obj)) | |
104 | ||
105 | output_opt = "/Fo" + obj | |
106 | try: | |
107 | self.spawn ([self.cc] + compile_opts + pp_opts + | |
108 | [input_opt, output_opt] + | |
109 | extra_postargs) | |
110 | except DistutilsExecError, msg: | |
111 | raise CompileError, msg | |
112 | ||
113 | return objects | |
114 | ||
115 | # compile () | |
116 | ||
117 | ||
118 | ||
22d08289 RD |
119 | from distutils.file_util import write_file |
120 | class MyBCPPCompiler(BCPPCompiler): | |
121 | ||
122 | ##------------------------------------------------------------ | |
123 | ## Override the entire compile method just to add flags to the | |
124 | ## RC command. There should be an easier way to do this from | |
125 | ## distutils directly or in a derived class... | |
126 | ##------------------------------------------------------------ | |
127 | ||
128 | def compile (self, | |
129 | sources, | |
130 | output_dir=None, | |
131 | macros=None, | |
132 | include_dirs=None, | |
133 | debug=0, | |
134 | extra_preargs=None, | |
135 | extra_postargs=None): | |
136 | ||
137 | (output_dir, macros, include_dirs) = \ | |
138 | self._fix_compile_args (output_dir, macros, include_dirs) | |
139 | (objects, skip_sources) = self._prep_compile (sources, output_dir) | |
140 | ||
141 | if extra_postargs is None: | |
142 | extra_postargs = [] | |
143 | ||
144 | pp_opts = gen_preprocess_options (macros, include_dirs) | |
145 | compile_opts = extra_preargs or [] | |
146 | compile_opts.append ('-c') | |
147 | if debug: | |
148 | compile_opts.extend (self.compile_options_debug) | |
149 | else: | |
150 | compile_opts.extend (self.compile_options) | |
151 | ||
152 | for i in range (len (sources)): | |
153 | src = sources[i] ; obj = objects[i] | |
154 | ext = (os.path.splitext (src))[1] | |
155 | ||
156 | if skip_sources[src]: | |
157 | self.announce ("skipping %s (%s up-to-date)" % (src, obj)) | |
158 | else: | |
159 | src = os.path.normpath(src) | |
160 | obj = os.path.normpath(obj) | |
161 | self.mkpath(os.path.dirname(obj)) | |
162 | ||
163 | if ext == '.res': | |
164 | # This is already a binary file -- skip it. | |
165 | continue # the 'for' loop | |
166 | if ext == '.rc': | |
167 | # This needs to be compiled to a .res file -- do it now. | |
168 | try: | |
169 | self.spawn (["brcc32"] + pp_opts + ["-fo"] + | |
170 | [obj] + [src]) ### RPD changed this lines only | |
171 | except DistutilsExecError, msg: | |
172 | raise CompileError, msg | |
173 | continue # the 'for' loop | |
174 | ||
175 | # The next two are both for the real compiler. | |
176 | if ext in self._c_extensions: | |
177 | input_opt = "" | |
178 | elif ext in self._cpp_extensions: | |
179 | input_opt = "-P" | |
180 | else: | |
181 | # Unknown file type -- no extra options. The compiler | |
182 | # will probably fail, but let it just in case this is a | |
183 | # file the compiler recognizes even if we don't. | |
184 | input_opt = "" | |
185 | ||
186 | output_opt = "-o" + obj | |
187 | ||
188 | # Compiler command line syntax is: "bcc32 [options] file(s)". | |
189 | # Note that the source file names must appear at the end of | |
190 | # the command line. | |
191 | try: | |
192 | self.spawn ([self.cc] + compile_opts + pp_opts + | |
193 | [input_opt, output_opt] + | |
194 | extra_postargs + [src]) | |
195 | except DistutilsExecError, msg: | |
196 | raise CompileError, msg | |
197 | ||
198 | return objects | |
199 | ||
200 | # compile () | |
201 | ||
202 | #################################################################### | |
203 | # Now we need to replace cw32mt library used by default by distutils | |
204 | # with cw32mti library as in wxWindows DLL make file | |
205 | # Othervise we obtain Windows "Core dump" ;-). | |
206 | # | |
207 | # Evgeny A Cherkashin <eugeneai@icc.ru> | |
208 | # | |
209 | #################################################################### | |
210 | ||
211 | def link (self, | |
212 | target_desc, | |
213 | objects, | |
214 | output_filename, | |
215 | output_dir=None, | |
216 | libraries=None, | |
217 | library_dirs=None, | |
218 | runtime_library_dirs=None, | |
219 | export_symbols=None, | |
220 | debug=0, | |
221 | extra_preargs=None, | |
222 | extra_postargs=None, | |
223 | build_temp=None): | |
224 | ||
225 | # XXX this ignores 'build_temp'! should follow the lead of | |
226 | # msvccompiler.py | |
227 | ||
228 | (objects, output_dir) = self._fix_object_args (objects, output_dir) | |
229 | (libraries, library_dirs, runtime_library_dirs) = \ | |
230 | self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) | |
231 | ||
232 | if runtime_library_dirs: | |
233 | self.warn ("I don't know what to do with 'runtime_library_dirs': " | |
234 | + str (runtime_library_dirs)) | |
235 | ||
236 | if output_dir is not None: | |
237 | output_filename = os.path.join (output_dir, output_filename) | |
238 | ||
239 | if self._need_link (objects, output_filename): | |
240 | ||
241 | # Figure out linker args based on type of target. | |
242 | if target_desc == CCompiler.EXECUTABLE: | |
243 | startup_obj = 'c0w32' | |
244 | if debug: | |
245 | ld_args = self.ldflags_exe_debug[:] | |
246 | else: | |
247 | ld_args = self.ldflags_exe[:] | |
248 | else: | |
249 | startup_obj = 'c0d32' | |
250 | if debug: | |
251 | ld_args = self.ldflags_shared_debug[:] | |
252 | else: | |
253 | ld_args = self.ldflags_shared[:] | |
254 | ||
255 | ||
256 | # Create a temporary exports file for use by the linker | |
257 | if export_symbols is None: | |
258 | def_file = '' | |
259 | else: | |
260 | head, tail = os.path.split (output_filename) | |
261 | modname, ext = os.path.splitext (tail) | |
262 | temp_dir = os.path.dirname(objects[0]) # preserve tree structure | |
263 | def_file = os.path.join (temp_dir, '%s.def' % modname) | |
264 | contents = ['EXPORTS'] | |
265 | for sym in (export_symbols or []): | |
266 | contents.append(' %s=_%s' % (sym, sym)) | |
267 | self.execute(write_file, (def_file, contents), | |
268 | "writing %s" % def_file) | |
269 | ||
270 | # Borland C++ has problems with '/' in paths | |
271 | objects2 = map(os.path.normpath, objects) | |
272 | # split objects in .obj and .res files | |
273 | # Borland C++ needs them at different positions in the command line | |
274 | objects = [startup_obj] | |
275 | resources = [] | |
276 | for file in objects2: | |
277 | (base, ext) = os.path.splitext(os.path.normcase(file)) | |
278 | if ext == '.res': | |
279 | resources.append(file) | |
280 | else: | |
281 | objects.append(file) | |
282 | ||
283 | ||
284 | for l in library_dirs: | |
285 | ld_args.append("/L%s" % os.path.normpath(l)) | |
286 | ld_args.append("/L.") # we sometimes use relative paths | |
287 | ||
288 | # list of object files | |
289 | ld_args.extend(objects) | |
290 | ||
291 | # XXX the command-line syntax for Borland C++ is a bit wonky; | |
292 | # certain filenames are jammed together in one big string, but | |
293 | # comma-delimited. This doesn't mesh too well with the | |
294 | # Unix-centric attitude (with a DOS/Windows quoting hack) of | |
295 | # 'spawn()', so constructing the argument list is a bit | |
296 | # awkward. Note that doing the obvious thing and jamming all | |
297 | # the filenames and commas into one argument would be wrong, | |
298 | # because 'spawn()' would quote any filenames with spaces in | |
299 | # them. Arghghh!. Apparently it works fine as coded... | |
300 | ||
301 | # name of dll/exe file | |
302 | ld_args.extend([',',output_filename]) | |
303 | # no map file and start libraries | |
304 | ld_args.append(',,') | |
305 | ||
306 | for lib in libraries: | |
307 | # see if we find it and if there is a bcpp specific lib | |
308 | # (xxx_bcpp.lib) | |
309 | libfile = self.find_library_file(library_dirs, lib, debug) | |
310 | if libfile is None: | |
311 | ld_args.append(lib) | |
312 | # probably a BCPP internal library -- don't warn | |
313 | # self.warn('library %s not found.' % lib) | |
314 | else: | |
315 | # full name which prefers bcpp_xxx.lib over xxx.lib | |
316 | ld_args.append(libfile) | |
317 | ||
318 | # some default libraries | |
319 | ld_args.append ('import32') | |
320 | ld_args.append ('cw32mti') ### mt->mti (as in wx2) | |
321 | ||
322 | # def file for export symbols | |
323 | ld_args.extend([',',def_file]) | |
324 | # add resource files | |
325 | ld_args.append(',') | |
326 | ld_args.extend(resources) | |
327 | ||
328 | ||
329 | if extra_preargs: | |
330 | ld_args[:0] = extra_preargs | |
331 | if extra_postargs: | |
332 | ld_args.extend(extra_postargs) | |
333 | ||
334 | self.mkpath (os.path.dirname (output_filename)) | |
335 | try: | |
336 | self.spawn ([self.linker] + ld_args) | |
337 | except DistutilsExecError, msg: | |
338 | raise LinkError, msg | |
339 | ||
340 | else: | |
341 | self.announce ("skipping %s (up-to-date)" % output_filename) | |
342 | ||
343 | # link () | |
344 | ||
c368d904 RD |
345 | |
346 | ||
347 | #---------------------------------------------------------------------- | |
348 | # Hack this module and class into the distutils... | |
349 | ||
350 | from distutils import ccompiler | |
351 | ||
743e91ff RD |
352 | if hasattr(ccompiler, "default_compiler"): |
353 | ccompiler.default_compiler['nt'] = 'my_msvc' | |
354 | elif hasattr(ccompiler, "_default_compilers"): | |
355 | lst = list(ccompiler._default_compilers) | |
356 | lst.remove( ('nt', 'msvc') ) | |
357 | lst.append( ('nt', 'my_msvc') ) | |
358 | ccompiler._default_compilers = tuple(lst) | |
359 | ||
360 | ||
c368d904 RD |
361 | ccompiler.compiler_class['my_msvc'] = ('my_distutils', |
362 | 'MyMSVCCompiler', | |
363 | 'My MSVC derived class') | |
364 | ||
365 | ||
22d08289 RD |
366 | ccompiler.compiler_class['my_bcpp'] = ('my_distutils', |
367 | 'MyBCPPCompiler', | |
368 | 'My BCPP derived class') | |
369 | ||
c368d904 RD |
370 | # make it look like it is part of the package... |
371 | import my_distutils | |
372 | sys.modules['distutils.my_distutils'] = my_distutils | |
373 | ||
374 | ||
743e91ff RD |
375 | #---------------------------------------------------------------------- |
376 | # More hacking... Distutils in Python 2.1 changed the strip_dir flag | |
377 | # passed to object_filenames to true, which causes problems for us since | |
378 | # there are a few duplicate source/object names between some of the | |
379 | # extensions in wxPython. This hack replaces the CCompiler._prep_compile | |
380 | # method with this one. | |
381 | ||
382 | from distutils.dep_util import newer_pairwise | |
383 | ||
384 | def _prep_compile (self, sources, output_dir): | |
385 | """Determine the list of object files corresponding to 'sources', | |
386 | and figure out which ones really need to be recompiled. Return a | |
387 | list of all object files and a dictionary telling which source | |
388 | files can be skipped. | |
389 | """ | |
390 | # Get the list of expected output (object) files | |
391 | objects = self.object_filenames (sources, | |
392 | strip_dir=0, | |
393 | output_dir=output_dir) | |
394 | ||
395 | if self.force: | |
396 | skip_source = {} # rebuild everything | |
397 | for source in sources: | |
398 | skip_source[source] = 0 | |
399 | else: | |
400 | # Figure out which source files we have to recompile according | |
401 | # to a simplistic check -- we just compare the source and | |
402 | # object file, no deep dependency checking involving header | |
403 | # files. | |
404 | skip_source = {} # rebuild everything | |
405 | for source in sources: # no wait, rebuild nothing | |
406 | skip_source[source] = 1 | |
407 | ||
408 | (n_sources, n_objects) = newer_pairwise (sources, objects) | |
409 | for source in n_sources: # no really, only rebuild what's | |
410 | skip_source[source] = 0 # out-of-date | |
411 | ||
412 | return (objects, skip_source) | |
413 | ||
414 | # _prep_compile () | |
415 | ||
416 | CCompiler._prep_compile = _prep_compile | |
417 | ||
418 | ||
419 | ||
420 | ||
c368d904 RD |
421 | #---------------------------------------------------------------------- |
422 | # Run SWIG the way I want it done | |
423 | ||
424 | def run_swig(files, dir, gendir, package, USE_SWIG, force, swig_args, swig_deps=[]): | |
425 | from distutils.file_util import copy_file | |
426 | from distutils.dep_util import newer | |
427 | from distutils.spawn import spawn | |
428 | ||
429 | sources = [] | |
430 | ||
431 | for file in files: | |
432 | basefile = os.path.splitext(file)[0] | |
433 | i_file = os.path.join(dir, file) | |
434 | py_file = os.path.join(dir, gendir, basefile+'.py') | |
435 | cpp_file = os.path.join(dir, gendir, basefile+'.cpp') | |
436 | ||
437 | sources.append(cpp_file) | |
438 | ||
439 | if USE_SWIG: | |
440 | for dep in swig_deps: | |
441 | if newer(dep, py_file) or newer(dep, cpp_file): | |
442 | force = 1 | |
443 | break | |
444 | ||
445 | if force or newer(i_file, py_file) or newer(i_file, cpp_file): | |
446 | # we need forward slashes here even on win32 | |
447 | cpp_file = string.join(string.split(cpp_file, '\\'), '/') | |
448 | i_file = string.join(string.split(i_file, '\\'), '/') | |
449 | ||
450 | cmd = ['swig'] + swig_args + ['-I'+dir, '-c', '-o', cpp_file, i_file] | |
451 | spawn(cmd, verbose=1) | |
452 | ||
453 | # copy the generated python file to the package directory | |
454 | copy_file(py_file, package, update=not force, verbose=0) | |
455 | ||
456 | ||
457 | return sources | |
458 | ||
459 | ||
460 | ||
461 | #---------------------------------------------------------------------- | |
462 | # Update local copies of wxWindows contrib files | |
463 | ||
464 | ||
465 | def contrib_copy_tree(src, dest, verbose=0): | |
466 | from distutils.dir_util import mkpath, copy_tree | |
467 | ||
468 | mkpath(dest, verbose=verbose) | |
469 | copy_tree(src, dest, update=1, verbose=verbose) | |
470 |