]> git.saurik.com Git - wxWidgets.git/blob - wxPython/my_distutils.py
compilation fixes
[wxWidgets.git] / wxPython / my_distutils.py
1
2 import sys, os, string
3
4 from distutils.msvccompiler import MSVCCompiler
5 from distutils.bcppcompiler import BCPPCompiler
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:
57 input_opt = "/Tc" + os.path.abspath(src) ### RPD
58 elif ext in self._cpp_extensions:
59 input_opt = "/Tp" + os.path.abspath(src) ### RPD
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
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
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
345
346
347 #----------------------------------------------------------------------
348 # Hack this module and class into the distutils...
349
350 from distutils import ccompiler
351
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
361 ccompiler.compiler_class['my_msvc'] = ('my_distutils',
362 'MyMSVCCompiler',
363 'My MSVC derived class')
364
365
366 ccompiler.compiler_class['my_bcpp'] = ('my_distutils',
367 'MyBCPPCompiler',
368 'My BCPP derived class')
369
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
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
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