]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """distutils.bcppcompiler\r |
2 | \r | |
3 | Contains BorlandCCompiler, an implementation of the abstract CCompiler class\r | |
4 | for the Borland C++ compiler.\r | |
5 | """\r | |
6 | \r | |
7 | # This implementation by Lyle Johnson, based on the original msvccompiler.py\r | |
8 | # module and using the directions originally published by Gordon Williams.\r | |
9 | \r | |
10 | # XXX looks like there's a LOT of overlap between these two classes:\r | |
11 | # someone should sit down and factor out the common code as\r | |
12 | # WindowsCCompiler! --GPW\r | |
13 | \r | |
14 | __revision__ = "$Id$"\r | |
15 | \r | |
16 | import os\r | |
17 | \r | |
18 | from distutils.errors import (DistutilsExecError, CompileError, LibError,\r | |
19 | LinkError, UnknownFileError)\r | |
20 | from distutils.ccompiler import CCompiler, gen_preprocess_options\r | |
21 | from distutils.file_util import write_file\r | |
22 | from distutils.dep_util import newer\r | |
23 | from distutils import log\r | |
24 | \r | |
25 | class BCPPCompiler(CCompiler) :\r | |
26 | """Concrete class that implements an interface to the Borland C/C++\r | |
27 | compiler, as defined by the CCompiler abstract class.\r | |
28 | """\r | |
29 | \r | |
30 | compiler_type = 'bcpp'\r | |
31 | \r | |
32 | # Just set this so CCompiler's constructor doesn't barf. We currently\r | |
33 | # don't use the 'set_executables()' bureaucracy provided by CCompiler,\r | |
34 | # as it really isn't necessary for this sort of single-compiler class.\r | |
35 | # Would be nice to have a consistent interface with UnixCCompiler,\r | |
36 | # though, so it's worth thinking about.\r | |
37 | executables = {}\r | |
38 | \r | |
39 | # Private class data (need to distinguish C from C++ source for compiler)\r | |
40 | _c_extensions = ['.c']\r | |
41 | _cpp_extensions = ['.cc', '.cpp', '.cxx']\r | |
42 | \r | |
43 | # Needed for the filename generation methods provided by the\r | |
44 | # base class, CCompiler.\r | |
45 | src_extensions = _c_extensions + _cpp_extensions\r | |
46 | obj_extension = '.obj'\r | |
47 | static_lib_extension = '.lib'\r | |
48 | shared_lib_extension = '.dll'\r | |
49 | static_lib_format = shared_lib_format = '%s%s'\r | |
50 | exe_extension = '.exe'\r | |
51 | \r | |
52 | \r | |
53 | def __init__ (self,\r | |
54 | verbose=0,\r | |
55 | dry_run=0,\r | |
56 | force=0):\r | |
57 | \r | |
58 | CCompiler.__init__ (self, verbose, dry_run, force)\r | |
59 | \r | |
60 | # These executables are assumed to all be in the path.\r | |
61 | # Borland doesn't seem to use any special registry settings to\r | |
62 | # indicate their installation locations.\r | |
63 | \r | |
64 | self.cc = "bcc32.exe"\r | |
65 | self.linker = "ilink32.exe"\r | |
66 | self.lib = "tlib.exe"\r | |
67 | \r | |
68 | self.preprocess_options = None\r | |
69 | self.compile_options = ['/tWM', '/O2', '/q', '/g0']\r | |
70 | self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0']\r | |
71 | \r | |
72 | self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x']\r | |
73 | self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']\r | |
74 | self.ldflags_static = []\r | |
75 | self.ldflags_exe = ['/Gn', '/q', '/x']\r | |
76 | self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r']\r | |
77 | \r | |
78 | \r | |
79 | # -- Worker methods ------------------------------------------------\r | |
80 | \r | |
81 | def compile(self, sources,\r | |
82 | output_dir=None, macros=None, include_dirs=None, debug=0,\r | |
83 | extra_preargs=None, extra_postargs=None, depends=None):\r | |
84 | \r | |
85 | macros, objects, extra_postargs, pp_opts, build = \\r | |
86 | self._setup_compile(output_dir, macros, include_dirs, sources,\r | |
87 | depends, extra_postargs)\r | |
88 | compile_opts = extra_preargs or []\r | |
89 | compile_opts.append ('-c')\r | |
90 | if debug:\r | |
91 | compile_opts.extend (self.compile_options_debug)\r | |
92 | else:\r | |
93 | compile_opts.extend (self.compile_options)\r | |
94 | \r | |
95 | for obj in objects:\r | |
96 | try:\r | |
97 | src, ext = build[obj]\r | |
98 | except KeyError:\r | |
99 | continue\r | |
100 | # XXX why do the normpath here?\r | |
101 | src = os.path.normpath(src)\r | |
102 | obj = os.path.normpath(obj)\r | |
103 | # XXX _setup_compile() did a mkpath() too but before the normpath.\r | |
104 | # Is it possible to skip the normpath?\r | |
105 | self.mkpath(os.path.dirname(obj))\r | |
106 | \r | |
107 | if ext == '.res':\r | |
108 | # This is already a binary file -- skip it.\r | |
109 | continue # the 'for' loop\r | |
110 | if ext == '.rc':\r | |
111 | # This needs to be compiled to a .res file -- do it now.\r | |
112 | try:\r | |
113 | self.spawn (["brcc32", "-fo", obj, src])\r | |
114 | except DistutilsExecError, msg:\r | |
115 | raise CompileError, msg\r | |
116 | continue # the 'for' loop\r | |
117 | \r | |
118 | # The next two are both for the real compiler.\r | |
119 | if ext in self._c_extensions:\r | |
120 | input_opt = ""\r | |
121 | elif ext in self._cpp_extensions:\r | |
122 | input_opt = "-P"\r | |
123 | else:\r | |
124 | # Unknown file type -- no extra options. The compiler\r | |
125 | # will probably fail, but let it just in case this is a\r | |
126 | # file the compiler recognizes even if we don't.\r | |
127 | input_opt = ""\r | |
128 | \r | |
129 | output_opt = "-o" + obj\r | |
130 | \r | |
131 | # Compiler command line syntax is: "bcc32 [options] file(s)".\r | |
132 | # Note that the source file names must appear at the end of\r | |
133 | # the command line.\r | |
134 | try:\r | |
135 | self.spawn ([self.cc] + compile_opts + pp_opts +\r | |
136 | [input_opt, output_opt] +\r | |
137 | extra_postargs + [src])\r | |
138 | except DistutilsExecError, msg:\r | |
139 | raise CompileError, msg\r | |
140 | \r | |
141 | return objects\r | |
142 | \r | |
143 | # compile ()\r | |
144 | \r | |
145 | \r | |
146 | def create_static_lib (self,\r | |
147 | objects,\r | |
148 | output_libname,\r | |
149 | output_dir=None,\r | |
150 | debug=0,\r | |
151 | target_lang=None):\r | |
152 | \r | |
153 | (objects, output_dir) = self._fix_object_args (objects, output_dir)\r | |
154 | output_filename = \\r | |
155 | self.library_filename (output_libname, output_dir=output_dir)\r | |
156 | \r | |
157 | if self._need_link (objects, output_filename):\r | |
158 | lib_args = [output_filename, '/u'] + objects\r | |
159 | if debug:\r | |
160 | pass # XXX what goes here?\r | |
161 | try:\r | |
162 | self.spawn ([self.lib] + lib_args)\r | |
163 | except DistutilsExecError, msg:\r | |
164 | raise LibError, msg\r | |
165 | else:\r | |
166 | log.debug("skipping %s (up-to-date)", output_filename)\r | |
167 | \r | |
168 | # create_static_lib ()\r | |
169 | \r | |
170 | \r | |
171 | def link (self,\r | |
172 | target_desc,\r | |
173 | objects,\r | |
174 | output_filename,\r | |
175 | output_dir=None,\r | |
176 | libraries=None,\r | |
177 | library_dirs=None,\r | |
178 | runtime_library_dirs=None,\r | |
179 | export_symbols=None,\r | |
180 | debug=0,\r | |
181 | extra_preargs=None,\r | |
182 | extra_postargs=None,\r | |
183 | build_temp=None,\r | |
184 | target_lang=None):\r | |
185 | \r | |
186 | # XXX this ignores 'build_temp'! should follow the lead of\r | |
187 | # msvccompiler.py\r | |
188 | \r | |
189 | (objects, output_dir) = self._fix_object_args (objects, output_dir)\r | |
190 | (libraries, library_dirs, runtime_library_dirs) = \\r | |
191 | self._fix_lib_args (libraries, library_dirs, runtime_library_dirs)\r | |
192 | \r | |
193 | if runtime_library_dirs:\r | |
194 | log.warn("I don't know what to do with 'runtime_library_dirs': %s",\r | |
195 | str(runtime_library_dirs))\r | |
196 | \r | |
197 | if output_dir is not None:\r | |
198 | output_filename = os.path.join (output_dir, output_filename)\r | |
199 | \r | |
200 | if self._need_link (objects, output_filename):\r | |
201 | \r | |
202 | # Figure out linker args based on type of target.\r | |
203 | if target_desc == CCompiler.EXECUTABLE:\r | |
204 | startup_obj = 'c0w32'\r | |
205 | if debug:\r | |
206 | ld_args = self.ldflags_exe_debug[:]\r | |
207 | else:\r | |
208 | ld_args = self.ldflags_exe[:]\r | |
209 | else:\r | |
210 | startup_obj = 'c0d32'\r | |
211 | if debug:\r | |
212 | ld_args = self.ldflags_shared_debug[:]\r | |
213 | else:\r | |
214 | ld_args = self.ldflags_shared[:]\r | |
215 | \r | |
216 | \r | |
217 | # Create a temporary exports file for use by the linker\r | |
218 | if export_symbols is None:\r | |
219 | def_file = ''\r | |
220 | else:\r | |
221 | head, tail = os.path.split (output_filename)\r | |
222 | modname, ext = os.path.splitext (tail)\r | |
223 | temp_dir = os.path.dirname(objects[0]) # preserve tree structure\r | |
224 | def_file = os.path.join (temp_dir, '%s.def' % modname)\r | |
225 | contents = ['EXPORTS']\r | |
226 | for sym in (export_symbols or []):\r | |
227 | contents.append(' %s=_%s' % (sym, sym))\r | |
228 | self.execute(write_file, (def_file, contents),\r | |
229 | "writing %s" % def_file)\r | |
230 | \r | |
231 | # Borland C++ has problems with '/' in paths\r | |
232 | objects2 = map(os.path.normpath, objects)\r | |
233 | # split objects in .obj and .res files\r | |
234 | # Borland C++ needs them at different positions in the command line\r | |
235 | objects = [startup_obj]\r | |
236 | resources = []\r | |
237 | for file in objects2:\r | |
238 | (base, ext) = os.path.splitext(os.path.normcase(file))\r | |
239 | if ext == '.res':\r | |
240 | resources.append(file)\r | |
241 | else:\r | |
242 | objects.append(file)\r | |
243 | \r | |
244 | \r | |
245 | for l in library_dirs:\r | |
246 | ld_args.append("/L%s" % os.path.normpath(l))\r | |
247 | ld_args.append("/L.") # we sometimes use relative paths\r | |
248 | \r | |
249 | # list of object files\r | |
250 | ld_args.extend(objects)\r | |
251 | \r | |
252 | # XXX the command-line syntax for Borland C++ is a bit wonky;\r | |
253 | # certain filenames are jammed together in one big string, but\r | |
254 | # comma-delimited. This doesn't mesh too well with the\r | |
255 | # Unix-centric attitude (with a DOS/Windows quoting hack) of\r | |
256 | # 'spawn()', so constructing the argument list is a bit\r | |
257 | # awkward. Note that doing the obvious thing and jamming all\r | |
258 | # the filenames and commas into one argument would be wrong,\r | |
259 | # because 'spawn()' would quote any filenames with spaces in\r | |
260 | # them. Arghghh!. Apparently it works fine as coded...\r | |
261 | \r | |
262 | # name of dll/exe file\r | |
263 | ld_args.extend([',',output_filename])\r | |
264 | # no map file and start libraries\r | |
265 | ld_args.append(',,')\r | |
266 | \r | |
267 | for lib in libraries:\r | |
268 | # see if we find it and if there is a bcpp specific lib\r | |
269 | # (xxx_bcpp.lib)\r | |
270 | libfile = self.find_library_file(library_dirs, lib, debug)\r | |
271 | if libfile is None:\r | |
272 | ld_args.append(lib)\r | |
273 | # probably a BCPP internal library -- don't warn\r | |
274 | else:\r | |
275 | # full name which prefers bcpp_xxx.lib over xxx.lib\r | |
276 | ld_args.append(libfile)\r | |
277 | \r | |
278 | # some default libraries\r | |
279 | ld_args.append ('import32')\r | |
280 | ld_args.append ('cw32mt')\r | |
281 | \r | |
282 | # def file for export symbols\r | |
283 | ld_args.extend([',',def_file])\r | |
284 | # add resource files\r | |
285 | ld_args.append(',')\r | |
286 | ld_args.extend(resources)\r | |
287 | \r | |
288 | \r | |
289 | if extra_preargs:\r | |
290 | ld_args[:0] = extra_preargs\r | |
291 | if extra_postargs:\r | |
292 | ld_args.extend(extra_postargs)\r | |
293 | \r | |
294 | self.mkpath (os.path.dirname (output_filename))\r | |
295 | try:\r | |
296 | self.spawn ([self.linker] + ld_args)\r | |
297 | except DistutilsExecError, msg:\r | |
298 | raise LinkError, msg\r | |
299 | \r | |
300 | else:\r | |
301 | log.debug("skipping %s (up-to-date)", output_filename)\r | |
302 | \r | |
303 | # link ()\r | |
304 | \r | |
305 | # -- Miscellaneous methods -----------------------------------------\r | |
306 | \r | |
307 | \r | |
308 | def find_library_file (self, dirs, lib, debug=0):\r | |
309 | # List of effective library names to try, in order of preference:\r | |
310 | # xxx_bcpp.lib is better than xxx.lib\r | |
311 | # and xxx_d.lib is better than xxx.lib if debug is set\r | |
312 | #\r | |
313 | # The "_bcpp" suffix is to handle a Python installation for people\r | |
314 | # with multiple compilers (primarily Distutils hackers, I suspect\r | |
315 | # ;-). The idea is they'd have one static library for each\r | |
316 | # compiler they care about, since (almost?) every Windows compiler\r | |
317 | # seems to have a different format for static libraries.\r | |
318 | if debug:\r | |
319 | dlib = (lib + "_d")\r | |
320 | try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)\r | |
321 | else:\r | |
322 | try_names = (lib + "_bcpp", lib)\r | |
323 | \r | |
324 | for dir in dirs:\r | |
325 | for name in try_names:\r | |
326 | libfile = os.path.join(dir, self.library_filename(name))\r | |
327 | if os.path.exists(libfile):\r | |
328 | return libfile\r | |
329 | else:\r | |
330 | # Oops, didn't find it in *any* of 'dirs'\r | |
331 | return None\r | |
332 | \r | |
333 | # overwrite the one from CCompiler to support rc and res-files\r | |
334 | def object_filenames (self,\r | |
335 | source_filenames,\r | |
336 | strip_dir=0,\r | |
337 | output_dir=''):\r | |
338 | if output_dir is None: output_dir = ''\r | |
339 | obj_names = []\r | |
340 | for src_name in source_filenames:\r | |
341 | # use normcase to make sure '.rc' is really '.rc' and not '.RC'\r | |
342 | (base, ext) = os.path.splitext (os.path.normcase(src_name))\r | |
343 | if ext not in (self.src_extensions + ['.rc','.res']):\r | |
344 | raise UnknownFileError, \\r | |
345 | "unknown file type '%s' (from '%s')" % \\r | |
346 | (ext, src_name)\r | |
347 | if strip_dir:\r | |
348 | base = os.path.basename (base)\r | |
349 | if ext == '.res':\r | |
350 | # these can go unchanged\r | |
351 | obj_names.append (os.path.join (output_dir, base + ext))\r | |
352 | elif ext == '.rc':\r | |
353 | # these need to be compiled to .res-files\r | |
354 | obj_names.append (os.path.join (output_dir, base + '.res'))\r | |
355 | else:\r | |
356 | obj_names.append (os.path.join (output_dir,\r | |
357 | base + self.obj_extension))\r | |
358 | return obj_names\r | |
359 | \r | |
360 | # object_filenames ()\r | |
361 | \r | |
362 | def preprocess (self,\r | |
363 | source,\r | |
364 | output_file=None,\r | |
365 | macros=None,\r | |
366 | include_dirs=None,\r | |
367 | extra_preargs=None,\r | |
368 | extra_postargs=None):\r | |
369 | \r | |
370 | (_, macros, include_dirs) = \\r | |
371 | self._fix_compile_args(None, macros, include_dirs)\r | |
372 | pp_opts = gen_preprocess_options(macros, include_dirs)\r | |
373 | pp_args = ['cpp32.exe'] + pp_opts\r | |
374 | if output_file is not None:\r | |
375 | pp_args.append('-o' + output_file)\r | |
376 | if extra_preargs:\r | |
377 | pp_args[:0] = extra_preargs\r | |
378 | if extra_postargs:\r | |
379 | pp_args.extend(extra_postargs)\r | |
380 | pp_args.append(source)\r | |
381 | \r | |
382 | # We need to preprocess: either we're being forced to, or the\r | |
383 | # source file is newer than the target (or the target doesn't\r | |
384 | # exist).\r | |
385 | if self.force or output_file is None or newer(source, output_file):\r | |
386 | if output_file:\r | |
387 | self.mkpath(os.path.dirname(output_file))\r | |
388 | try:\r | |
389 | self.spawn(pp_args)\r | |
390 | except DistutilsExecError, msg:\r | |
391 | print msg\r | |
392 | raise CompileError, msg\r | |
393 | \r | |
394 | # preprocess()\r |