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, 2009, 2010 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.
38 __revision__ = "src/engine/SCons/Tool/__init__.py 5023 2010/06/14 22:05:46 scons"
47 import SCons.Scanner.C
48 import SCons.Scanner.D
49 import SCons.Scanner.LaTeX
50 import SCons.Scanner.Prog
54 CScanner = SCons.Scanner.C.CScanner()
55 DScanner = SCons.Scanner.D.DScanner()
56 LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
57 PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner()
58 ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
59 SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner')
61 CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
62 ".h", ".H", ".hxx", ".hpp", ".hh",
69 IDLSuffixes = [".idl", ".IDL"]
71 LaTeXSuffixes = [".tex", ".ltx", ".latex"]
73 for suffix in CSuffixes:
74 SourceFileScanner.add_scanner(suffix, CScanner)
76 for suffix in DSuffixes:
77 SourceFileScanner.add_scanner(suffix, DScanner)
79 # FIXME: what should be done here? Two scanners scan the same extensions,
80 # but look for different files, e.g., "picture.eps" vs. "picture.pdf".
81 # The builders for DVI and PDF explicitly reference their scanners
82 # I think that means this is not needed???
83 for suffix in LaTeXSuffixes:
84 SourceFileScanner.add_scanner(suffix, LaTeXScanner)
85 SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner)
88 def __init__(self, name, toolpath=[], **kw):
90 self.toolpath = toolpath + DefaultToolpath
91 # remember these so we can merge them into the call
94 module = self._tool_module()
95 self.generate = module.generate
96 self.exists = module.exists
97 if hasattr(module, 'options'):
98 self.options = module.options
100 def _tool_module(self):
101 # TODO: Interchange zipimport with normal initilization for better error reporting
102 oldpythonpath = sys.path
103 sys.path = self.toolpath + sys.path
107 file, path, desc = imp.find_module(self.name, self.toolpath)
109 return imp.load_module(self.name, file, path, desc)
113 except ImportError, e:
114 if str(e)!="No module named %s"%self.name:
115 raise SCons.Errors.EnvironmentError(e)
121 for aPath in self.toolpath:
123 importer = zipimport.zipimporter(aPath)
124 return importer.load_module(self.name)
125 except ImportError, e:
128 sys.path = oldpythonpath
130 full_name = 'SCons.Tool.' + self.name
132 return sys.modules[full_name]
135 smpath = sys.modules['SCons.Tool'].__path__
137 file, path, desc = imp.find_module(self.name, smpath)
138 module = imp.load_module(full_name, file, path, desc)
139 setattr(SCons.Tool, self.name, module)
143 except ImportError, e:
144 if str(e)!="No module named %s"%self.name:
145 raise SCons.Errors.EnvironmentError(e)
148 importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] )
149 module = importer.load_module(full_name)
150 setattr(SCons.Tool, self.name, module)
152 except ImportError, e:
153 m = "No tool named '%s': %s" % (self.name, e)
154 raise SCons.Errors.EnvironmentError(m)
155 except ImportError, e:
156 m = "No tool named '%s': %s" % (self.name, e)
157 raise SCons.Errors.EnvironmentError(m)
159 def __call__(self, env, *args, **kw):
160 if self.init_kw is not None:
161 # Merge call kws into init kws;
162 # but don't bash self.init_kw.
165 kw = self.init_kw.copy()
169 env.Append(TOOLS = [ self.name ])
170 if hasattr(self, 'options'):
171 import SCons.Variables
172 if 'options' not in env:
173 from SCons.Script import ARGUMENTS
174 env['options']=SCons.Variables.Variables(args=ARGUMENTS)
180 self.generate(env, *args, **kw)
185 ##########################################################################
186 # Create common executable program / library / object builders
188 def createProgBuilder(env):
189 """This is a utility function that creates the Program
190 Builder in an Environment if it is not there already.
192 If it is already there, we return the existing one.
196 program = env['BUILDERS']['Program']
198 import SCons.Defaults
199 program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction,
200 emitter = '$PROGEMITTER',
201 prefix = '$PROGPREFIX',
202 suffix = '$PROGSUFFIX',
203 src_suffix = '$OBJSUFFIX',
204 src_builder = 'Object',
205 target_scanner = ProgramScanner)
206 env['BUILDERS']['Program'] = program
210 def createStaticLibBuilder(env):
211 """This is a utility function that creates the StaticLibrary
212 Builder in an Environment if it is not there already.
214 If it is already there, we return the existing one.
218 static_lib = env['BUILDERS']['StaticLibrary']
220 action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
221 if env.Detect('ranlib'):
222 ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
223 action_list.append(ranlib_action)
225 static_lib = SCons.Builder.Builder(action = action_list,
226 emitter = '$LIBEMITTER',
227 prefix = '$LIBPREFIX',
228 suffix = '$LIBSUFFIX',
229 src_suffix = '$OBJSUFFIX',
230 src_builder = 'StaticObject')
231 env['BUILDERS']['StaticLibrary'] = static_lib
232 env['BUILDERS']['Library'] = static_lib
236 def createSharedLibBuilder(env):
237 """This is a utility function that creates the SharedLibrary
238 Builder in an Environment if it is not there already.
240 If it is already there, we return the existing one.
244 shared_lib = env['BUILDERS']['SharedLibrary']
246 import SCons.Defaults
247 action_list = [ SCons.Defaults.SharedCheck,
248 SCons.Defaults.ShLinkAction ]
249 shared_lib = SCons.Builder.Builder(action = action_list,
250 emitter = "$SHLIBEMITTER",
251 prefix = '$SHLIBPREFIX',
252 suffix = '$SHLIBSUFFIX',
253 target_scanner = ProgramScanner,
254 src_suffix = '$SHOBJSUFFIX',
255 src_builder = 'SharedObject')
256 env['BUILDERS']['SharedLibrary'] = shared_lib
260 def createLoadableModuleBuilder(env):
261 """This is a utility function that creates the LoadableModule
262 Builder in an Environment if it is not there already.
264 If it is already there, we return the existing one.
268 ld_module = env['BUILDERS']['LoadableModule']
270 import SCons.Defaults
271 action_list = [ SCons.Defaults.SharedCheck,
272 SCons.Defaults.LdModuleLinkAction ]
273 ld_module = SCons.Builder.Builder(action = action_list,
274 emitter = "$LDMODULEEMITTER",
275 prefix = '$LDMODULEPREFIX',
276 suffix = '$LDMODULESUFFIX',
277 target_scanner = ProgramScanner,
278 src_suffix = '$SHOBJSUFFIX',
279 src_builder = 'SharedObject')
280 env['BUILDERS']['LoadableModule'] = ld_module
284 def createObjBuilders(env):
285 """This is a utility function that creates the StaticObject
286 and SharedObject Builders in an Environment if they
287 are not there already.
289 If they are there already, we return the existing ones.
291 This is a separate function because soooo many Tools
292 use this functionality.
294 The return is a 2-tuple of (StaticObject, SharedObject)
299 static_obj = env['BUILDERS']['StaticObject']
301 static_obj = SCons.Builder.Builder(action = {},
303 prefix = '$OBJPREFIX',
304 suffix = '$OBJSUFFIX',
305 src_builder = ['CFile', 'CXXFile'],
306 source_scanner = SourceFileScanner,
308 env['BUILDERS']['StaticObject'] = static_obj
309 env['BUILDERS']['Object'] = static_obj
312 shared_obj = env['BUILDERS']['SharedObject']
314 shared_obj = SCons.Builder.Builder(action = {},
316 prefix = '$SHOBJPREFIX',
317 suffix = '$SHOBJSUFFIX',
318 src_builder = ['CFile', 'CXXFile'],
319 source_scanner = SourceFileScanner,
321 env['BUILDERS']['SharedObject'] = shared_obj
323 return (static_obj, shared_obj)
325 def createCFileBuilders(env):
326 """This is a utility function that creates the CFile/CXXFile
327 Builders in an Environment if they
328 are not there already.
330 If they are there already, we return the existing ones.
332 This is a separate function because soooo many Tools
333 use this functionality.
335 The return is a 2-tuple of (CFile, CXXFile)
339 c_file = env['BUILDERS']['CFile']
341 c_file = SCons.Builder.Builder(action = {},
343 suffix = {None:'$CFILESUFFIX'})
344 env['BUILDERS']['CFile'] = c_file
346 env.SetDefault(CFILESUFFIX = '.c')
349 cxx_file = env['BUILDERS']['CXXFile']
351 cxx_file = SCons.Builder.Builder(action = {},
353 suffix = {None:'$CXXFILESUFFIX'})
354 env['BUILDERS']['CXXFile'] = cxx_file
355 env.SetDefault(CXXFILESUFFIX = '.cc')
357 return (c_file, cxx_file)
359 ##########################################################################
360 # Create common Java builders
362 def CreateJarBuilder(env):
364 java_jar = env['BUILDERS']['Jar']
366 fs = SCons.Node.FS.get_default_fs()
367 jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR')
368 java_jar = SCons.Builder.Builder(action = jar_com,
369 suffix = '$JARSUFFIX',
370 src_suffix = '$JAVACLASSSUFIX',
371 src_builder = 'JavaClassFile',
372 source_factory = fs.Entry)
373 env['BUILDERS']['Jar'] = java_jar
376 def CreateJavaHBuilder(env):
378 java_javah = env['BUILDERS']['JavaH']
380 fs = SCons.Node.FS.get_default_fs()
381 java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR')
382 java_javah = SCons.Builder.Builder(action = java_javah_com,
383 src_suffix = '$JAVACLASSSUFFIX',
384 target_factory = fs.Entry,
385 source_factory = fs.File,
386 src_builder = 'JavaClassFile')
387 env['BUILDERS']['JavaH'] = java_javah
390 def CreateJavaClassFileBuilder(env):
392 java_class_file = env['BUILDERS']['JavaClassFile']
394 fs = SCons.Node.FS.get_default_fs()
395 javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
396 java_class_file = SCons.Builder.Builder(action = javac_com,
398 #suffix = '$JAVACLASSSUFFIX',
399 src_suffix = '$JAVASUFFIX',
400 src_builder = ['JavaFile'],
401 target_factory = fs.Entry,
402 source_factory = fs.File)
403 env['BUILDERS']['JavaClassFile'] = java_class_file
404 return java_class_file
406 def CreateJavaClassDirBuilder(env):
408 java_class_dir = env['BUILDERS']['JavaClassDir']
410 fs = SCons.Node.FS.get_default_fs()
411 javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
412 java_class_dir = SCons.Builder.Builder(action = javac_com,
414 target_factory = fs.Dir,
415 source_factory = fs.Dir)
416 env['BUILDERS']['JavaClassDir'] = java_class_dir
417 return java_class_dir
419 def CreateJavaFileBuilder(env):
421 java_file = env['BUILDERS']['JavaFile']
423 java_file = SCons.Builder.Builder(action = {},
425 suffix = {None:'$JAVASUFFIX'})
426 env['BUILDERS']['JavaFile'] = java_file
427 env['JAVASUFFIX'] = '.java'
430 class ToolInitializerMethod(object):
432 This is added to a construction environment in place of a
433 method(s) normally called for a Builder (env.Object, env.StaticObject,
434 etc.). When called, it has its associated ToolInitializer
435 object search the specified list of tools and apply the first
436 one that exists to the construction environment. It then calls
437 whatever builder was (presumably) added to the construction
438 environment in place of this particular instance.
440 def __init__(self, name, initializer):
442 Note: we store the tool name as __name__ so it can be used by
443 the class that attaches this to a construction environment.
446 self.initializer = initializer
448 def get_builder(self, env):
450 Returns the appropriate real Builder for this method name
451 after having the associated ToolInitializer object apply
452 the appropriate Tool module.
454 builder = getattr(env, self.__name__)
456 self.initializer.apply_tools(env)
458 builder = getattr(env, self.__name__)
460 # There was no Builder added, which means no valid Tool
461 # for this name was found (or possibly there's a mismatch
462 # between the name we were called by and the Builder name
463 # added by the Tool module).
466 self.initializer.remove_methods(env)
470 def __call__(self, env, *args, **kw):
473 builder = self.get_builder(env)
476 return builder(*args, **kw)
478 class ToolInitializer(object):
480 A class for delayed initialization of Tools modules.
482 Instances of this class associate a list of Tool modules with
483 a list of Builder method names that will be added by those Tool
484 modules. As part of instantiating this object for a particular
485 construction environment, we also add the appropriate
486 ToolInitializerMethod objects for the various Builder methods
487 that we want to use to delay Tool searches until necessary.
489 def __init__(self, env, tools, names):
490 if not SCons.Util.is_List(tools):
492 if not SCons.Util.is_List(names):
499 method = ToolInitializerMethod(name, self)
500 self.methods[name] = method
501 env.AddMethod(method)
503 def remove_methods(self, env):
505 Removes the methods that were added by the tool initialization
506 so we no longer copy and re-bind them when the construction
507 environment gets cloned.
509 for method in self.methods.values():
510 env.RemoveMethod(method)
512 def apply_tools(self, env):
514 Searches the list of associated Tool modules for one that
515 exists, and applies that to the construction environment.
518 tool = SCons.Tool.Tool(t)
523 # If we fall through here, there was no tool module found.
524 # This is where we can put an informative error message
525 # about the inability to find the tool. We'll start doing
526 # this as we cut over more pre-defined Builder+Tools to use
527 # the ToolInitializer class.
529 def Initializers(env):
530 ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs'])
531 def Install(self, *args, **kw):
532 return self._InternalInstall(*args, **kw)
533 def InstallAs(self, *args, **kw):
534 return self._InternalInstallAs(*args, **kw)
535 env.AddMethod(Install)
536 env.AddMethod(InstallAs)
538 def FindTool(tools, env):
545 def FindAllTools(tools, env):
546 def ToolExists(tool, env=env):
547 return Tool(tool).exists(env)
548 return list(filter (ToolExists, tools))
550 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 other_plat_tools=['msvs','midl']
567 elif str(platform) == 'os2':
568 "prefer IBM tools on OS/2"
569 linkers = ['ilink', 'gnulink', ]#'mslink']
570 c_compilers = ['icc', 'gcc',]# 'msvc', 'cc']
571 cxx_compilers = ['icc', 'g++',]# 'msvc', 'c++']
572 assemblers = ['nasm',]# 'masm', 'gas']
573 fortran_compilers = ['ifl', 'g77']
574 ars = ['ar',]# 'mslib']
575 elif str(platform) == 'irix':
576 "prefer MIPSPro on IRIX"
577 linkers = ['sgilink', 'gnulink']
578 c_compilers = ['sgicc', 'gcc', 'cc']
579 cxx_compilers = ['sgic++', 'g++', 'c++']
580 assemblers = ['as', 'gas']
581 fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
583 elif str(platform) == 'sunos':
584 "prefer Forte tools on SunOS"
585 linkers = ['sunlink', 'gnulink']
586 c_compilers = ['suncc', 'gcc', 'cc']
587 cxx_compilers = ['sunc++', 'g++', 'c++']
588 assemblers = ['as', 'gas']
589 fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77',
590 'gfortran', 'g77', 'fortran']
592 elif str(platform) == 'hpux':
593 "prefer aCC tools on HP-UX"
594 linkers = ['hplink', 'gnulink']
595 c_compilers = ['hpcc', 'gcc', 'cc']
596 cxx_compilers = ['hpc++', 'g++', 'c++']
597 assemblers = ['as', 'gas']
598 fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
600 elif str(platform) == 'aix':
601 "prefer AIX Visual Age tools on AIX"
602 linkers = ['aixlink', 'gnulink']
603 c_compilers = ['aixcc', 'gcc', 'cc']
604 cxx_compilers = ['aixc++', 'g++', 'c++']
605 assemblers = ['as', 'gas']
606 fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran']
608 elif str(platform) == 'darwin':
609 "prefer GNU tools on Mac OS X, except for some linkers and IBM tools"
610 linkers = ['applelink', 'gnulink']
611 c_compilers = ['gcc', 'cc']
612 cxx_compilers = ['g++', 'c++']
614 fortran_compilers = ['gfortran', 'f95', 'f90', 'g77']
617 "prefer GNU tools on all other platforms"
618 linkers = ['gnulink', 'mslink', 'ilink']
619 c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc']
620 cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++']
621 assemblers = ['gas', 'nasm', 'masm']
622 fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77']
623 ars = ['ar', 'mslib']
625 c_compiler = FindTool(c_compilers, env) or c_compilers[0]
627 # XXX this logic about what tool provides what should somehow be
628 # moved into the tool files themselves.
629 if c_compiler and c_compiler == 'mingw':
630 # MinGW contains a linker, C compiler, C++ compiler,
631 # Fortran compiler, archiver and assembler:
635 fortran_compiler = None
638 # Don't use g++ if the C compiler has built-in C++ support:
639 if c_compiler in ('msvc', 'intelc', 'icc'):
642 cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0]
643 linker = FindTool(linkers, env) or linkers[0]
644 assembler = FindTool(assemblers, env) or assemblers[0]
645 fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0]
646 ar = FindTool(ars, env) or ars[0]
648 other_tools = FindAllTools(other_plat_tools + [
650 #TODO: merge 'install' into 'filesystem' and
651 # make 'filesystem' the default
654 'wix', #'midl', 'msvs',
657 # Foreign function interface
660 'jar', 'javac', 'javah', 'rmic',
662 'dvipdf', 'dvips', 'gs',
663 'tex', 'latex', 'pdflatex', 'pdftex',
666 # SourceCode factories
667 'BitKeeper', 'CVS', 'Perforce',
668 'RCS', 'SCCS', # 'Subversion',
671 tools = ([linker, c_compiler, cxx_compiler,
672 fortran_compiler, assembler, ar]
675 return [x for x in tools if x]
679 # indent-tabs-mode:nil
681 # vim: set expandtab tabstop=4 shiftwidth=4: