97da344cd6216cb4dbb518fa7c83591b13bbfe91
[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 subsysem 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.Defaults
46
47 class ToolSpec:
48     def __init__(self, name):
49         self.name = name
50
51     def __call__(self, env, *args, **kw):
52         env.Append(TOOLS = [ self.name ])
53         apply(self.generate, ( env, ) + args, kw)
54
55     def __str__(self):
56         return self.name
57     
58 def Tool(name, toolpath=[]):
59     "Select a canned Tool specification, optionally searching in toolpath."
60
61     try:
62         file, path, desc = imp.find_module(name, toolpath)
63         try:
64             module = imp.load_module(name, file, path, desc)
65             spec = ToolSpec(name)
66             spec.generate = module.generate
67             spec.exists = module.exists
68             return spec
69         finally:
70             if file:
71                 file.close()
72     except ImportError, e:
73         pass
74     
75     full_name = 'SCons.Tool.' + name
76     if not sys.modules.has_key(full_name):
77         try:
78             file, path, desc = imp.find_module(name,
79                                         sys.modules['SCons.Tool'].__path__)
80             mod = imp.load_module(full_name, file, path, desc)
81             setattr(SCons.Tool, name, mod)
82         except ImportError, e:
83             raise SCons.Errors.UserError, "No tool named '%s': %s" % (name, e)
84         if file:
85             file.close()
86     spec = ToolSpec(name)
87     spec.generate = sys.modules[full_name].generate
88     spec.exists = sys.modules[full_name].exists
89     return spec
90
91 def createProgBuilder(env):
92     """This is a utility function that creates the Program
93     Builder in an Environment if it is not there already.
94
95     If it is already there, we return the existing one.
96     """
97
98     try:
99         program = env['BUILDERS']['Program']
100     except KeyError:
101         program = SCons.Builder.Builder(action = '$LINKCOM',
102                                         emitter = '$PROGEMITTER',
103                                         prefix = '$PROGPREFIX',
104                                         suffix = '$PROGSUFFIX',
105                                         src_suffix = '$OBJSUFFIX',
106                                         src_builder = 'Object',
107                                         scanner = SCons.Defaults.ProgScan)
108         env['BUILDERS']['Program'] = program
109
110     return program
111
112 def createStaticLibBuilder(env):
113     """This is a utility function that creates the StaticLibrary
114     Builder in an Environment if it is not there already.
115
116     If it is already there, we return the existing one.
117     """
118
119     try:
120         static_lib = env['BUILDERS']['StaticLibrary']
121     except KeyError:
122         static_lib = SCons.Builder.Builder(action = "$ARCOM",
123                                            emitter = '$LIBEMITTER',
124                                            prefix = '$LIBPREFIX',
125                                            suffix = '$LIBSUFFIX',
126                                            src_suffix = '$OBJSUFFIX',
127                                            src_builder = 'StaticObject')
128         env['BUILDERS']['StaticLibrary'] = static_lib
129         env['BUILDERS']['Library'] = static_lib
130
131     return static_lib
132
133 def createSharedLibBuilder(env):
134     """This is a utility function that creates the SharedLibrary
135     Builder in an Environment if it is not there already.
136
137     If it is already there, we return the existing one.
138     """
139
140     try:
141         shared_lib = env['BUILDERS']['SharedLibrary']
142     except KeyError:
143         action_list = [ SCons.Defaults.SharedCheck, "$SHLINKCOM" ]
144         shared_lib = SCons.Builder.Builder(action = action_list,
145                                            emitter = "$SHLIBEMITTER",
146                                            prefix = '$SHLIBPREFIX',
147                                            suffix = '$SHLIBSUFFIX',
148                                            scanner = SCons.Defaults.ProgScan,
149                                            src_suffix = '$SHOBJSUFFIX',
150                                            src_builder = 'SharedObject')
151         env['BUILDERS']['SharedLibrary'] = shared_lib
152
153     return shared_lib
154
155 def createObjBuilders(env):
156     """This is a utility function that creates the StaticObject
157     and SharedObject Builders in an Environment if they
158     are not there already.
159
160     If they are there already, we return the existing ones.
161
162     This is a separate function because soooo many Tools
163     use this functionality.
164
165     The return is a 2-tuple of (StaticObject, SharedObject)
166     """
167
168     try:
169         static_obj = env['BUILDERS']['StaticObject']
170     except KeyError:
171         static_obj = SCons.Builder.Builder(action = {},
172                                            emitter = "$OBJEMITTER",
173                                            prefix = '$OBJPREFIX',
174                                            suffix = '$OBJSUFFIX',
175                                            src_builder = ['CFile', 'CXXFile'])
176         env['BUILDERS']['StaticObject'] = static_obj
177         env['BUILDERS']['Object'] = static_obj
178         env['OBJEMITTER'] = SCons.Defaults.StaticObjectEmitter
179
180     try:
181         shared_obj = env['BUILDERS']['SharedObject']
182     except KeyError:
183         shared_obj = SCons.Builder.Builder(action = {},
184                                            emitter = "$SHOBJEMITTER",
185                                            prefix = '$SHOBJPREFIX',
186                                            suffix = '$SHOBJSUFFIX',
187                                            src_builder = ['CFile', 'CXXFile'])
188         env['BUILDERS']['SharedObject'] = shared_obj
189         env['SHOBJEMITTER'] = SCons.Defaults.SharedObjectEmitter
190
191     return (static_obj, shared_obj)
192
193 def createCFileBuilders(env):
194     """This is a utility function that creates the CFile/CXXFile
195     Builders in an Environment if they
196     are not there already.
197
198     If they are there already, we return the existing ones.
199
200     This is a separate function because soooo many Tools
201     use this functionality.
202
203     The return is a 2-tuple of (CFile, CXXFile)
204     """
205
206     try:
207         c_file = env['BUILDERS']['CFile']
208     except KeyError:
209         c_file = SCons.Builder.Builder(action = {},
210                                        emitter = {},
211                                        suffix = {None:'$CFILESUFFIX'})
212         env['BUILDERS']['CFile'] = c_file
213         env['CFILESUFFIX'] = '.c'
214
215     try:
216         cxx_file = env['BUILDERS']['CXXFile']
217     except KeyError:
218         cxx_file = SCons.Builder.Builder(action = {},
219                                          emitter = {},
220                                          suffix = {None:'$CXXFILESUFFIX'})
221         env['BUILDERS']['CXXFile'] = cxx_file
222         env['CXXFILESUFFIX'] = '.cc'
223
224     return (c_file, cxx_file)
225
226 def FindTool(tools, env):
227     for tool in tools:
228         t = Tool(tool)
229         if t.exists(env):
230             return tool
231     return None
232
233 def FindAllTools(tools, env):
234     def ToolExists(tool, env=env):
235         return Tool(tool).exists(env)
236     return filter (ToolExists, tools)
237              
238 def tool_list(platform, env):
239
240     # XXX this logic about what tool to prefer on which platform
241     #     should be moved into either the platform files or
242     #     the tool files themselves.
243     # The search orders here are described in the man page.  If you
244     # change these search orders, update the man page as well.
245     if str(platform) == 'win32':
246         "prefer Microsoft tools on Windows"
247         linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ]
248         c_compilers = ['msvc', 'mingw', 'gcc', 'icl', 'icc', 'cc', 'bcc32' ]
249         cxx_compilers = ['msvc', 'icc', 'g++', 'c++', 'bcc32' ]
250         assemblers = ['masm', 'nasm', 'gas', '386asm' ]
251         fortran_compilers = ['g77', 'ifl']
252         ars = ['mslib', 'ar', 'tlib']
253     elif str(platform) == 'os2':
254         "prefer IBM tools on OS/2"
255         linkers = ['ilink', 'gnulink', 'mslink']
256         c_compilers = ['icc', 'gcc', 'msvc', 'cc']
257         cxx_compilers = ['icc', 'g++', 'msvc', 'c++']
258         assemblers = ['nasm', 'masm', 'gas']
259         fortran_compilers = ['ifl', 'g77']
260         ars = ['ar', 'mslib']
261     elif str(platform) == 'irix':
262         "prefer MIPSPro on IRIX"
263         linkers = ['sgilink', 'gnulink']
264         c_compilers = ['sgicc', 'gcc', 'cc']
265         cxx_compilers = ['sgic++', 'g++', 'c++']
266         assemblers = ['as', 'gas']
267         fortran_compilers = ['f77', 'g77']
268         ars = ['sgiar']
269     elif str(platform) == 'sunos':
270         "prefer Forte tools on SunOS"
271         linkers = ['sunlink', 'gnulink']
272         c_compilers = ['suncc', 'gcc', 'cc']
273         cxx_compilers = ['sunc++', 'g++', 'c++']
274         assemblers = ['as', 'gas']
275         fortran_compilers = ['f77', 'g77']
276         ars = ['sunar']
277     elif str(platform) == 'hpux':
278         "prefer aCC tools on HP-UX"
279         linkers = ['hplink', 'gnulink']
280         c_compilers = ['hpcc', 'gcc', 'cc']
281         cxx_compilers = ['hpc++', 'g++', 'c++']
282         assemblers = ['as', 'gas']
283         fortran_compilers = ['f77', 'g77']
284         ars = ['ar']
285     elif str(platform) == 'aix':
286         "prefer AIX Visual Age tools on AIX"
287         linkers = ['aixlink', 'gnulink']
288         c_compilers = ['aixcc', 'gcc', 'cc']
289         cxx_compilers = ['aixc++', 'g++', 'c++']
290         assemblers = ['as', 'gas']
291         fortran_compilers = ['aixf77', 'g77']
292         ars = ['ar']
293     else:
294         "prefer GNU tools on all other platforms"
295         linkers = ['gnulink', 'mslink', 'ilink']
296         c_compilers = ['gcc', 'msvc', 'icc', 'cc']
297         cxx_compilers = ['g++', 'msvc', 'icc', 'c++']
298         assemblers = ['gas', 'nasm', 'masm']
299         fortran_compilers = ['g77', 'ifl']
300         ars = ['ar', 'mslib']
301
302     c_compiler = FindTool(c_compilers, env) or c_compilers[0]
303  
304     # XXX this logic about what tool provides what should somehow be
305     #     moved into the tool files themselves.
306     if c_compiler and c_compiler == 'mingw':
307         # MinGW contains a linker, C compiler, C++ compiler, 
308         # Fortran compiler, archiver and assembler:
309         cxx_compiler = None
310         linker = None
311         assembler = None
312         fortran_compiler = None
313         ar = None
314     else:
315         # Don't use g++ if the C compiler has built-in C++ support:
316         if c_compiler in ('msvc', 'icc'):
317             cxx_compiler = None
318         else:
319             cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0]
320         linker = FindTool(linkers, env) or linkers[0]
321         assembler = FindTool(assemblers, env) or assemblers[0]
322         fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0]
323         ar = FindTool(ars, env) or ars[0]
324
325     other_tools = FindAllTools(['BitKeeper', 'CVS',
326                                 'dvipdf', 'dvips', 'gs',
327                                 'jar', 'javac', 'javah',
328                                 'latex', 'lex', 'm4', 'midl', 'msvs',
329                                 'pdflatex', 'pdftex', 'Perforce',
330                                 'RCS', 'rmic', 'SCCS',
331                                 # 'Subversion',
332                                 'swig',
333                                 'tar', 'tex', 'yacc', 'zip'],
334                                env)
335
336     tools = ([linker, c_compiler, cxx_compiler,
337               fortran_compiler, assembler, ar]
338              + other_tools)
339     
340     return filter(lambda x: x, tools)