### Copyright (c) 2003 by Scott Lystig Fritchie, ### See the file "LICENSE" for full license terms. import os import string import re import SCons.Environment import SCons.Script.SConscript import SCons.Node.FS import SCons.Builder ### ### All of the arguments to these functions are strings unless ### otherwise noted. For lists of things, we assume that the ### things are separated by whitespace (any character that ### string.split() considers whitespace). ### ### The whitespace splitting and (occasionally) joining/adding ### can be a bit of a pain, but it seems easier than dealing ### with the Python rules for appending (or using the "+" ### operator) on lists vs. on strings. ### ### ### Subdirs -- Read the SConscript files in the specified subdirectories. ### ### NOTE: We assume that the environment we'll be using from the ### caller is "env". Since this function is always called by my ### SConscripts in the boilerplate at the beginning, this has always ### been a safe assumption. Caveat utilitor. ### def Subdirs(dirlist): for file in _subconf_list(dirlist): #print "Subdirs: processing", file SCons.Script.SConscript.SConscript(file, "env") ### ### Program -- Specify the source files in the current directory ### to be used to create the target executable. Any additional ### libraries required for linking should be specified with ### AddLibs. ### def Program(env, target, source): env.Program(target, string.split(source)) ### ### AddCFlags -- Append flags to $CPPFLAGS. ### ### NOTE: Do not use for: ### 1. "-Ipath" flags for header file directories. Use AddIncludeDirs. ### 2. "-Lpath" flags for library directories. Use AddLibDirs. ### ### NOTE: FWIW, the sanity checking is compiler-specific. ### ### NOTE: SCons make a distinction between C/C++ preprocessor options ### (stored in $CPPFLAGS), options only for the C compiler ### (stored in $CCFLAGS), and options only for the C++ compiler ### (stored in $CXXFLAGS). See the SCons man page for full ### details. This function will append to $CPPFLAGS. ### def AddCFlags(env, str): str = re.sub("\n", " ", str) # Newline in flags is bad juju if re.search("-I", str): print "AddCFlags: Warning: -I flag found in '%s' in %s" %\ (str, SCons.Node.FS.default_fs.Dir(".")) print "\tPlease use the Mylib.AddIncludeDirs() function instead!\n" env.Append(CPPFLAGS = " " + str) ### ### AddIncludeDirs -- Specify directories to be added to compiler's ### header file search path. ### def AddIncludeDirs(env, str): env.Append(CPPPATH = string.split(str)) ### ### AddLibs -- Append libraries to the current environment. ### def AddLibs(env, str): env.Append(LIBS = string.split(str)) ### ### AddLibDirs -- Specify directories to be added to compiler's ### library search path. ### def AddLibDirs(env, str): env.Append(LIBPATH = string.split(str)) ### ### StaticLibrary -- Create a static library. ### def StaticLibrary(env, target, source): env.StaticLibrary(target, string.split(source)) ### ### SharedLibrary -- Same as StaticLibrary, but use the files in ### source to create a shared/dynamically loadable library. ### def SharedLibrary(env, target, source): env.SharedLibrary(target, string.split(source)) ### ### StaticLibMergeMembers -- Like StaticLibrary, but the objects that ### go into the library are not all found in the same directory. Use ### this function to specify which objects will go into the final ### library file. ### ### WARNING: The way this is implemented assumes that the directory ### that "libname" will be eventually be created is a *parent* ### of the directory that contains "files". Furthermore, we ### are assuming that the SConscripts will process ### "libnames"'s member object files before calling ### CreateMergedStaticLibrary. (This assumption is safe ### if using the boilerplate used by all the SConscript ### files in this example.) ### ### The dir containing "libname" and "files" may be the ### *same* dir, but then the StaticLibMergeMembers function ### call(s) **must** be called before CreateMergedStaticLibrary. ### def StaticLibMergeMembers(local_env, libname, files): hackpath = os.path.join("#", str(SCons.Node.FS.default_fs.Dir("."))) for file in string.split(files): objname = re.sub("\.(c|C|cpp|CPP|cxx|CXX|c\+\+|C\+\+|cc|CC)$", local_env["OBJSUFFIX"], file) local_env.Object(target = objname, source = file) e = "local_env[\"TopEnv\"].Append(%s = [\"%s\"])" % (libname, os.path.join(hackpath, objname)) exec(e) ### ### CreateMergedStaticLibrary -- Use in the directory where the ### library file will be created. ### def CreateMergedStaticLibrary(env, libname): objpaths = env["TopEnv"][libname] libname = "%s%s%s" % (env["LIBPREFIX"], libname, env["LIBSUFFIX"]) env.StaticLibMerge(target = libname, source = objpaths) ### ### Gen_StaticLibMerge -- The generator trigged by ### CreateMergedStaticLibrary. ### def Gen_StaticLibMerge(source, target, env, for_signature): target_string = "" for t in target: target_string = str(t) subdir = os.path.dirname(target_string) srclist = [] for src in source: srclist.append(src) return [["ar", "cq"] + target + srclist, ["ranlib"] + target] ### ### ExportHeader -- Copy header files into the $EXPORT_INCLUDE ### directory. This isn't the sort of scheme that everyone is fond ### of ... myself included. However, Mylib was cooked up to mimic ### a pre-existing, "make"-driven development environment. In that ### environment, all header files used by some other software ### component is copied into the "export" directory. ### ### Although this scheme can cause headaches if a less-current ### version of a header remains in the "export" dir, there are a ### couple of reasons why I keep ExportHeader here: ### ### 1. Unlike poorly-written Makefiles, SCons doesn't forget to copy ### the most recent version of all headers to the "export" dir. :-) ### 2. In the project Mylib was written for, the alternative was to ### include over 75 (!) "-I" directives for each "gcc" command line. ### def ExportHeader(env, headers): env.Install(dir = env["EXPORT_INCLUDE"], source = string.split(headers)) ### ### ExportStaticLibrary -- Same reason that ExportHeader exists: to ### reduce the number of "-L" directives on each "gcc" command line. ### If you've got some other way of telling SCons where all your ### librarys' parent directories are and would rather not use this ### function, then don't use it. :-) ### def ExportStaticLibrary(env, libs): target_dir = env["EXPORT_LIB"] for lib in string.split(libs): libfile = "%s%s%s" %\ (env["LIBPREFIX"], lib, env["LIBSUFFIX"]) env.Install(dir = target_dir, source = libfile) #g_env = env["TopEnv"] #g_env.Append(LibraryList = "%s/%s" % (target_dir, libfile)) ### ### InstallBin -- Copy a list of executables to the $INSTALL_BIN ### directory. For general usage, this function would probably take ### another argument, namely the directory into which the executables ### should be copied.... ### def InstallBin(env, bins): env.Install(dir = env["INSTALL_BIN"], source = string.split(bins)) ### ### CompileIDL -- Included here as a proof of concept ... it probably ### will not work as-is for you. ### def CompileIDL(env, idlfile, xflags = ""): current_dir = SCons.Node.FS.default_fs.Dir(".") base = string.replace(idlfile, ".idl", "") l_env = env.Copy() # # Set up two special environment vars to be used by the builder: # __XFLAGS: any extra flags to be passed to the IDL compiler # __OUTPUT_DIR: directory store the IDL compiler's output # l_env["__XFLAGS"] = xflags l_env["__OUTPUT_DIR"] = current_dir # # We've have an emitter that spits out correct targets, so # don't list any generated files here. # l_env.CompileIDL(target = [], source = idlfile) ### ### Emitter_IDL -- Included here as a proof of concept ... it probably ### will not work as-is for your IDL compiler. ### def Emitter_IDL(target, source, env): base = string.replace(source[0], ".idl", "") (f1, f2, f3, f4) = string.split("%s.cpp %s.h %s_skel.cpp %s_skel.h" %\ (base, base, base, base)) return [f1, f2, f3, f4], source ### ### Misc helper functions. ### def _subconf_list(dirlist): return map(lambda x: os.path.join(x, "SConscript"), string.split(dirlist))