]>
Commit | Line | Data |
---|---|---|
1e4a197e RD |
1 | """distutils.unixccompiler |
2 | ||
3 | Contains the UnixCCompiler class, a subclass of CCompiler that handles | |
4 | the "typical" Unix-style command-line C compiler: | |
5 | * macros defined with -Dname[=value] | |
6 | * macros undefined with -Uname | |
7 | * include search directories specified with -Idir | |
8 | * libraries specified with -lllib | |
9 | * library search directories specified with -Ldir | |
10 | * compile handled by 'cc' (or similar) executable with -c option: | |
11 | compiles .c to .o | |
12 | * link static library handled by 'ar' command (possibly with 'ranlib') | |
13 | * link shared library handled by 'cc -shared' | |
14 | """ | |
15 | ||
16 | __revision__ = "$Id$" | |
17 | ||
18 | import os, sys | |
19 | from types import StringType, NoneType | |
20 | from copy import copy | |
21 | ||
22 | from distutils import sysconfig | |
23 | from distutils.dep_util import newer | |
24 | from distutils.ccompiler import \ | |
25 | CCompiler, gen_preprocess_options, gen_lib_options | |
26 | from distutils.errors import \ | |
27 | DistutilsExecError, CompileError, LibError, LinkError | |
28 | from distutils import log | |
29 | ||
30 | # XXX Things not currently handled: | |
31 | # * optimization/debug/warning flags; we just use whatever's in Python's | |
32 | # Makefile and live with it. Is this adequate? If not, we might | |
33 | # have to have a bunch of subclasses GNUCCompiler, SGICCompiler, | |
34 | # SunCCompiler, and I suspect down that road lies madness. | |
35 | # * even if we don't know a warning flag from an optimization flag, | |
36 | # we need some way for outsiders to feed preprocessor/compiler/linker | |
37 | # flags in to us -- eg. a sysadmin might want to mandate certain flags | |
38 | # via a site config file, or a user might want to set something for | |
39 | # compiling this module distribution only via the setup.py command | |
40 | # line, whatever. As long as these options come from something on the | |
41 | # current system, they can be as system-dependent as they like, and we | |
42 | # should just happily stuff them into the preprocessor/compiler/linker | |
43 | # options and carry on. | |
44 | ||
45 | class UnixCCompiler(CCompiler): | |
46 | ||
47 | compiler_type = 'unix' | |
48 | ||
49 | # These are used by CCompiler in two places: the constructor sets | |
50 | # instance attributes 'preprocessor', 'compiler', etc. from them, and | |
51 | # 'set_executable()' allows any of these to be set. The defaults here | |
52 | # are pretty generic; they will probably have to be set by an outsider | |
53 | # (eg. using information discovered by the sysconfig about building | |
54 | # Python extensions). | |
55 | executables = {'preprocessor' : None, | |
56 | 'compiler' : ["cc"], | |
57 | 'compiler_so' : ["cc"], | |
58 | 'compiler_cxx' : ["cc"], | |
59 | 'linker_so' : ["cc", "-shared"], | |
60 | 'linker_exe' : ["cc"], | |
61 | 'archiver' : ["ar", "-cr"], | |
62 | 'ranlib' : None, | |
63 | } | |
64 | ||
65 | if sys.platform[:6] == "darwin": | |
66 | executables['ranlib'] = ["ranlib"] | |
67 | ||
68 | # Needed for the filename generation methods provided by the base | |
69 | # class, CCompiler. NB. whoever instantiates/uses a particular | |
70 | # UnixCCompiler instance should set 'shared_lib_ext' -- we set a | |
71 | # reasonable common default here, but it's not necessarily used on all | |
72 | # Unices! | |
73 | ||
74 | src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"] | |
75 | obj_extension = ".o" | |
76 | static_lib_extension = ".a" | |
77 | shared_lib_extension = ".so" | |
78 | dylib_lib_extension = ".dylib" | |
79 | static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" | |
80 | ||
81 | def preprocess(self, source, | |
82 | output_file=None, macros=None, include_dirs=None, | |
83 | extra_preargs=None, extra_postargs=None): | |
84 | ignore, macros, include_dirs = \ | |
85 | self._fix_compile_args(None, macros, include_dirs) | |
86 | pp_opts = gen_preprocess_options(macros, include_dirs) | |
87 | pp_args = self.preprocessor + pp_opts | |
88 | if output_file: | |
89 | pp_args.extend(['-o', output_file]) | |
90 | if extra_preargs: | |
91 | pp_args[:0] = extra_preargs | |
92 | if extra_postargs: | |
93 | pp_args.extend(extra_postargs) | |
94 | pp_args.append(source) | |
95 | ||
96 | # We need to preprocess: either we're being forced to, or we're | |
97 | # generating output to stdout, or there's a target output file and | |
98 | # the source file is newer than the target (or the target doesn't | |
99 | # exist). | |
100 | if self.force or output_file is None or newer(source, output_file): | |
101 | if output_file: | |
102 | self.mkpath(os.path.dirname(output_file)) | |
103 | try: | |
104 | self.spawn(pp_args) | |
105 | except DistutilsExecError, msg: | |
106 | raise CompileError, msg | |
107 | ||
108 | def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): | |
109 | try: | |
110 | self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + | |
111 | extra_postargs) | |
112 | except DistutilsExecError, msg: | |
113 | raise CompileError, msg | |
114 | ||
115 | def create_static_lib(self, objects, output_libname, | |
116 | output_dir=None, debug=0, target_lang=None): | |
117 | objects, output_dir = self._fix_object_args(objects, output_dir) | |
118 | ||
119 | output_filename = \ | |
120 | self.library_filename(output_libname, output_dir=output_dir) | |
121 | ||
122 | if self._need_link(objects, output_filename): | |
123 | self.mkpath(os.path.dirname(output_filename)) | |
124 | self.spawn(self.archiver + | |
125 | [output_filename] + | |
126 | objects + self.objects) | |
127 | ||
128 | # Not many Unices required ranlib anymore -- SunOS 4.x is, I | |
129 | # think the only major Unix that does. Maybe we need some | |
130 | # platform intelligence here to skip ranlib if it's not | |
131 | # needed -- or maybe Python's configure script took care of | |
132 | # it for us, hence the check for leading colon. | |
133 | if self.ranlib: | |
134 | try: | |
135 | self.spawn(self.ranlib + [output_filename]) | |
136 | except DistutilsExecError, msg: | |
137 | raise LibError, msg | |
138 | else: | |
139 | log.debug("skipping %s (up-to-date)", output_filename) | |
140 | ||
141 | def link(self, target_desc, objects, | |
142 | output_filename, output_dir=None, libraries=None, | |
143 | library_dirs=None, runtime_library_dirs=None, | |
144 | export_symbols=None, debug=0, extra_preargs=None, | |
145 | extra_postargs=None, build_temp=None, target_lang=None): | |
146 | objects, output_dir = self._fix_object_args(objects, output_dir) | |
147 | libraries, library_dirs, runtime_library_dirs = \ | |
148 | self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) | |
149 | ||
150 | lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, | |
151 | libraries) | |
152 | if type(output_dir) not in (StringType, NoneType): | |
153 | raise TypeError, "'output_dir' must be a string or None" | |
154 | if output_dir is not None: | |
155 | output_filename = os.path.join(output_dir, output_filename) | |
156 | ||
157 | if self._need_link(objects, output_filename): | |
158 | ld_args = (objects + self.objects + | |
159 | lib_opts + ['-o', output_filename]) | |
160 | if debug: | |
161 | ld_args[:0] = ['-g'] | |
162 | if extra_preargs: | |
163 | ld_args[:0] = extra_preargs | |
164 | if extra_postargs: | |
165 | ld_args.extend(extra_postargs) | |
166 | self.mkpath(os.path.dirname(output_filename)) | |
167 | try: | |
168 | if target_desc == CCompiler.EXECUTABLE: | |
169 | linker = self.linker_exe[:] | |
170 | else: | |
171 | linker = self.linker_so[:] | |
172 | if target_lang == "c++" and self.compiler_cxx: | |
173 | linker[0] = self.compiler_cxx[0] | |
174 | self.spawn(linker + ld_args) | |
175 | except DistutilsExecError, msg: | |
176 | raise LinkError, msg | |
177 | else: | |
178 | log.debug("skipping %s (up-to-date)", output_filename) | |
179 | ||
180 | # -- Miscellaneous methods ----------------------------------------- | |
181 | # These are all used by the 'gen_lib_options() function, in | |
182 | # ccompiler.py. | |
183 | ||
184 | def library_dir_option(self, dir): | |
185 | return "-L" + dir | |
186 | ||
187 | def runtime_library_dir_option(self, dir): | |
188 | # XXX Hackish, at the very least. See Python bug #445902: | |
189 | # http://sourceforge.net/tracker/index.php | |
190 | # ?func=detail&aid=445902&group_id=5470&atid=105470 | |
191 | # Linkers on different platforms need different options to | |
192 | # specify that directories need to be added to the list of | |
193 | # directories searched for dependencies when a dynamic library | |
194 | # is sought. GCC has to be told to pass the -R option through | |
195 | # to the linker, whereas other compilers just know this. | |
196 | # Other compilers may need something slightly different. At | |
197 | # this time, there's no way to determine this information from | |
198 | # the configuration data stored in the Python installation, so | |
199 | # we use this hack. | |
200 | compiler = os.path.basename(sysconfig.get_config_var("CC")) | |
201 | if sys.platform[:6] == "darwin": | |
202 | # MacOSX's linker doesn't understand the -R flag at all | |
203 | return "-L" + dir | |
204 | elif compiler[:3] == "gcc" or compiler[:3] == "g++": | |
205 | return "-Wl,-R" + dir | |
206 | else: | |
207 | return "-R" + dir | |
208 | ||
209 | def library_option(self, lib): | |
210 | return "-l" + lib | |
211 | ||
212 | def find_library_file(self, dirs, lib, debug=0): | |
213 | shared_f = self.library_filename(lib, lib_type='shared') | |
214 | dylib_f = self.library_filename(lib, lib_type='dylib') | |
215 | static_f = self.library_filename(lib, lib_type='static') | |
216 | ||
217 | for dir in dirs: | |
218 | shared = os.path.join(dir, shared_f) | |
219 | dylib = os.path.join(dir, dylib_f) | |
220 | static = os.path.join(dir, static_f) | |
221 | # We're second-guessing the linker here, with not much hard | |
222 | # data to go on: GCC seems to prefer the shared library, so I'm | |
223 | # assuming that *all* Unix C compilers do. And of course I'm | |
224 | # ignoring even GCC's "-static" option. So sue me. | |
225 | if os.path.exists(dylib): | |
226 | return dylib | |
227 | elif os.path.exists(shared): | |
228 | return shared | |
229 | elif os.path.exists(static): | |
230 | return static | |
231 | ||
232 | # Oops, didn't find it in *any* of 'dirs' | |
233 | return None |