Merged revisions 1540-1545,1547-1581 via svnmerge from
[scons.git] / src / engine / SCons / Tool / __init__.py
1 """SCons.Tool
2
3 SCons tool selection.
4
5 This looks for modules that define a callable object that can modify
6 a construction environment as appropriate for a given tool (or tool
7 chain).
8
9 Note that because this subsystem just *selects* a callable that can
10 modify a construction environment, it's possible for people to define
11 their own "tool specification" in an arbitrary callable function.  No
12 one needs to use or tie in to this subsystem in order to roll their own
13 tool definition.
14 """
15
16 #
17 # __COPYRIGHT__
18
19 # Permission is hereby granted, free of charge, to any person obtaining
20 # a copy of this software and associated documentation files (the
21 # "Software"), to deal in the Software without restriction, including
22 # without limitation the rights to use, copy, modify, merge, publish,
23 # distribute, sublicense, and/or sell copies of the Software, and to
24 # permit persons to whom the Software is furnished to do so, subject to
25 # the following conditions:
26 #
27 # The above copyright notice and this permission notice shall be included
28 # in all copies or substantial portions of the Software.
29 #
30 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
31 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
32 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
34 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
35 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
36 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 #
38
39 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
40
41 import imp
42 import sys
43
44 import SCons.Errors
45 import SCons.Scanner
46 import SCons.Scanner.C
47 import SCons.Scanner.D
48 import SCons.Scanner.LaTeX
49 import SCons.Scanner.Prog
50
51 CScanner = SCons.Scanner.C.CScanner()
52 DScanner = SCons.Scanner.D.DScanner()
53 LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
54 ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
55 SourceFileScanner = SCons.Scanner.Scanner({}, name='SourceFileScanner')
56
57 CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
58              ".h", ".H", ".hxx", ".hpp", ".hh",
59              ".F", ".fpp", ".FPP",
60              ".m", ".mm",
61              ".S", ".spp", ".SPP"]
62
63 DSuffixes = ['.d']
64
65 IDLSuffixes = [".idl", ".IDL"]
66
67 LaTeXSuffixes = [".tex", ".ltx", ".latex"]
68
69 for suffix in CSuffixes:
70     SourceFileScanner.add_scanner(suffix, CScanner)
71
72 for suffix in DSuffixes:
73     SourceFileScanner.add_scanner(suffix, DScanner)
74
75 for suffix in LaTeXSuffixes:
76      SourceFileScanner.add_scanner(suffix, LaTeXScanner)
77
78 class Tool:
79     def __init__(self, name, toolpath=[], **kw):
80         self.name = name
81         self.toolpath = toolpath
82         # remember these so we can merge them into the call
83         self.init_kw = kw
84
85         module = self._tool_module()
86         self.generate = module.generate
87         self.exists = module.exists
88
89     def _tool_module(self):
90         oldpythonpath = sys.path
91         sys.path = self.toolpath + sys.path
92
93         try:
94             try:
95                 file, path, desc = imp.find_module(self.name, self.toolpath)
96                 try:
97                     return imp.load_module(self.name, file, path, desc)
98                 finally:
99                     if file:
100                         file.close()
101             except ImportError, e:
102                 try:
103                     import zipimport
104                 except ImportError:
105                     pass
106                 else:
107                     for aPath in self.toolpath:
108                         try:
109                             importer = zipimport.zipimporter(aPath)
110                             return importer.load_module(self.name)
111                         except ImportError, e:
112                             pass
113         finally:
114             sys.path = oldpythonpath
115
116         full_name = 'SCons.Tool.' + self.name
117         try:
118             return sys.modules[full_name]
119         except KeyError:
120             try:
121                 smpath = sys.modules['SCons.Tool'].__path__
122                 try:
123                     file, path, desc = imp.find_module(self.name, smpath)
124                     module = imp.load_module(full_name, file, path, desc)
125                     setattr(SCons.Tool, self.name, module)
126                     if file:
127                         file.close()
128                     return module
129                 except ImportError, e:
130                     try:
131                         import zipimport
132                         importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] )
133                         module = importer.load_module(full_name)
134                         setattr(SCons.Tool, self.name, module)
135                         return module
136                     except ImportError, e:
137                         m = "No tool named '%s': %s" % (self.name, e)
138                         raise SCons.Errors.UserError, m
139             except ImportError, e:
140                 m = "No tool named '%s': %s" % (self.name, e)
141                 raise SCons.Errors.UserError, m
142
143     def __call__(self, env, *args, **kw):
144         if self.init_kw is not None:
145             # Merge call kws into init kws;
146             # but don't bash self.init_kw.
147             if kw is not None:
148                 call_kw = kw
149                 kw = self.init_kw.copy()
150                 kw.update(call_kw)
151             else:
152                 kw = self.init_kw
153         env.Append(TOOLS = [ self.name ])
154         apply(self.generate, ( env, ) + args, kw)
155
156     def __str__(self):
157         return self.name
158
159 def createProgBuilder(env):
160     """This is a utility function that creates the Program
161     Builder in an Environment if it is not there already.
162
163     If it is already there, we return the existing one.
164     """
165
166     try:
167         program = env['BUILDERS']['Program']
168     except KeyError:
169         import SCons.Defaults
170         program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction,
171                                         emitter = '$PROGEMITTER',
172                                         prefix = '$PROGPREFIX',
173                                         suffix = '$PROGSUFFIX',
174                                         src_suffix = '$OBJSUFFIX',
175                                         src_builder = 'Object',
176                                         target_scanner = ProgramScanner)
177         env['BUILDERS']['Program'] = program
178
179     return program
180
181 def createStaticLibBuilder(env):
182     """This is a utility function that creates the StaticLibrary
183     Builder in an Environment if it is not there already.
184
185     If it is already there, we return the existing one.
186     """
187
188     try:
189         static_lib = env['BUILDERS']['StaticLibrary']
190     except KeyError:
191         action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
192         if env.Detect('ranlib'):
193             ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
194             action_list.append(ranlib_action)
195
196         static_lib = SCons.Builder.Builder(action = action_list,
197                                            emitter = '$LIBEMITTER',
198                                            prefix = '$LIBPREFIX',
199                                            suffix = '$LIBSUFFIX',
200                                            src_suffix = '$OBJSUFFIX',
201                                            src_builder = 'StaticObject')
202         env['BUILDERS']['StaticLibrary'] = static_lib
203         env['BUILDERS']['Library'] = static_lib
204
205     return static_lib
206
207 def createSharedLibBuilder(env):
208     """This is a utility function that creates the SharedLibrary
209     Builder in an Environment if it is not there already.
210
211     If it is already there, we return the existing one.
212     """
213
214     try:
215         shared_lib = env['BUILDERS']['SharedLibrary']
216     except KeyError:
217         import SCons.Defaults
218         action_list = [ SCons.Defaults.SharedCheck,
219                         SCons.Defaults.ShLinkAction ]
220         shared_lib = SCons.Builder.Builder(action = action_list,
221                                            emitter = "$SHLIBEMITTER",
222                                            prefix = '$SHLIBPREFIX',
223                                            suffix = '$SHLIBSUFFIX',
224                                            target_scanner = ProgramScanner,
225                                            src_suffix = '$SHOBJSUFFIX',
226                                            src_builder = 'SharedObject')
227         env['BUILDERS']['SharedLibrary'] = shared_lib
228
229     return shared_lib
230
231 def createLoadableModuleBuilder(env):
232     """This is a utility function that creates the LoadableModule
233     Builder in an Environment if it is not there already.
234
235     If it is already there, we return the existing one.
236     """
237
238     try:
239         ld_module = env['BUILDERS']['LoadableModule']
240     except KeyError:
241         import SCons.Defaults
242         action_list = [ SCons.Defaults.SharedCheck,
243                         SCons.Defaults.LdModuleLinkAction ]
244         ld_module = SCons.Builder.Builder(action = action_list,
245                                           emitter = "$SHLIBEMITTER",
246                                           prefix = '$LDMODULEPREFIX',
247                                           suffix = '$LDMODULESUFFIX',
248                                           target_scanner = ProgramScanner,
249                                           src_suffix = '$SHOBJSUFFIX',
250                                           src_builder = 'SharedObject')
251         env['BUILDERS']['LoadableModule'] = ld_module
252
253     return ld_module
254
255 def createObjBuilders(env):
256     """This is a utility function that creates the StaticObject
257     and SharedObject Builders in an Environment if they
258     are not there already.
259
260     If they are there already, we return the existing ones.
261
262     This is a separate function because soooo many Tools
263     use this functionality.
264
265     The return is a 2-tuple of (StaticObject, SharedObject)
266     """
267
268
269     try:
270         static_obj = env['BUILDERS']['StaticObject']
271     except KeyError:
272         static_obj = SCons.Builder.Builder(action = {},
273                                            emitter = {},
274                                            prefix = '$OBJPREFIX',
275                                            suffix = '$OBJSUFFIX',
276                                            src_builder = ['CFile', 'CXXFile'],
277                                            source_scanner = SourceFileScanner,
278                                            single_source = 1)
279         env['BUILDERS']['StaticObject'] = static_obj
280         env['BUILDERS']['Object'] = static_obj
281
282     try:
283         shared_obj = env['BUILDERS']['SharedObject']
284     except KeyError:
285         shared_obj = SCons.Builder.Builder(action = {},
286                                            emitter = {},
287                                            prefix = '$SHOBJPREFIX',
288                                            suffix = '$SHOBJSUFFIX',
289                                            src_builder = ['CFile', 'CXXFile'],
290                                            source_scanner = SourceFileScanner,
291                                            single_source = 1)
292         env['BUILDERS']['SharedObject'] = shared_obj
293
294     return (static_obj, shared_obj)
295
296 def createCFileBuilders(env):
297     """This is a utility function that creates the CFile/CXXFile
298     Builders in an Environment if they
299     are not there already.
300
301     If they are there already, we return the existing ones.
302
303     This is a separate function because soooo many Tools
304     use this functionality.
305
306     The return is a 2-tuple of (CFile, CXXFile)
307     """
308
309     try:
310         c_file = env['BUILDERS']['CFile']
311     except KeyError:
312         c_file = SCons.Builder.Builder(action = {},
313                                        emitter = {},
314                                        suffix = {None:'$CFILESUFFIX'})
315         env['BUILDERS']['CFile'] = c_file
316         env['CFILESUFFIX'] = '.c'
317
318     try:
319         cxx_file = env['BUILDERS']['CXXFile']
320     except KeyError:
321         cxx_file = SCons.Builder.Builder(action = {},
322                                          emitter = {},
323                                          suffix = {None:'$CXXFILESUFFIX'})
324         env['BUILDERS']['CXXFile'] = cxx_file
325         env['CXXFILESUFFIX'] = '.cc'
326
327     return (c_file, cxx_file)
328
329 def FindTool(tools, env):
330     for tool in tools:
331         t = Tool(tool)
332         if t.exists(env):
333             return tool
334     return None
335
336 def FindAllTools(tools, env):
337     def ToolExists(tool, env=env):
338         return Tool(tool).exists(env)
339     return filter (ToolExists, tools)
340              
341 def tool_list(platform, env):
342
343     # XXX this logic about what tool to prefer on which platform
344     #     should be moved into either the platform files or
345     #     the tool files themselves.
346     # The search orders here are described in the man page.  If you
347     # change these search orders, update the man page as well.
348     if str(platform) == 'win32':
349         "prefer Microsoft tools on Windows"
350         linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ]
351         c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ]
352         cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ]
353         assemblers = ['masm', 'nasm', 'gas', '386asm' ]
354         fortran_compilers = ['g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran']
355         ars = ['mslib', 'ar', 'tlib']
356     elif str(platform) == 'os2':
357         "prefer IBM tools on OS/2"
358         linkers = ['ilink', 'gnulink', 'mslink']
359         c_compilers = ['icc', 'gcc', 'msvc', 'cc']
360         cxx_compilers = ['icc', 'g++', 'msvc', 'c++']
361         assemblers = ['nasm', 'masm', 'gas']
362         fortran_compilers = ['ifl', 'g77']
363         ars = ['ar', 'mslib']
364     elif str(platform) == 'irix':
365         "prefer MIPSPro on IRIX"
366         linkers = ['sgilink', 'gnulink']
367         c_compilers = ['sgicc', 'gcc', 'cc']
368         cxx_compilers = ['sgic++', 'g++', 'c++']
369         assemblers = ['as', 'gas']
370         fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
371         ars = ['sgiar']
372     elif str(platform) == 'sunos':
373         "prefer Forte tools on SunOS"
374         linkers = ['sunlink', 'gnulink']
375         c_compilers = ['suncc', 'gcc', 'cc']
376         cxx_compilers = ['sunc++', 'g++', 'c++']
377         assemblers = ['as', 'gas']
378         fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
379         ars = ['sunar']
380     elif str(platform) == 'hpux':
381         "prefer aCC tools on HP-UX"
382         linkers = ['hplink', 'gnulink']
383         c_compilers = ['hpcc', 'gcc', 'cc']
384         cxx_compilers = ['hpc++', 'g++', 'c++']
385         assemblers = ['as', 'gas']
386         fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
387         ars = ['ar']
388     elif str(platform) == 'aix':
389         "prefer AIX Visual Age tools on AIX"
390         linkers = ['aixlink', 'gnulink']
391         c_compilers = ['aixcc', 'gcc', 'cc']
392         cxx_compilers = ['aixc++', 'g++', 'c++']
393         assemblers = ['as', 'gas']
394         fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran']
395         ars = ['ar']
396     elif str(platform) == 'darwin':
397         "prefer GNU tools on Mac OS X, except for some linkers and IBM tools"
398         linkers = ['applelink', 'gnulink']
399         c_compilers = ['gcc', 'cc']
400         cxx_compilers = ['g++', 'c++']
401         assemblers = ['as']
402         fortran_compilers = ['f95', 'f90', 'g77']
403         ars = ['ar']
404     else:
405         "prefer GNU tools on all other platforms"
406         linkers = ['gnulink', 'mslink', 'ilink']
407         c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc']
408         cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++']
409         assemblers = ['gas', 'nasm', 'masm']
410         fortran_compilers = ['f95', 'f90', 'g77', 'ifort', 'ifl', 'fortran']
411         ars = ['ar', 'mslib']
412
413     c_compiler = FindTool(c_compilers, env) or c_compilers[0]
414  
415     # XXX this logic about what tool provides what should somehow be
416     #     moved into the tool files themselves.
417     if c_compiler and c_compiler == 'mingw':
418         # MinGW contains a linker, C compiler, C++ compiler, 
419         # Fortran compiler, archiver and assembler:
420         cxx_compiler = None
421         linker = None
422         assembler = None
423         fortran_compiler = None
424         ar = None
425     else:
426         # Don't use g++ if the C compiler has built-in C++ support:
427         if c_compiler in ('msvc', 'intelc', 'icc'):
428             cxx_compiler = None
429         else:
430             cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0]
431         linker = FindTool(linkers, env) or linkers[0]
432         assembler = FindTool(assemblers, env) or assemblers[0]
433         fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0]
434         ar = FindTool(ars, env) or ars[0]
435
436     other_tools = FindAllTools(['BitKeeper', 'CVS',
437                                 'dmd',
438                                 'dvipdf', 'dvips', 'gs',
439                                 'jar', 'javac', 'javah',
440                                 'latex', 'lex',
441                                 'm4', 'midl', 'msvs',
442                                 'pdflatex', 'pdftex', 'Perforce',
443                                 'RCS', 'rmic', 'rpcgen',
444                                 'SCCS',
445                                 # 'Subversion',
446                                 'swig',
447                                 'tar', 'tex',
448                                 'yacc', 'zip'],
449                                env)
450
451     tools = ([linker, c_compiler, cxx_compiler,
452               fortran_compiler, assembler, ar]
453              + other_tools)
454     
455     return filter(lambda x: x, tools)