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
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 from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
40 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
49 import SCons.Scanner.C
50 import SCons.Scanner.D
51 import SCons.Scanner.LaTeX
52 import SCons.Scanner.Prog
56 CScanner = SCons.Scanner.C.CScanner()
57 DScanner = SCons.Scanner.D.DScanner()
58 LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
59 PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner()
60 ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
61 SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner')
63 CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
64 ".h", ".H", ".hxx", ".hpp", ".hh",
71 IDLSuffixes = [".idl", ".IDL"]
73 LaTeXSuffixes = [".tex", ".ltx", ".latex"]
75 for suffix in CSuffixes:
76 SourceFileScanner.add_scanner(suffix, CScanner)
78 for suffix in DSuffixes:
79 SourceFileScanner.add_scanner(suffix, DScanner)
81 # FIXME: what should be done here? Two scanners scan the same extensions,
82 # but look for different files, e.g., "picture.eps" vs. "picture.pdf".
83 # The builders for DVI and PDF explicitly reference their scanners
84 # I think that means this is not needed???
85 for suffix in LaTeXSuffixes:
86 SourceFileScanner.add_scanner(suffix, LaTeXScanner)
87 SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner)
90 def __init__(self, name, toolpath=[], **kw):
92 self.toolpath = toolpath + DefaultToolpath
93 # remember these so we can merge them into the call
96 module = self._tool_module()
97 self.generate = module.generate
98 self.exists = module.exists
99 if hasattr(module, 'options'):
100 self.options = module.options
102 def _tool_module(self):
103 # TODO: Interchange zipimport with normal initilization for better error reporting
104 oldpythonpath = sys.path
105 sys.path = self.toolpath + sys.path
109 file, path, desc = imp.find_module(self.name, self.toolpath)
111 return imp.load_module(self.name, file, path, desc)
115 except ImportError, e:
116 if str(e)!="No module named %s"%self.name:
117 raise SCons.Errors.EnvironmentError(e)
123 for aPath in self.toolpath:
125 importer = zipimport.zipimporter(aPath)
126 return importer.load_module(self.name)
127 except ImportError, e:
130 sys.path = oldpythonpath
132 full_name = 'SCons.Tool.' + self.name
134 return sys.modules[full_name]
137 smpath = sys.modules['SCons.Tool'].__path__
139 file, path, desc = imp.find_module(self.name, smpath)
140 module = imp.load_module(full_name, file, path, desc)
141 setattr(SCons.Tool, self.name, module)
145 except ImportError, e:
146 if str(e)!="No module named %s"%self.name:
147 raise SCons.Errors.EnvironmentError(e)
150 importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] )
151 module = importer.load_module(full_name)
152 setattr(SCons.Tool, self.name, module)
154 except ImportError, e:
155 m = "No tool named '%s': %s" % (self.name, e)
156 raise SCons.Errors.EnvironmentError(m)
157 except ImportError, e:
158 m = "No tool named '%s': %s" % (self.name, e)
159 raise SCons.Errors.EnvironmentError(m)
161 def __call__(self, env, *args, **kw):
162 if self.init_kw is not None:
163 # Merge call kws into init kws;
164 # but don't bash self.init_kw.
167 kw = self.init_kw.copy()
171 env.Append(TOOLS = [ self.name ])
172 if hasattr(self, 'options'):
173 import SCons.Variables
174 if 'options' not in env:
175 from SCons.Script import ARGUMENTS
176 env['options']=SCons.Variables.Variables(args=ARGUMENTS)
182 self.generate(env, *args, **kw)
187 ##########################################################################
188 # Create common executable program / library / object builders
190 def createProgBuilder(env):
191 """This is a utility function that creates the Program
192 Builder in an Environment if it is not there already.
194 If it is already there, we return the existing one.
198 program = env['BUILDERS']['Program']
200 import SCons.Defaults
201 program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction,
202 emitter = '$PROGEMITTER',
203 prefix = '$PROGPREFIX',
204 suffix = '$PROGSUFFIX',
205 src_suffix = '$OBJSUFFIX',
206 src_builder = 'Object',
207 target_scanner = ProgramScanner)
208 env['BUILDERS']['Program'] = program
212 def createStaticLibBuilder(env):
213 """This is a utility function that creates the StaticLibrary
214 Builder in an Environment if it is not there already.
216 If it is already there, we return the existing one.
220 static_lib = env['BUILDERS']['StaticLibrary']
222 action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
223 if env.Detect('ranlib'):
224 ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
225 action_list.append(ranlib_action)
227 static_lib = SCons.Builder.Builder(action = action_list,
228 emitter = '$LIBEMITTER',
229 prefix = '$LIBPREFIX',
230 suffix = '$LIBSUFFIX',
231 src_suffix = '$OBJSUFFIX',
232 src_builder = 'StaticObject')
233 env['BUILDERS']['StaticLibrary'] = static_lib
234 env['BUILDERS']['Library'] = static_lib
238 def createSharedLibBuilder(env):
239 """This is a utility function that creates the SharedLibrary
240 Builder in an Environment if it is not there already.
242 If it is already there, we return the existing one.
246 shared_lib = env['BUILDERS']['SharedLibrary']
248 import SCons.Defaults
249 action_list = [ SCons.Defaults.SharedCheck,
250 SCons.Defaults.ShLinkAction ]
251 shared_lib = SCons.Builder.Builder(action = action_list,
252 emitter = "$SHLIBEMITTER",
253 prefix = '$SHLIBPREFIX',
254 suffix = '$SHLIBSUFFIX',
255 target_scanner = ProgramScanner,
256 src_suffix = '$SHOBJSUFFIX',
257 src_builder = 'SharedObject')
258 env['BUILDERS']['SharedLibrary'] = shared_lib
262 def createLoadableModuleBuilder(env):
263 """This is a utility function that creates the LoadableModule
264 Builder in an Environment if it is not there already.
266 If it is already there, we return the existing one.
270 ld_module = env['BUILDERS']['LoadableModule']
272 import SCons.Defaults
273 action_list = [ SCons.Defaults.SharedCheck,
274 SCons.Defaults.LdModuleLinkAction ]
275 ld_module = SCons.Builder.Builder(action = action_list,
276 emitter = "$LDMODULEEMITTER",
277 prefix = '$LDMODULEPREFIX',
278 suffix = '$LDMODULESUFFIX',
279 target_scanner = ProgramScanner,
280 src_suffix = '$SHOBJSUFFIX',
281 src_builder = 'SharedObject')
282 env['BUILDERS']['LoadableModule'] = ld_module
286 def createObjBuilders(env):
287 """This is a utility function that creates the StaticObject
288 and SharedObject Builders in an Environment if they
289 are not there already.
291 If they are there already, we return the existing ones.
293 This is a separate function because soooo many Tools
294 use this functionality.
296 The return is a 2-tuple of (StaticObject, SharedObject)
301 static_obj = env['BUILDERS']['StaticObject']
303 static_obj = SCons.Builder.Builder(action = {},
305 prefix = '$OBJPREFIX',
306 suffix = '$OBJSUFFIX',
307 src_builder = ['CFile', 'CXXFile'],
308 source_scanner = SourceFileScanner,
310 env['BUILDERS']['StaticObject'] = static_obj
311 env['BUILDERS']['Object'] = static_obj
314 shared_obj = env['BUILDERS']['SharedObject']
316 shared_obj = SCons.Builder.Builder(action = {},
318 prefix = '$SHOBJPREFIX',
319 suffix = '$SHOBJSUFFIX',
320 src_builder = ['CFile', 'CXXFile'],
321 source_scanner = SourceFileScanner,
323 env['BUILDERS']['SharedObject'] = shared_obj
325 return (static_obj, shared_obj)
327 def createCFileBuilders(env):
328 """This is a utility function that creates the CFile/CXXFile
329 Builders in an Environment if they
330 are not there already.
332 If they are there already, we return the existing ones.
334 This is a separate function because soooo many Tools
335 use this functionality.
337 The return is a 2-tuple of (CFile, CXXFile)
341 c_file = env['BUILDERS']['CFile']
343 c_file = SCons.Builder.Builder(action = {},
345 suffix = {None:'$CFILESUFFIX'})
346 env['BUILDERS']['CFile'] = c_file
348 env.SetDefault(CFILESUFFIX = '.c')
351 cxx_file = env['BUILDERS']['CXXFile']
353 cxx_file = SCons.Builder.Builder(action = {},
355 suffix = {None:'$CXXFILESUFFIX'})
356 env['BUILDERS']['CXXFile'] = cxx_file
357 env.SetDefault(CXXFILESUFFIX = '.cc')
359 return (c_file, cxx_file)
361 ##########################################################################
362 # Create common Java builders
364 def CreateJarBuilder(env):
366 java_jar = env['BUILDERS']['Jar']
368 fs = SCons.Node.FS.get_default_fs()
369 jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR')
370 java_jar = SCons.Builder.Builder(action = jar_com,
371 suffix = '$JARSUFFIX',
372 src_suffix = '$JAVACLASSSUFIX',
373 src_builder = 'JavaClassFile',
374 source_factory = fs.Entry)
375 env['BUILDERS']['Jar'] = java_jar
378 def CreateJavaHBuilder(env):
380 java_javah = env['BUILDERS']['JavaH']
382 fs = SCons.Node.FS.get_default_fs()
383 java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR')
384 java_javah = SCons.Builder.Builder(action = java_javah_com,
385 src_suffix = '$JAVACLASSSUFFIX',
386 target_factory = fs.Entry,
387 source_factory = fs.File,
388 src_builder = 'JavaClassFile')
389 env['BUILDERS']['JavaH'] = java_javah
392 def CreateJavaClassFileBuilder(env):
394 java_class_file = env['BUILDERS']['JavaClassFile']
396 fs = SCons.Node.FS.get_default_fs()
397 javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
398 java_class_file = SCons.Builder.Builder(action = javac_com,
400 #suffix = '$JAVACLASSSUFFIX',
401 src_suffix = '$JAVASUFFIX',
402 src_builder = ['JavaFile'],
403 target_factory = fs.Entry,
404 source_factory = fs.File)
405 env['BUILDERS']['JavaClassFile'] = java_class_file
406 return java_class_file
408 def CreateJavaClassDirBuilder(env):
410 java_class_dir = env['BUILDERS']['JavaClassDir']
412 fs = SCons.Node.FS.get_default_fs()
413 javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
414 java_class_dir = SCons.Builder.Builder(action = javac_com,
416 target_factory = fs.Dir,
417 source_factory = fs.Dir)
418 env['BUILDERS']['JavaClassDir'] = java_class_dir
419 return java_class_dir
421 def CreateJavaFileBuilder(env):
423 java_file = env['BUILDERS']['JavaFile']
425 java_file = SCons.Builder.Builder(action = {},
427 suffix = {None:'$JAVASUFFIX'})
428 env['BUILDERS']['JavaFile'] = java_file
429 env['JAVASUFFIX'] = '.java'
432 class ToolInitializerMethod:
434 This is added to a construction environment in place of a
435 method(s) normally called for a Builder (env.Object, env.StaticObject,
436 etc.). When called, it has its associated ToolInitializer
437 object search the specified list of tools and apply the first
438 one that exists to the construction environment. It then calls
439 whatever builder was (presumably) added to the construction
440 environment in place of this particular instance.
442 def __init__(self, name, initializer):
444 Note: we store the tool name as __name__ so it can be used by
445 the class that attaches this to a construction environment.
448 self.initializer = initializer
450 def get_builder(self, env):
452 Returns the appropriate real Builder for this method name
453 after having the associated ToolInitializer object apply
454 the appropriate Tool module.
456 builder = getattr(env, self.__name__)
458 self.initializer.apply_tools(env)
460 builder = getattr(env, self.__name__)
462 # There was no Builder added, which means no valid Tool
463 # for this name was found (or possibly there's a mismatch
464 # between the name we were called by and the Builder name
465 # added by the Tool module).
468 self.initializer.remove_methods(env)
472 def __call__(self, env, *args, **kw):
475 builder = self.get_builder(env)
478 return builder(*args, **kw)
480 class ToolInitializer:
482 A class for delayed initialization of Tools modules.
484 Instances of this class associate a list of Tool modules with
485 a list of Builder method names that will be added by those Tool
486 modules. As part of instantiating this object for a particular
487 construction environment, we also add the appropriate
488 ToolInitializerMethod objects for the various Builder methods
489 that we want to use to delay Tool searches until necessary.
491 def __init__(self, env, tools, names):
492 if not SCons.Util.is_List(tools):
494 if not SCons.Util.is_List(names):
501 method = ToolInitializerMethod(name, self)
502 self.methods[name] = method
503 env.AddMethod(method)
505 def remove_methods(self, env):
507 Removes the methods that were added by the tool initialization
508 so we no longer copy and re-bind them when the construction
509 environment gets cloned.
511 for method in self.methods.values():
512 env.RemoveMethod(method)
514 def apply_tools(self, env):
516 Searches the list of associated Tool modules for one that
517 exists, and applies that to the construction environment.
520 tool = SCons.Tool.Tool(t)
525 # If we fall through here, there was no tool module found.
526 # This is where we can put an informative error message
527 # about the inability to find the tool. We'll start doing
528 # this as we cut over more pre-defined Builder+Tools to use
529 # the ToolInitializer class.
531 def Initializers(env):
532 ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs'])
533 def Install(self, *args, **kw):
534 return self._InternalInstall(*args, **kw)
535 def InstallAs(self, *args, **kw):
536 return self._InternalInstallAs(*args, **kw)
537 env.AddMethod(Install)
538 env.AddMethod(InstallAs)
540 def FindTool(tools, env):
547 def FindAllTools(tools, env):
548 def ToolExists(tool, env=env):
549 return Tool(tool).exists(env)
550 return list(filter (ToolExists, tools))
552 def tool_list(platform, env):
555 # XXX this logic about what tool to prefer on which platform
556 # should be moved into either the platform files or
557 # the tool files themselves.
558 # The search orders here are described in the man page. If you
559 # change these search orders, update the man page as well.
560 if str(platform) == 'win32':
561 "prefer Microsoft tools on Windows"
562 linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ]
563 c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ]
564 cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ]
565 assemblers = ['masm', 'nasm', 'gas', '386asm' ]
566 fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran']
567 ars = ['mslib', 'ar', 'tlib']
568 other_plat_tools=['msvs','midl']
569 elif str(platform) == 'os2':
570 "prefer IBM tools on OS/2"
571 linkers = ['ilink', 'gnulink', ]#'mslink']
572 c_compilers = ['icc', 'gcc',]# 'msvc', 'cc']
573 cxx_compilers = ['icc', 'g++',]# 'msvc', 'c++']
574 assemblers = ['nasm',]# 'masm', 'gas']
575 fortran_compilers = ['ifl', 'g77']
576 ars = ['ar',]# 'mslib']
577 elif str(platform) == 'irix':
578 "prefer MIPSPro on IRIX"
579 linkers = ['sgilink', 'gnulink']
580 c_compilers = ['sgicc', 'gcc', 'cc']
581 cxx_compilers = ['sgic++', 'g++', 'c++']
582 assemblers = ['as', 'gas']
583 fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
585 elif str(platform) == 'sunos':
586 "prefer Forte tools on SunOS"
587 linkers = ['sunlink', 'gnulink']
588 c_compilers = ['suncc', 'gcc', 'cc']
589 cxx_compilers = ['sunc++', 'g++', 'c++']
590 assemblers = ['as', 'gas']
591 fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77',
592 'gfortran', 'g77', 'fortran']
594 elif str(platform) == 'hpux':
595 "prefer aCC tools on HP-UX"
596 linkers = ['hplink', 'gnulink']
597 c_compilers = ['hpcc', 'gcc', 'cc']
598 cxx_compilers = ['hpc++', 'g++', 'c++']
599 assemblers = ['as', 'gas']
600 fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran']
602 elif str(platform) == 'aix':
603 "prefer AIX Visual Age tools on AIX"
604 linkers = ['aixlink', 'gnulink']
605 c_compilers = ['aixcc', 'gcc', 'cc']
606 cxx_compilers = ['aixc++', 'g++', 'c++']
607 assemblers = ['as', 'gas']
608 fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran']
610 elif str(platform) == 'darwin':
611 "prefer GNU tools on Mac OS X, except for some linkers and IBM tools"
612 linkers = ['applelink', 'gnulink']
613 c_compilers = ['gcc', 'cc']
614 cxx_compilers = ['g++', 'c++']
616 fortran_compilers = ['gfortran', 'f95', 'f90', 'g77']
619 "prefer GNU tools on all other platforms"
620 linkers = ['gnulink', 'mslink', 'ilink']
621 c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc']
622 cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++']
623 assemblers = ['gas', 'nasm', 'masm']
624 fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77']
625 ars = ['ar', 'mslib']
627 c_compiler = FindTool(c_compilers, env) or c_compilers[0]
629 # XXX this logic about what tool provides what should somehow be
630 # moved into the tool files themselves.
631 if c_compiler and c_compiler == 'mingw':
632 # MinGW contains a linker, C compiler, C++ compiler,
633 # Fortran compiler, archiver and assembler:
637 fortran_compiler = None
640 # Don't use g++ if the C compiler has built-in C++ support:
641 if c_compiler in ('msvc', 'intelc', 'icc'):
644 cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0]
645 linker = FindTool(linkers, env) or linkers[0]
646 assembler = FindTool(assemblers, env) or assemblers[0]
647 fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0]
648 ar = FindTool(ars, env) or ars[0]
650 other_tools = FindAllTools(['BitKeeper', 'CVS',
653 'dvipdf', 'dvips', 'gs',
654 'jar', 'javac', 'javah',
656 'm4', #'midl', 'msvs',
657 'pdflatex', 'pdftex', 'Perforce',
658 'RCS', 'rmic', 'rpcgen',
663 'yacc', 'zip', 'rpm', 'wix']+other_plat_tools,
666 tools = ([linker, c_compiler, cxx_compiler,
667 fortran_compiler, assembler, ar]
670 return [x for x in tools if x]
674 # indent-tabs-mode:nil
676 # vim: set expandtab tabstop=4 shiftwidth=4: