3 Builders and other things for the local site. Here's where we'll
4 duplicate the functionality of autoconf until we move it into the
5 installation procedure or use something like qmconf.
7 The code that reads the registry to find MSVC components was borrowed
8 from distutils.msvccompiler.
13 # Copyright (c) 2001, 2002 Steven Knight
15 # Permission is hereby granted, free of charge, to any person obtaining
16 # a copy of this software and associated documentation files (the
17 # "Software"), to deal in the Software without restriction, including
18 # without limitation the rights to use, copy, modify, merge, publish,
19 # distribute, sublicense, and/or sell copies of the Software, and to
20 # permit persons to whom the Software is furnished to do so, subject to
21 # the following conditions:
23 # The above copyright notice and this permission notice shall be included
24 # in all copies or substantial portions of the Software.
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
48 import SCons.Node.Alias
51 import SCons.Scanner.C
52 import SCons.Scanner.Fortran
53 import SCons.Scanner.Prog
56 def yaccEmitter(target, source, env, **kw):
57 # Yacc can be configured to emit a .h file as well
58 # as a .c file, if -d is specified on the command line.
60 os.path.splitext(SCons.Util.to_String(source[0]))[1] in \
62 '-d' in string.split(env.subst("$YACCFLAGS")):
63 target.append(os.path.splitext(SCons.Util.to_String(target[0]))[0] + \
65 return (target, source)
67 CFile = SCons.Builder.Builder(action = { '.l' : '$LEXCOM',
70 emitter = yaccEmitter,
71 suffix = '$CFILESUFFIX')
73 CXXFile = SCons.Builder.Builder(action = { '.ll' : '$LEXCOM',
76 emitter = yaccEmitter,
77 suffix = '$CXXFILESUFFIX')
79 class SharedFlagChecker:
80 """This is a callable class that is used as
81 a build action for all objects, libraries, and programs.
82 Its job is to run before the "real" action that builds the
83 file, to make sure we aren't trying to link shared objects
84 into a static library/program, or static objects into a
87 def __init__(self, shared):
90 def __call__(self, source, target, env, **kw):
91 if kw.has_key('shared'):
92 raise SCons.Errors.UserError, "The shared= parameter to Library() or Object() no longer works.\nUse SharedObject() or SharedLibrary() instead."
94 tgt.attributes.shared = self.shared
97 if hasattr(src.attributes, 'shared'):
98 if self.shared and not src.attributes.shared:
99 raise SCons.Errors.UserError, "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])
100 elif not self.shared and src.attributes.shared:
101 raise SCons.Errors.UserError, "Source file: %s is shared and is not compatible with static target: %s" % (src, target[0])
103 SharedCheck = SharedFlagChecker(1)
104 StaticCheck = SharedFlagChecker(0)
106 CAction = SCons.Action.Action([ StaticCheck, "$CCCOM" ])
107 ShCAction = SCons.Action.Action([ SharedCheck, "$SHCCCOM" ])
108 CXXAction = SCons.Action.Action([ StaticCheck, "$CXXCOM" ])
109 ShCXXAction = SCons.Action.Action([ SharedCheck, "$SHCXXCOM" ])
110 F77Action = SCons.Action.Action([ StaticCheck, "$F77COM" ])
111 ShF77Action = SCons.Action.Action([ SharedCheck, "$SHF77COM" ])
112 F77PPAction = SCons.Action.Action([ StaticCheck, "$F77PPCOM" ])
113 ShF77PPAction = SCons.Action.Action([ SharedCheck, "$SHF77PPCOM" ])
115 if os.path.normcase('.c') == os.path.normcase('.C'):
116 # We're on a case-insensitive system, so .[CF] (upper case)
117 # files should be treated like .[cf] (lower case) files.
121 F_shared = ShF77Action
123 # We're on a case-sensitive system, so .C (upper case) files
124 # are C++, and .F (upper case) files get run through the C
127 C_shared = ShCXXAction
128 F_static = F77PPAction
129 F_shared = ShF77PPAction
131 StaticObject = SCons.Builder.Builder(action = { ".C" : C_static,
142 ".fpp" : F77PPAction,
143 ".FPP" : F77PPAction },
144 prefix = '$OBJPREFIX',
145 suffix = '$OBJSUFFIX',
146 src_builder = [CFile, CXXFile])
148 SharedObject = SCons.Builder.Builder(action = { ".C" : C_shared,
150 ".cpp" : ShCXXAction,
151 ".cxx" : ShCXXAction,
152 ".c++" : ShCXXAction,
153 ".C++" : ShCXXAction,
156 ".for" : ShF77Action,
157 ".FOR" : ShF77Action,
159 ".fpp" : ShF77PPAction,
160 ".FPP" : ShF77PPAction },
161 prefix = '$OBJPREFIX',
162 suffix = '$OBJSUFFIX',
163 src_builder = [CFile, CXXFile])
165 def win32TempFileMunge(env, cmd_list, for_signature):
166 """Given a list of command line arguments, see if it is too
167 long to pass to the win32 command line interpreter. If so,
168 create a temp file, then pass "@tempfile" as the sole argument
169 to the supplied command (which is the first element of cmd_list).
170 Otherwise, just return [cmd_list]."""
171 cmd = env.subst_list(cmd_list)[0]
172 if for_signature or \
173 (reduce(lambda x, y: x + len(y), cmd, 0) + len(cmd)) <= 2048:
177 # We do a normpath because mktemp() has what appears to be
178 # a bug in Win32 that will use a forward slash as a path
179 # delimiter. Win32's link mistakes that for a command line
181 tmp = os.path.normpath(tempfile.mktemp())
182 args = map(SCons.Util.quote_spaces, cmd[1:])
183 open(tmp, 'w').write(string.join(args, " ") + "\n")
184 return [ [cmd[0], '@' + tmp],
187 def win32LinkGenerator(env, target, source, for_signature, **kw):
188 args = [ '$LINK', '$LINKFLAGS', '/OUT:%s' % target[0],
189 '$(', '$_LIBDIRFLAGS', '$)', '$_LIBFLAGS' ]
190 args.extend(map(SCons.Util.to_String, source))
191 return win32TempFileMunge(env, args, for_signature)
193 ProgScan = SCons.Scanner.Prog.ProgScan()
195 Program = SCons.Builder.Builder(action=[ StaticCheck, '$LINKCOM' ],
196 prefix='$PROGPREFIX',
197 suffix='$PROGSUFFIX',
198 src_suffix='$OBJSUFFIX',
199 src_builder=StaticObject,
202 def win32LibGenerator(target, source, env, for_signature, no_import_lib=0):
203 listCmd = [ "$SHLINK", "$SHLINKFLAGS" ]
206 ext = os.path.splitext(str(tgt))[1]
207 if ext == env.subst("$LIBSUFFIX"):
208 # Put it on the command line as an import library.
210 raise SCons.Errors.UserError, "%s: You cannot specify a .lib file as a target of a shared library build if no_import_library is nonzero." % tgt
211 listCmd.append("${WIN32IMPLIBPREFIX}%s" % tgt)
213 listCmd.append("${WIN32DLLPREFIX}%s" % tgt)
215 listCmd.extend([ '$_LIBDIRFLAGS', '$_LIBFLAGS' ])
217 ext = os.path.splitext(str(src))[1]
218 if ext == env.subst("$WIN32DEFSUFFIX"):
219 # Treat this source as a .def file.
220 listCmd.append("${WIN32DEFPREFIX}%s" % src)
222 # Just treat it as a generic source file.
223 listCmd.append(str(src))
224 return win32TempFileMunge(env, listCmd, for_signature)
226 def win32LibEmitter(target, source, env, no_import_lib=0):
229 ext = os.path.splitext(str(tgt))[1]
230 if ext == env.subst("$SHLIBSUFFIX"):
234 raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")
236 if env.has_key("WIN32_INSERT_DEF") and \
237 env["WIN32_INSERT_DEF"] and \
238 not '.def' in map(lambda x: os.path.split(str(x))[1],
241 # append a def file to the list of sources
242 source.append("%s%s" % (os.path.splitext(str(dll))[0],
243 env.subst("$WIN32DEFSUFFIX")))
244 if not no_import_lib and \
245 not env.subst("$LIBSUFFIX") in \
246 map(lambda x: os.path.split(str(x))[1], target):
247 # Append an import library to the list of targets.
248 target.append("%s%s%s" % (env.subst("$LIBPREFIX"),
249 os.path.splitext(str(dll))[0],
250 env.subst("$LIBSUFFIX")))
251 return (target, source)
253 StaticLibrary = SCons.Builder.Builder(action=[ StaticCheck, "$ARCOM" ],
254 prefix = '$LIBPREFIX',
255 suffix = '$LIBSUFFIX',
256 src_suffix = '$OBJSUFFIX',
257 src_builder = StaticObject)
259 SharedLibrary = SCons.Builder.Builder(action=[ SharedCheck, "$SHLINKCOM" ],
260 emitter="$LIBEMITTER",
261 prefix = '$SHLIBPREFIX',
262 suffix = '$SHLIBSUFFIX',
264 src_suffix = '$OBJSUFFIX',
265 src_builder = SharedObject)
267 LaTeXAction = SCons.Action.Action('$LATEXCOM')
269 DVI = SCons.Builder.Builder(action = { '.tex' : '$TEXCOM',
270 '.ltx' : LaTeXAction,
271 '.latex' : LaTeXAction,
273 # The suffix is not configurable via a
274 # construction variable like $DVISUFFIX
275 # because the output file name is
276 # hard-coded within TeX.
279 PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM')
281 PDF = SCons.Builder.Builder(action = { '.dvi' : '$PDFCOM',
282 '.tex' : '$PDFTEXCOM',
283 '.ltx' : PDFLaTeXAction,
284 '.latex' : PDFLaTeXAction,
286 prefix = '$PDFPREFIX',
287 suffix = '$PDFSUFFIX')
289 PostScript = SCons.Builder.Builder(action = '$PSCOM',
290 prefix = '$PSPREFIX',
291 suffix = '$PSSUFFIX',
295 CScan = SCons.Scanner.C.CScan()
297 FortranScan = SCons.Scanner.Fortran.FortranScan()
299 def alias_builder(env, target, source):
302 Alias = SCons.Builder.Builder(action = alias_builder,
303 target_factory = SCons.Node.Alias.default_ans.Alias,
304 source_factory = SCons.Node.FS.default_fs.Entry,
307 def get_devstudio_versions ():
309 Get list of devstudio versions from the Windows registry. Return a
310 list of strings containing version numbers; an exception will be raised
311 if we were unable to access the registry (eg. couldn't import
312 a registry-access module) or the appropriate registry keys weren't
316 if not SCons.Util.can_read_reg:
317 raise SCons.Errors.InternalError, "No Windows registry module was found"
319 K = 'Software\\Microsoft\\Devstudio'
321 for base in (SCons.Util.HKEY_CLASSES_ROOT,
322 SCons.Util.HKEY_LOCAL_MACHINE,
323 SCons.Util.HKEY_CURRENT_USER,
324 SCons.Util.HKEY_USERS):
326 k = SCons.Util.RegOpenKeyEx(base,K)
330 p = SCons.Util.RegEnumKey(k,i)
331 if p[0] in '123456789' and p not in L:
333 except SCons.Util.RegError:
336 except SCons.Util.RegError:
340 raise SCons.Errors.InternalError, "DevStudio was not found."
346 def get_msvc_path (path, version, platform='x86'):
348 Get a list of devstudio directories (include, lib or path). Return
349 a string delimited by ';'. An exception will be raised if unable to
350 access the registry or appropriate registry keys not found.
353 if not SCons.Util.can_read_reg:
354 raise SCons.Errors.InternalError, "No Windows registry module was found"
358 path = string.upper(path + ' Dirs')
359 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
360 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
362 for base in (SCons.Util.HKEY_CLASSES_ROOT,
363 SCons.Util.HKEY_LOCAL_MACHINE,
364 SCons.Util.HKEY_CURRENT_USER,
365 SCons.Util.HKEY_USERS):
367 k = SCons.Util.RegOpenKeyEx(base,K)
371 (p,v,t) = SCons.Util.RegEnumValue(k,i)
372 if string.upper(p) == path:
375 except SCons.Util.RegError:
377 except SCons.Util.RegError:
380 # if we got here, then we didn't find the registry entries:
381 raise SCons.Errors.InternalError, "%s was not found in the registry."%path
383 def get_msdev_dir(version):
384 """Returns the root directory of the MSDev installation from the
385 registry if it can be found, otherwise we guess."""
386 if SCons.Util.can_read_reg:
387 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
388 'Products\\Microsoft Visual C++') % \
390 for base in (SCons.Util.HKEY_LOCAL_MACHINE,
391 SCons.Util.HKEY_CURRENT_USER):
393 k = SCons.Util.RegOpenKeyEx(base,K)
394 val, tok = SCons.Util.RegQueryValueEx(k, 'ProductDir')
395 return os.path.split(val)[0]
396 except SCons.Util.RegError:
399 def make_win32_env_from_paths(include, lib, path):
401 Build a dictionary of construction variables for a win32 platform.
402 include - include path
404 path - executable path
408 'CCFLAGS' : '/nologo',
409 'CCCOM' : '$CC $CCFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET',
411 'SHCCFLAGS' : '$CCFLAGS',
412 'SHCCCOM' : '$SHCC $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET',
413 'CFILESUFFIX' : '.c',
415 'CXXFLAGS' : '$CCFLAGS',
416 'CXXCOM' : '$CXX $CXXFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET',
418 'SHCXXFLAGS' : '$CXXFLAGS',
419 'SHCXXCOM' : '$SHCXX $SHCXXFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET',
420 'CXXFILESUFFIX' : '.cc',
423 'F77COM' : '$F77 $F77FLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET',
424 'F77PPCOM' : '$F77 $F77FLAGS $CPPFLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET',
426 'SHF77FLAGS' : '$F77FLAGS',
427 'SHF77COM' : '$SHF77 $SHF77FLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET',
428 'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET',
430 'LINKFLAGS' : '/nologo',
431 'LINKCOM' : SCons.Action.CommandGenerator(win32LinkGenerator),
433 'SHLINKFLAGS': '$LINKFLAGS /dll',
434 'SHLINKCOM' : SCons.Action.CommandGenerator(win32LibGenerator),
436 'ARFLAGS' : '/nologo',
437 'ARCOM' : '$AR $ARFLAGS /OUT:$TARGET $SOURCES',
440 'LEXCOM' : '$LEX $LEXFLAGS -t $SOURCES > $TARGET',
443 'YACCCOM' : '$YACC $YACCFLAGS -o $TARGET $SOURCES',
446 'TEXCOM' : '$TEX $TEXFLAGS $SOURCES',
449 'LATEXCOM' : '$LATEX $LATEXFLAGS $SOURCES',
452 'PDFCOM' : '$DVIPDF $DVIPDFFLAGS $SOURCES $TARGET',
454 'PDFSUFFIX' : '.pdf',
457 'PDFTEXCOM' : '$PDFTEX $PDFTEXFLAGS $SOURCES $TARGET',
458 'PDFLATEX' : 'pdflatex',
459 'PDFLATEXFLAGS' : '',
460 'PDFLATEXCOM' : '$PDFLATEX $PDFLATEXFLAGS $SOURCES $TARGET',
463 'PSCOM' : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
466 'BUILDERS' : { 'Alias' : Alias,
470 'Library' : StaticLibrary,
471 'StaticLibrary' : StaticLibrary,
472 'SharedLibrary' : SharedLibrary,
473 'Object' : StaticObject,
474 'StaticObject' : StaticObject,
475 'SharedObject' : SharedObject,
477 'PostScript' : PostScript,
478 'Program' : Program },
479 'SCANNERS' : [CScan, FortranScan],
480 'LIBDIRPREFIX' : '/LIBPATH:',
482 'LIBLINKPREFIX' : '',
483 'LIBLINKSUFFIX' : '$LIBSUFFIX',
484 'LIBEMITTER' : win32LibEmitter,
487 'WIN32DEFPREFIX' : '/def:',
488 'WIN32DEFSUFFIX' : '.def',
489 'WIN32DLLPREFIX' : '/out:',
490 'WIN32IMPLIBPREFIX' : '/implib:',
491 'WIN32_INSERT_DEF' : 0,
499 def make_win32_env(version):
501 Build a dictionary of construction variables for a win32 platform.
502 ver - the version string of DevStudio to use (e.g. "6.0")
504 return make_win32_env_from_paths(get_msvc_path("include", version),
505 get_msvc_path("lib", version),
506 get_msvc_path("path", version)
507 + ";" + os.environ['PATH'])
510 if os.name == 'posix':
511 arcom = '$AR $ARFLAGS $TARGET $SOURCES'
513 if SCons.Util.WhereIs(ranlib):
514 arcom = arcom + '\n$RANLIB $RANLIBFLAGS $TARGET'
516 ConstructionEnvironment = {
519 'CCCOM' : '$CC $CCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
521 'SHCCFLAGS' : '$CCFLAGS -fPIC',
522 'SHCCCOM' : '$SHCC $SHCCFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
523 'CFILESUFFIX' : '.c',
525 'CXXFLAGS' : '$CCFLAGS',
526 'CXXCOM' : '$CXX $CXXFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
527 'CXXFILESUFFIX' : '.cc',
529 'SHCXXFLAGS' : '$CXXFLAGS -fPIC',
530 'SHCXXCOM' : '$SHCXX $SHCXXFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
533 'F77COM' : '$F77 $F77FLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
534 'F77PPCOM' : '$F77 $F77FLAGS $CPPFLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
535 'SHF77FLAGS' : '$F77FLAGS -fPIC',
536 'SHF77COM' : '$F77 $SHF77FLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
537 'SHF77PPCOM' : '$F77 $SHF77FLAGS $CPPFLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
539 'SHF77FLAGS' : '$F77FLAGS -fPIC',
540 'SHF77COM' : '$SHF77 $SHF77FLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
541 'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
544 'LINKCOM' : '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
546 'SHLINKFLAGS': '$LINKFLAGS -shared',
547 'SHLINKCOM' : '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
555 'LEXCOM' : '$LEX $LEXFLAGS -t $SOURCES > $TARGET',
558 'YACCCOM' : '$YACC $YACCFLAGS -o $TARGET $SOURCES',
561 'TEXCOM' : '$TEX $TEXFLAGS $SOURCES',
564 'LATEXCOM' : '$LATEX $LATEXFLAGS $SOURCES',
566 'PDFCOM' : '$DVIPDF $DVIPDFFLAGS $SOURCES $TARGET',
568 'PDFSUFFIX' : '.pdf',
571 'PDFTEXCOM' : '$PDFTEX $PDFTEXFLAGS $SOURCES $TARGET',
572 'PDFLATEX' : 'pdflatex',
573 'PDFLATEXFLAGS' : '',
574 'PDFLATEXCOM' : '$PDFLATEX $PDFLATEXFLAGS $SOURCES $TARGET',
576 'PSCOM' : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
579 'BUILDERS' : { 'Alias' : Alias,
583 'Library' : StaticLibrary,
584 'StaticLibrary' : StaticLibrary,
585 'SharedLibrary' : SharedLibrary,
586 'Object' : StaticObject,
587 'StaticObject' : StaticObject,
588 'SharedObject' : SharedObject,
590 'PostScript' : PostScript,
591 'Program' : Program },
592 'SCANNERS' : [CScan, FortranScan],
593 'LIBDIRPREFIX' : '-L',
595 'LIBLINKPREFIX' : '-l',
596 'LIBLINKSUFFIX' : '',
601 elif os.name == 'nt':
604 versions = get_devstudio_versions()
605 ConstructionEnvironment = make_win32_env(versions[0]) #use highest version
606 except (SCons.Util.RegError, SCons.Errors.InternalError):
607 # Could not get the configured directories from the registry.
608 # However, the configured directories only appear if the user
609 # changes them from the default. Therefore, we'll see if
610 # we can get the path to the MSDev base installation from
611 # the registry and deduce the default directories.
614 MVSdir = get_msdev_dir(versions[0])
616 MVSVCdir = r'%s\VC98' % MVSdir
617 MVSCommondir = r'%s\Common' % MVSdir
619 extra_path = os.pathsep + os.environ['PATH']
622 ConstructionEnvironment = make_win32_env_from_paths(
623 r'%s\atl\include;%s\mfc\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir),
624 r'%s\mfc\lib;%s\lib' % (MVSVCdir, MVSVCdir),
625 (r'%s\MSDev98\Bin;%s\Bin' % (MVSCommondir, MVSVCdir)) + extra_path)
627 # The DevStudio environment variables don't exist,
628 # so just use the variables from the source environment.
629 MVSdir = r'C:\Program Files\Microsoft Visual Studio'
630 MVSVCdir = r'%s\VC98' % MVSdir
631 MVSCommondir = r'%s\Common' % MVSdir
633 include_path = os.environ['INCLUDE']
637 lib_path = os.environ['LIB']
641 exe_path = os.environ['PATH']
644 ConstructionEnvironment = make_win32_env_from_paths(