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
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
17 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
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:
27 # The above copyright notice and this permission notice shall be included
28 # in all copies or substantial portions of the Software.
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.
39 __revision__ = "src/engine/SCons/Tool/__init__.py 3842 2008/12/20 22:59:52 scons"
48 import SCons.Scanner.C
49 import SCons.Scanner.D
50 import SCons.Scanner.LaTeX
51 import SCons.Scanner.Prog
55 CScanner = SCons.Scanner.C.CScanner()
56 DScanner = SCons.Scanner.D.DScanner()
57 LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
58 PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner()
59 ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
60 SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner')
62 CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
63 ".h", ".H", ".hxx", ".hpp", ".hh",
70 IDLSuffixes = [".idl", ".IDL"]
72 LaTeXSuffixes = [".tex", ".ltx", ".latex"]
74 for suffix in CSuffixes:
75 SourceFileScanner.add_scanner(suffix, CScanner)
77 for suffix in DSuffixes:
78 SourceFileScanner.add_scanner(suffix, DScanner)
80 # FIXME: what should be done here? Two scanners scan the same extensions,
81 # but look for different files, e.g., "picture.eps" vs. "picture.pdf".
82 # The builders for DVI and PDF explicitly reference their scanners
83 # I think that means this is not needed???
84 for suffix in LaTeXSuffixes:
85 SourceFileScanner.add_scanner(suffix, LaTeXScanner)
86 SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner)
89 def __init__(self, name, toolpath=[], **kw):
91 self.toolpath = toolpath + DefaultToolpath
92 # remember these so we can merge them into the call
95 module = self._tool_module()
96 self.generate = module.generate
97 self.exists = module.exists
98 if hasattr(module, 'options'):
99 self.options = module.options
101 def _tool_module(self):
102 # TODO: Interchange zipimport with normal initilization for better error reporting
103 oldpythonpath = sys.path
104 sys.path = self.toolpath + sys.path
108 file, path, desc = imp.find_module(self.name, self.toolpath)
110 return imp.load_module(self.name, file, path, desc)
114 except ImportError, e:
115 if str(e)!="No module named %s"%self.name:
116 raise SCons.Errors.EnvironmentError, e
122 for aPath in self.toolpath:
124 importer = zipimport.zipimporter(aPath)
125 return importer.load_module(self.name)
126 except ImportError, e:
129 sys.path = oldpythonpath
131 full_name = 'SCons.Tool.' + self.name
133 return sys.modules[full_name]
136 smpath = sys.modules['SCons.Tool'].__path__
138 file, path, desc = imp.find_module(self.name, smpath)
139 module = imp.load_module(full_name, file, path, desc)
140 setattr(SCons.Tool, self.name, module)
144 except ImportError, e:
145 if str(e)!="No module named %s"%self.name:
146 raise SCons.Errors.EnvironmentError, e
149 importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] )
150 module = importer.load_module(full_name)
151 setattr(SCons.Tool, self.name, module)
153 except ImportError, e:
154 m = "No tool named '%s': %s" % (self.name, e)
155 raise SCons.Errors.EnvironmentError, m
156 except ImportError, e:
157 m = "No tool named '%s': %s" % (self.name, e)
158 raise SCons.Errors.EnvironmentError, m
160 def __call__(self, env, *args, **kw):
161 if self.init_kw is not None:
162 # Merge call kws into init kws;
163 # but don't bash self.init_kw.
166 kw = self.init_kw.copy()
170 env.Append(TOOLS = [ self.name ])
171 if hasattr(self, 'options'):
172 import SCons.Variables
173 if not env.has_key('options'):
174 from SCons.Script import ARGUMENTS
175 env['options']=SCons.Variables.Variables(args=ARGUMENTS)
181 apply(self.generate, ( env, ) + args, kw)
186 ##########################################################################
187 # Create common executable program / library / object builders
189 def createProgBuilder(env):
190 """This is a utility function that creates the Program
191 Builder in an Environment if it is not there already.
193 If it is already there, we return the existing one.
197 program = env['BUILDERS']['Program']
199 import SCons.Defaults
200 program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction,
201 emitter = '$PROGEMITTER',
202 prefix = '$PROGPREFIX',
203 suffix = '$PROGSUFFIX',
204 src_suffix = '$OBJSUFFIX',
205 src_builder = 'Object',
206 target_scanner = ProgramScanner)
207 env['BUILDERS']['Program'] = program
211 def createStaticLibBuilder(env):
212 """This is a utility function that creates the StaticLibrary
213 Builder in an Environment if it is not there already.
215 If it is already there, we return the existing one.
219 static_lib = env['BUILDERS']['StaticLibrary']
221 action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
222 if env.Detect('ranlib'):
223 ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
224 action_list.append(ranlib_action)
226 static_lib = SCons.Builder.Builder(action = action_list,
227 emitter = '$LIBEMITTER',
228 prefix = '$LIBPREFIX',
229 suffix = '$LIBSUFFIX',
230 src_suffix = '$OBJSUFFIX',
231 src_builder = 'StaticObject')
232 env['BUILDERS']['StaticLibrary'] = static_lib
233 env['BUILDERS']['Library'] = static_lib
237 def createSharedLibBuilder(env):
238 """This is a utility function that creates the SharedLibrary
239 Builder in an Environment if it is not there already.
241 If it is already there, we return the existing one.
245 shared_lib = env['BUILDERS']['SharedLibrary']
247 import SCons.Defaults
248 action_list = [ SCons.Defaults.SharedCheck,
249 SCons.Defaults.ShLinkAction ]
250 shared_lib = SCons.Builder.Builder(action = action_list,
251 emitter = "$SHLIBEMITTER",
252 prefix = '$SHLIBPREFIX',
253 suffix = '$SHLIBSUFFIX',
254 target_scanner = ProgramScanner,
255 src_suffix = '$SHOBJSUFFIX',
256 src_builder = 'SharedObject')
257 env['BUILDERS']['SharedLibrary'] = shared_lib
261 def createLoadableModuleBuilder(env):
262 """This is a utility function that creates the LoadableModule
263 Builder in an Environment if it is not there already.
265 If it is already there, we return the existing one.
269 ld_module = env['BUILDERS']['LoadableModule']
271 import SCons.Defaults
272 action_list = [ SCons.Defaults.SharedCheck,
273 SCons.Defaults.LdModuleLinkAction ]
274 ld_module = SCons.Builder.Builder(action = action_list,
275 emitter = "$SHLIBEMITTER",
276 prefix = '$LDMODULEPREFIX',
277 suffix = '$LDMODULESUFFIX',
278 target_scanner = ProgramScanner,
279 src_suffix = '$SHOBJSUFFIX',
280 src_builder = 'SharedObject')
281 env['BUILDERS']['LoadableModule'] = ld_module
285 def createObjBuilders(env):
286 """This is a utility function that creates the StaticObject
287 and SharedObject Builders in an Environment if they
288 are not there already.
290 If they are there already, we return the existing ones.
292 This is a separate function because soooo many Tools
293 use this functionality.
295 The return is a 2-tuple of (StaticObject, SharedObject)
300 static_obj = env['BUILDERS']['StaticObject']
302 static_obj = SCons.Builder.Builder(action = {},
304 prefix = '$OBJPREFIX',
305 suffix = '$OBJSUFFIX',
306 src_builder = ['CFile', 'CXXFile'],
307 source_scanner = SourceFileScanner,
309 env['BUILDERS']['StaticObject'] = static_obj
310 env['BUILDERS']['Object'] = static_obj
313 shared_obj = env['BUILDERS']['SharedObject']
315 shared_obj = SCons.Builder.Builder(action = {},
317 prefix = '$SHOBJPREFIX',
318 suffix = '$SHOBJSUFFIX',
319 src_builder = ['CFile', 'CXXFile'],
320 source_scanner = SourceFileScanner,
322 env['BUILDERS']['SharedObject'] = shared_obj
324 return (static_obj, shared_obj)
326 def createCFileBuilders(env):
327 """This is a utility function that creates the CFile/CXXFile
328 Builders in an Environment if they
329 are not there already.
331 If they are there already, we return the existing ones.
333 This is a separate function because soooo many Tools
334 use this functionality.
336 The return is a 2-tuple of (CFile, CXXFile)
340 c_file = env['BUILDERS']['CFile']
342 c_file = SCons.Builder.Builder(action = {},
344 suffix = {None:'$CFILESUFFIX'})
345 env['BUILDERS']['CFile'] = c_file
347 env.SetDefault(CFILESUFFIX = '.c')
350 cxx_file = env['BUILDERS']['CXXFile']
352 cxx_file = SCons.Builder.Builder(action = {},
354 suffix = {None:'$CXXFILESUFFIX'})
355 env['BUILDERS']['CXXFile'] = cxx_file
356 env.SetDefault(CXXFILESUFFIX = '.cc')
358 return (c_file, cxx_file)
360 ##########################################################################
361 # Create common Java builders
363 def CreateJarBuilder(env):
365 java_jar = env['BUILDERS']['Jar']
367 fs = SCons.Node.FS.get_default_fs()
368 jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR')
369 java_jar = SCons.Builder.Builder(action = jar_com,
370 suffix = '$JARSUFFIX',
371 src_suffix = '$JAVACLASSSUFIX',
372 src_builder = 'JavaClassFile',
373 source_factory = fs.Entry)
374 env['BUILDERS']['Jar'] = java_jar
377 def CreateJavaHBuilder(env):
379 java_javah = env['BUILDERS']['JavaH']
381 fs = SCons.Node.FS.get_default_fs()
382 java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR')
383 java_javah = SCons.Builder.Builder(action = java_javah_com,
384 src_suffix = '$JAVACLASSSUFFIX',
385 target_factory = fs.Entry,
386 source_factory = fs.File,
387 src_builder = 'JavaClassFile')
388 env['BUILDERS']['JavaH'] = java_javah
391 def CreateJavaClassFileBuilder(env):
393 java_class_file = env['BUILDERS']['JavaClassFile']
395 fs = SCons.Node.FS.get_default_fs()
396 javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
397 java_class_file = SCons.Builder.Builder(action = javac_com,
399 #suffix = '$JAVACLASSSUFFIX',
400 src_suffix = '$JAVASUFFIX',
401 src_builder = ['JavaFile'],
402 target_factory = fs.Entry,
403 source_factory = fs.File)
404 env['BUILDERS']['JavaClassFile'] = java_class_file
405 return java_class_file
407 def CreateJavaClassDirBuilder(env):
409 java_class_dir = env['BUILDERS']['JavaClassDir']
411 fs = SCons.Node.FS.get_default_fs()
412 javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
413 java_class_dir = SCons.Builder.Builder(action = javac_com,
415 target_factory = fs.Dir,
416 source_factory = fs.Dir)
417 env['BUILDERS']['JavaClassDir'] = java_class_dir
418 return java_class_dir
420 def CreateJavaFileBuilder(env):
422 java_file = env['BUILDERS']['JavaFile']
424 java_file = SCons.Builder.Builder(action = {},
426 suffix = {None:'$JAVASUFFIX'})
427 env['BUILDERS']['JavaFile'] = java_file
428 env['JAVASUFFIX'] = '.java'
431 class ToolInitializerMethod:
433 This is added to a construction environment in place of a
434 method(s) normally called for a Builder (env.Object, env.StaticObject,
435 etc.). When called, it has its associated ToolInitializer
436 object search the specified list of tools and apply the first
437 one that exists to the construction environment. It then calls
438 whatever builder was (presumably) added to the construction
439 environment in place of this particular instance.
441 def __init__(self, name, initializer):
443 Note: we store the tool name as __name__ so it can be used by
444 the class that attaches this to a construction environment.
447 self.initializer = initializer
449 def get_builder(self, env):
451 Returns the appropriate real Builder for this method name
452 after having the associated ToolInitializer object apply
453 the appropriate Tool module.
455 builder = getattr(env, self.__name__)
457 self.initializer.apply_tools(env)
459 builder = getattr(env, self.__name__)
461 # There was no Builder added, which means no valid Tool
462 # for this name was found (or possibly there's a mismatch
463 # between the name we were called by and the Builder name
464 # added by the Tool module).
467 self.initializer.remove_methods(env)
471 def __call__(self, env, *args, **kw):
474 builder = self.get_builder(env)
477 return apply(builder, args, kw)
479 class ToolInitializer:
481 A class for delayed initialization of Tools modules.
483 Instances of this class associate a list of Tool modules with
484 a list of Builder method names that will be added by those Tool
485 modules. As part of instantiating this object for a particular
486 construction environment, we also add the appropriate
487 ToolInitializerMethod objects for the various Builder methods
488 that we want to use to delay Tool searches until necessary.
490 def __init__(self, env, tools, names):
491 if not SCons.Util.is_List(tools):
493 if not SCons.Util.is_List(names):
500 method = ToolInitializerMethod(name, self)
501 self.methods[name] = method
502 env.AddMethod(method)
504 def remove_methods(self, env):
506 Removes the methods that were added by the tool initialization
507 so we no longer copy and re-bind them when the construction
508 environment gets cloned.
510 for method in self.methods.values():
511 env.RemoveMethod(method)
513 def apply_tools(self, env):
515 Searches the list of associated Tool modules for one that
516 exists, and applies that to the construction environment.
519 tool = SCons.Tool.Tool(t)
524 # If we fall through here, there was no tool module found.
525 # This is where we can put an informative error message
526 # about the inability to find the tool. We'll start doing
527 # this as we cut over more pre-defined Builder+Tools to use
528 # the ToolInitializer class.
530 def Initializers(env):
531 ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs'])
532 def Install(self, *args, **kw):
533 return apply(self._InternalInstall, args, kw)
534 def InstallAs(self, *args, **kw):
535 return apply(self._InternalInstallAs, args, kw)
536 env.AddMethod(Install)
537 env.AddMethod(InstallAs)
539 def FindTool(tools, env):
546 def FindAllTools(tools, env):
547 def ToolExists(tool, env=env):
548 return Tool(tool).exists(env)
549 return filter (ToolExists, tools)
551 def tool_list(platform, env):
553 # XXX this logic about what tool to prefer on which platform
554 # should be moved into either the platform files or
555 # the tool files themselves.
556 # The search orders here are described in the man page. If you
557 # change these search orders, update the man page as well.
558 if str(platform) == 'win32':
559 "prefer Microsoft tools on Windows"
560 linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ]
561 c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ]
562 cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ]
563 assemblers = ['masm', 'nasm', 'gas', '386asm' ]
564 fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran']
565 ars = ['mslib', 'ar', 'tlib']
566 elif str(platform) == 'os2':
567 "prefer IBM tools on OS/2"
568 linkers = ['ilink', 'gnulink', 'mslink']
569 c_compilers = ['icc', 'gcc', 'msvc', 'cc']
570 cxx_compilers = ['icc', 'g++', 'msvc', 'c++']
571 assemblers = ['nasm', 'masm', 'gas']
572 fortran_compilers = ['ifl', 'g77']
573 ars = ['ar', 'mslib']
574 elif str(platform) == 'irix':
575 "prefer MIPSPro on IRIX"
576 linkers = ['sgilink', 'gnulink']
577 c_compilers = ['sgicc', 'gcc', 'cc']
578 cxx_compilers = ['sgic++', 'g++', 'c++']
579 assemblers = ['as', 'gas']
580 fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
582 elif str(platform) == 'sunos':
583 "prefer Forte tools on SunOS"
584 linkers = ['sunlink', 'gnulink']
585 c_compilers = ['suncc', 'gcc', 'cc']
586 cxx_compilers = ['sunc++', 'g++', 'c++']
587 assemblers = ['as', 'gas']
588 fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77',
589 'gfortran', 'g77', 'fortran']
591 elif str(platform) == 'hpux':
592 "prefer aCC tools on HP-UX"
593 linkers = ['hplink', 'gnulink']
594 c_compilers = ['hpcc', 'gcc', 'cc']
595 cxx_compilers = ['hpc++', 'g++', 'c++']
596 assemblers = ['as', 'gas']
597 fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
599 elif str(platform) == 'aix':
600 "prefer AIX Visual Age tools on AIX"
601 linkers = ['aixlink', 'gnulink']
602 c_compilers = ['aixcc', 'gcc', 'cc']
603 cxx_compilers = ['aixc++', 'g++', 'c++']
604 assemblers = ['as', 'gas']
605 fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran']
607 elif str(platform) == 'darwin':
608 "prefer GNU tools on Mac OS X, except for some linkers and IBM tools"
609 linkers = ['applelink', 'gnulink']
610 c_compilers = ['gcc', 'cc']
611 cxx_compilers = ['g++', 'c++']
613 fortran_compilers = ['gfortran', 'f95', 'f90', 'g77']
616 "prefer GNU tools on all other platforms"
617 linkers = ['gnulink', 'mslink', 'ilink']
618 c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc']
619 cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++']
620 assemblers = ['gas', 'nasm', 'masm']
621 fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77']
622 ars = ['ar', 'mslib']
624 c_compiler = FindTool(c_compilers, env) or c_compilers[0]
626 # XXX this logic about what tool provides what should somehow be
627 # moved into the tool files themselves.
628 if c_compiler and c_compiler == 'mingw':
629 # MinGW contains a linker, C compiler, C++ compiler,
630 # Fortran compiler, archiver and assembler:
634 fortran_compiler = None
637 # Don't use g++ if the C compiler has built-in C++ support:
638 if c_compiler in ('msvc', 'intelc', 'icc'):
641 cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0]
642 linker = FindTool(linkers, env) or linkers[0]
643 assembler = FindTool(assemblers, env) or assemblers[0]
644 fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0]
645 ar = FindTool(ars, env) or ars[0]
647 other_tools = FindAllTools(['BitKeeper', 'CVS',
650 'dvipdf', 'dvips', 'gs',
651 'jar', 'javac', 'javah',
653 'm4', 'midl', 'msvs',
654 'pdflatex', 'pdftex', 'Perforce',
655 'RCS', 'rmic', 'rpcgen',
660 'yacc', 'zip', 'rpm', 'wix'],
663 tools = ([linker, c_compiler, cxx_compiler,
664 fortran_compiler, assembler, ar]
667 return filter(lambda x: x, tools)