|
1 """distutils.mwerkscompiler |
|
2 |
|
3 Contains MWerksCompiler, an implementation of the abstract CCompiler class |
|
4 for MetroWerks CodeWarrior on the Macintosh. Needs work to support CW on |
|
5 Windows.""" |
|
6 |
|
7 # This module should be kept compatible with Python 2.1. |
|
8 |
|
9 __revision__ = "$Id: mwerkscompiler.py 55881 2007-06-11 05:28:45Z neal.norwitz $" |
|
10 |
|
11 import sys, os, string |
|
12 from types import * |
|
13 from distutils.errors import \ |
|
14 DistutilsExecError, DistutilsPlatformError, \ |
|
15 CompileError, LibError, LinkError |
|
16 from distutils.ccompiler import \ |
|
17 CCompiler, gen_preprocess_options, gen_lib_options |
|
18 import distutils.util |
|
19 import distutils.dir_util |
|
20 from distutils import log |
|
21 |
|
22 class MWerksCompiler (CCompiler) : |
|
23 """Concrete class that implements an interface to MetroWerks CodeWarrior, |
|
24 as defined by the CCompiler abstract class.""" |
|
25 |
|
26 compiler_type = 'mwerks' |
|
27 |
|
28 # Just set this so CCompiler's constructor doesn't barf. We currently |
|
29 # don't use the 'set_executables()' bureaucracy provided by CCompiler, |
|
30 # as it really isn't necessary for this sort of single-compiler class. |
|
31 # Would be nice to have a consistent interface with UnixCCompiler, |
|
32 # though, so it's worth thinking about. |
|
33 executables = {} |
|
34 |
|
35 # Private class data (need to distinguish C from C++ source for compiler) |
|
36 _c_extensions = ['.c'] |
|
37 _cpp_extensions = ['.cc', '.cpp', '.cxx'] |
|
38 _rc_extensions = ['.r'] |
|
39 _exp_extension = '.exp' |
|
40 |
|
41 # Needed for the filename generation methods provided by the |
|
42 # base class, CCompiler. |
|
43 src_extensions = (_c_extensions + _cpp_extensions + |
|
44 _rc_extensions) |
|
45 res_extension = '.rsrc' |
|
46 obj_extension = '.obj' # Not used, really |
|
47 static_lib_extension = '.lib' |
|
48 shared_lib_extension = '.slb' |
|
49 static_lib_format = shared_lib_format = '%s%s' |
|
50 exe_extension = '' |
|
51 |
|
52 |
|
53 def __init__ (self, |
|
54 verbose=0, |
|
55 dry_run=0, |
|
56 force=0): |
|
57 |
|
58 CCompiler.__init__ (self, verbose, dry_run, force) |
|
59 |
|
60 |
|
61 def compile (self, |
|
62 sources, |
|
63 output_dir=None, |
|
64 macros=None, |
|
65 include_dirs=None, |
|
66 debug=0, |
|
67 extra_preargs=None, |
|
68 extra_postargs=None, |
|
69 depends=None): |
|
70 (output_dir, macros, include_dirs) = \ |
|
71 self._fix_compile_args (output_dir, macros, include_dirs) |
|
72 self.__sources = sources |
|
73 self.__macros = macros |
|
74 self.__include_dirs = include_dirs |
|
75 # Don't need extra_preargs and extra_postargs for CW |
|
76 return [] |
|
77 |
|
78 def link (self, |
|
79 target_desc, |
|
80 objects, |
|
81 output_filename, |
|
82 output_dir=None, |
|
83 libraries=None, |
|
84 library_dirs=None, |
|
85 runtime_library_dirs=None, |
|
86 export_symbols=None, |
|
87 debug=0, |
|
88 extra_preargs=None, |
|
89 extra_postargs=None, |
|
90 build_temp=None, |
|
91 target_lang=None): |
|
92 # First fixup. |
|
93 (objects, output_dir) = self._fix_object_args (objects, output_dir) |
|
94 (libraries, library_dirs, runtime_library_dirs) = \ |
|
95 self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) |
|
96 |
|
97 # First examine a couple of options for things that aren't implemented yet |
|
98 if not target_desc in (self.SHARED_LIBRARY, self.SHARED_OBJECT): |
|
99 raise DistutilsPlatformError, 'Can only make SHARED_LIBRARY or SHARED_OBJECT targets on the Mac' |
|
100 if runtime_library_dirs: |
|
101 raise DistutilsPlatformError, 'Runtime library dirs not implemented yet' |
|
102 if extra_preargs or extra_postargs: |
|
103 raise DistutilsPlatformError, 'Runtime library dirs not implemented yet' |
|
104 if len(export_symbols) != 1: |
|
105 raise DistutilsPlatformError, 'Need exactly one export symbol' |
|
106 # Next there are various things for which we need absolute pathnames. |
|
107 # This is because we (usually) create the project in a subdirectory of |
|
108 # where we are now, and keeping the paths relative is too much work right |
|
109 # now. |
|
110 sources = map(self._filename_to_abs, self.__sources) |
|
111 include_dirs = map(self._filename_to_abs, self.__include_dirs) |
|
112 if objects: |
|
113 objects = map(self._filename_to_abs, objects) |
|
114 else: |
|
115 objects = [] |
|
116 if build_temp: |
|
117 build_temp = self._filename_to_abs(build_temp) |
|
118 else: |
|
119 build_temp = os.curdir() |
|
120 if output_dir: |
|
121 output_filename = os.path.join(output_dir, output_filename) |
|
122 # The output filename needs special handling: splitting it into dir and |
|
123 # filename part. Actually I'm not sure this is really needed, but it |
|
124 # can't hurt. |
|
125 output_filename = self._filename_to_abs(output_filename) |
|
126 output_dir, output_filename = os.path.split(output_filename) |
|
127 # Now we need the short names of a couple of things for putting them |
|
128 # into the project. |
|
129 if output_filename[-8:] == '.ppc.slb': |
|
130 basename = output_filename[:-8] |
|
131 elif output_filename[-11:] == '.carbon.slb': |
|
132 basename = output_filename[:-11] |
|
133 else: |
|
134 basename = os.path.strip(output_filename)[0] |
|
135 projectname = basename + '.mcp' |
|
136 targetname = basename |
|
137 xmlname = basename + '.xml' |
|
138 exportname = basename + '.mcp.exp' |
|
139 prefixname = 'mwerks_%s_config.h'%basename |
|
140 # Create the directories we need |
|
141 distutils.dir_util.mkpath(build_temp, dry_run=self.dry_run) |
|
142 distutils.dir_util.mkpath(output_dir, dry_run=self.dry_run) |
|
143 # And on to filling in the parameters for the project builder |
|
144 settings = {} |
|
145 settings['mac_exportname'] = exportname |
|
146 settings['mac_outputdir'] = output_dir |
|
147 settings['mac_dllname'] = output_filename |
|
148 settings['mac_targetname'] = targetname |
|
149 settings['sysprefix'] = sys.prefix |
|
150 settings['mac_sysprefixtype'] = 'Absolute' |
|
151 sourcefilenames = [] |
|
152 sourcefiledirs = [] |
|
153 for filename in sources + objects: |
|
154 dirname, filename = os.path.split(filename) |
|
155 sourcefilenames.append(filename) |
|
156 if not dirname in sourcefiledirs: |
|
157 sourcefiledirs.append(dirname) |
|
158 settings['sources'] = sourcefilenames |
|
159 settings['libraries'] = libraries |
|
160 settings['extrasearchdirs'] = sourcefiledirs + include_dirs + library_dirs |
|
161 if self.dry_run: |
|
162 print 'CALLING LINKER IN', os.getcwd() |
|
163 for key, value in settings.items(): |
|
164 print '%20.20s %s'%(key, value) |
|
165 return |
|
166 # Build the export file |
|
167 exportfilename = os.path.join(build_temp, exportname) |
|
168 log.debug("\tCreate export file %s", exportfilename) |
|
169 fp = open(exportfilename, 'w') |
|
170 fp.write('%s\n'%export_symbols[0]) |
|
171 fp.close() |
|
172 # Generate the prefix file, if needed, and put it in the settings |
|
173 if self.__macros: |
|
174 prefixfilename = os.path.join(os.getcwd(), os.path.join(build_temp, prefixname)) |
|
175 fp = open(prefixfilename, 'w') |
|
176 fp.write('#include "mwerks_shcarbon_config.h"\n') |
|
177 for name, value in self.__macros: |
|
178 if value is None: |
|
179 fp.write('#define %s\n'%name) |
|
180 else: |
|
181 fp.write('#define %s %s\n'%(name, value)) |
|
182 fp.close() |
|
183 settings['prefixname'] = prefixname |
|
184 |
|
185 # Build the XML file. We need the full pathname (only lateron, really) |
|
186 # because we pass this pathname to CodeWarrior in an AppleEvent, and CW |
|
187 # doesn't have a clue about our working directory. |
|
188 xmlfilename = os.path.join(os.getcwd(), os.path.join(build_temp, xmlname)) |
|
189 log.debug("\tCreate XML file %s", xmlfilename) |
|
190 import mkcwproject |
|
191 xmlbuilder = mkcwproject.cwxmlgen.ProjectBuilder(settings) |
|
192 xmlbuilder.generate() |
|
193 xmldata = settings['tmp_projectxmldata'] |
|
194 fp = open(xmlfilename, 'w') |
|
195 fp.write(xmldata) |
|
196 fp.close() |
|
197 # Generate the project. Again a full pathname. |
|
198 projectfilename = os.path.join(os.getcwd(), os.path.join(build_temp, projectname)) |
|
199 log.debug('\tCreate project file %s', projectfilename) |
|
200 mkcwproject.makeproject(xmlfilename, projectfilename) |
|
201 # And build it |
|
202 log.debug('\tBuild project') |
|
203 mkcwproject.buildproject(projectfilename) |
|
204 |
|
205 def _filename_to_abs(self, filename): |
|
206 # Some filenames seem to be unix-like. Convert to Mac names. |
|
207 ## if '/' in filename and ':' in filename: |
|
208 ## raise DistutilsPlatformError, 'Filename may be Unix or Mac style: %s'%filename |
|
209 ## if '/' in filename: |
|
210 ## filename = macurl2path(filename) |
|
211 filename = distutils.util.convert_path(filename) |
|
212 if not os.path.isabs(filename): |
|
213 curdir = os.getcwd() |
|
214 filename = os.path.join(curdir, filename) |
|
215 # Finally remove .. components |
|
216 components = string.split(filename, ':') |
|
217 for i in range(1, len(components)): |
|
218 if components[i] == '..': |
|
219 components[i] = '' |
|
220 return string.join(components, ':') |
|
221 |
|
222 def library_dir_option (self, dir): |
|
223 """Return the compiler option to add 'dir' to the list of |
|
224 directories searched for libraries. |
|
225 """ |
|
226 return # XXXX Not correct... |
|
227 |
|
228 def runtime_library_dir_option (self, dir): |
|
229 """Return the compiler option to add 'dir' to the list of |
|
230 directories searched for runtime libraries. |
|
231 """ |
|
232 # Nothing needed or Mwerks/Mac. |
|
233 return |
|
234 |
|
235 def library_option (self, lib): |
|
236 """Return the compiler option to add 'dir' to the list of libraries |
|
237 linked into the shared library or executable. |
|
238 """ |
|
239 return |
|
240 |
|
241 def find_library_file (self, dirs, lib, debug=0): |
|
242 """Search the specified list of directories for a static or shared |
|
243 library file 'lib' and return the full path to that file. If |
|
244 'debug' true, look for a debugging version (if that makes sense on |
|
245 the current platform). Return None if 'lib' wasn't found in any of |
|
246 the specified directories. |
|
247 """ |
|
248 return 0 |