SCons for DevelopersARCHIVED COPY - Obsolete - for current version, click here. Be sure to read SCons:
An Introduction, to familiarize yourself Notes:
SLAC Linux SCons InstallationSCons is installed at SLAC in: /afs/slac/g/glast/applications/SCons/version/bin/scons e.g., /afs/slac/g/glast/applications/SCons/1.3.0/bin/scons Desktop MachinesPrerequisites
Developer ReleasesDeveloper Releases provide all source and associated files required for rebuilding and debugging the code as necessary as well as all installed files created when the release is built. Developer Releases are not (yet) available for all RM builds. When available, they may be obtained from the RMViewer. Pick the release and platform you're interested in and check the Download menu in the upper left. SAS Use of SConsSupport for SCons involves several types of locally-written files. Two of these are specific to individual packages: SConscript and xxLib.py (where "xx" represents package name; e.g., facilitiesLib.py). All packages must have an SConscript file. The package maintainer should be familiar with it and capable of modifying it if, for example, build products for the package change. xxLib.py is only needed for packages which build libraries and only very rarely needs to be modified. These files are discussed in more detail in the following sections. Remaining support files — e.g. SConstruct, allExternals.scons, containerSettings/externals.scons — are global to any individual package and are only occasionally of interest to package developers . Developers can freely modify their local copies but should not commit changes to CVS without contacting the package owner: SCons maintenance crew for SConstruct or anything else in the SConsFiles package; GlastRelease (ScienceTools) head honcho for files in GlastRelease-scons/containerSettings (respectively ScienceTools-scons/containerSettings). Minimal SConscript FileThe following example shows a minimal SConscript file for the imaginary package myPkg which will build nothing, but which will import some of the tools which may be necessary to build typical package targets. This file should be stored in the top level directory of the package. Note SConscript files are Python code, following the usual Python syntax. In particular, # indicates that the rest of the text on the line is a comment. Such comments will be rendered in red in all examples to follow.
The first two lines are strongly recommended. The first identifies the file as Python; the second embeds current CVS versioning information in the file.
The two Import() calls import two SCons objects that have been defined at the top level; baseEnv is the basic build environment created by the top level, and includes all necessary options such as debug, optimized, third party library locations, etc.
listFiles is a function defined at the top level. This function should be used any time you wish to pass a list of files to be included for creating a shared library, static library, program, etc. It is imported in thisexample because most packages end up using it. As will be demonstrated in examples to follow, listFiles can do wildcard expansion. Simple Static LibraryAfter creating the SConscript file, targets to be compiled by SCons are created in a static library. The additional code needed to build this library (and assuming the package has no other targets) is shown below:
The first line creates a static library object containing all the information necessary to build the library at a later point. Notice the libEnv variable that is used, creating one of the copies made of the base environment. This copy should NEVER be used for creating an application because applications need different link options (must link to the library) from those required for the library itself.
The StaticLibrary function call takes two arguments. The first specifies the name that should be given to the library. This name should not include any prefixes/suffixes that are platform specific. SCons will take care of adding those automatically. On Unix-based systems, this example would create a library named libmyLib.a. The second argument, is a list of files to be compiled into the library. In this case, it is specified that the files to be included are in the src directory, relative to the top directory of the package, and are named *.cxx (i.e., anything ending with .cxx). Should only a single file be needed for the compilation of that library, the listFiles invocation may be replaced with a single-element list, e.g. ['src/myLib.cxx'] The final line registers objects at the top level to be compiled when appropriate. (This is not a standard SCons tool; rather, it is a custom extension to SCons.) Note that the progEnv copy of the environment is typically used to register objects, even if the objects are libraries, and it is strongly recommended that you abide by this convention. Arguments used by the Tool() call are as follows:
Simple Shared LibraryThe code for a package with a simple shared library target (i.e., not a swig library, not a Gaudi component library, and so forth) would look something like this:
The first line, an invocation of the locally written tool addLinkDeps is required because dependencies for shared libraries are treated differently for different OSes. addLinkDeps will do the right thing for each OS. There is no counterpart in the static library example because for static libraries all OSes require the same behavior (which is to not add any dependencies on other libraries). Note that the call libEnv.SharedLibrary( ) is used instead of the libEnv.StaticLibrary( ) call; however, the argument structure is identical. In this example sources are listed explicitly rather than using listFiles As before, this object must be registered to be included in any SCons builds. Since it's a different type of object, a different keyword (libraryCxts) is used, but the syntax is otherwise identical to the static library case. Simple ApplicationCreating a simple application requires that we add a section similar to the following:
The progEnv copy of the base environment is used to create a program objection.
The arguments provided to the progEnv.Program() call are similar to those for libraries:
And, just as in the previous sections, the program object must be registered with the top level before it can be used. Assuming the static and shared libraries from before still exist and we want to add this program to the registration call, we would modify the registration line as follows:
The binaryCxts keyword takes a list of two-element lists [program-object, associated-environment], similar to libraryCxts.
Elements of a Typical SConscriptNow that we've seen several examples, it's time to step back and note common features of SConscript files. Construction EnvironmentsEach target (library or executable) is built in the context of a construction environment. The original environment, baseEnv, which has been created with appropriate global settings, is repeatedly cloned as needed within package SConscript files with lines like libEnv = baseEnv.Clone() A different copy of the base environment is needed whenever the target requires different settings (e.g. compiler options, linker options, collection of libraries included in the link) than other targets. For example, in a package which builds a library and executables, the executables need to link to the library but of course the library doesn't link to itself. Therefore the executables cannot use the library copy (by convention called libEnv); They need their own copy, progEnv. If a particular executable requires different compiler settings or must link to an additional library not used by the others, it, too, will need a separate copy of baseEnv. Swig wrapper libraries need to link to the "regular" package library and require other special settings not appropriate for other targets, so they also need their own environment. SConscript TemplateSConscripts are composed of the following:
OS Specific ConditionsTo perform functions on certain platforms only, use regular python conditionals around the functions. For example, to define the TRAP_FPE macro only on Linux platforms, append:
The platform.system() call returns the name of the OS. In this case, we wish to know if we are running on a Linux platform and, if that is the case, we wish to add a -DTRAP_FPE to the gcc command line. Note: The progEnv.Append() call is explained later. (See Compiler Options.) Libraries that Depend on Other LibrariesSCons performs dependency computations at the source code level. It does not compute dependencies of various binary packages such as the dependency of library A on library B when compiling library A into application A. A package maintainer writing application A does not wish to know all dependencies of all libraries; he only needs to know the DIRECT dependencies of the application. Similarly, the package maintainer of library A should not have to know all the dependencies of library B when creating library A; he only needs to know that library A depends on library B. SCons, by default, does not have this ability. The package maintainer for A only has to know that application A depends on library A, but the owner also has to know that library A depends on library B, ... and so on, until all dependencies have been met. Fortunately, SCons provides a "tool" that simplifies this problem so that only direct dependencies need be specified. When the package owner creates library A in the SConscript files, the package owner also creates an additional file to record the DIRECT dependencies of library A. This file must have a specific name called <package>Lib.py, and it has to be located in the top level of the package, together with the SConscript file. In our example, the owner of 'myPackage' has two libraries: myLib and mySharedLib. Assume that myLib DIRECTLY depends on some other library of some other package (called someOtherPackage) and myLib also depends on the external library xerces. The contents of myPackageLib.py would be as follows:
Both of these python functions need to exist at all times. The second of these functions (exists()) is for features currently not used by us, so it should always be specified as shown. The first function is what creates the recursive computation of library dependencies. The first line in the if statement adds libA to the dependencies. This line is put inside an if statement that determines if it should be added or not, because the dependencies of libA need to be specified when libA is created. However, when libA is being created, we can't specify that it should include libA. This would create a recursive dependency. As a result, when we want to build libA and we want to catch all the dependencies (not always the case; see following section), we call this function; but we also pass an additional argument setting depsOnly = 1, so that the recursive dependency isn't created. The remaining lines — those of the form Note: These must be only DIRECT dependencies to keep computation fast. Unnecessary listings will slow SCons down considerably. How and When to Add Library Dependencies to an EnvironmentAny package myPkg building a library needs a file myPkgLib.py as described above to list its direct dependencies, but the circumstances under which a particular target needs those dependencies are not immediately obvious.
Because of this complication, another tool, addLinkDeps has been written to encapsulate the information of whether or not myPkgLib.py should be invoked (and to invoke it if appropriate) when the shared library belonging to myPkg is built. addLinkDeps takes the following keyword arguments:
For a package which builds a "simple" shared library and a swig library wrapping it, lines similar to the following should be included in the package SConscript:
Details will vary depending on naming convention you choose to use for the libraries and the .i file Currently there's no way to specify which library created by a single package we wish to use. We recommend that developers organize their packages such that each package builds at most one library, static or shared, that another target might need to link to. (Swig libraries don't count since they never appear in link commands.) Application Dependence on LibrariesWith the dependency tree generated by the previous section for libraries, package owners wishing to create dependencies on libraries for their applications need only list the DIRECT dependencies of their applications. In our example, the owner of myPackage currently has one application myApp. Assuming this application depends on some library from myPackage as well as the external library ROOT DIRECTLY, he would add these lines to his SConscript file:
Technically, the ordering of these two calls does not matter. However, it is strongly recommended that these calls be made prior to the call for generating the application. This will make it easier for a human to understand the code at a later point. The first line will call the generate function from myPackageLib created as shown in the previous section. (The alternate version, invoking addLinkDeps, does precisely the same thing.) That function will add the library from that package to the dependencies of myApp, along with any other libraries that myPackage's library depends on. The second call adds ROOT to the libraries that myApp depends on DIRECTLY. Should myApp not need ROOT directly but through some other package's library, it should not be included here. It is the responsibility of that package to add the ROOT dependency. Arguments to RegisterTargetsRegistration functionality is an extension of SCons created by us. The registration is done by a call to progEnv.Tool('registerTargets, 'mypackage', ...). There are a minimum of two arguments. The first argument has to always be 'registerTarget', and the second argument always has to be the name of the package performing the function call. Additional arguments, all optional, can be any of the following:
Note: The complicated argument structure for registering libraries and executables, and the distinctions among all the different kinds of libraries are required in order to build project files on Windows. Compiler OptionsSCons provides some compiler independent options, which should be used as much as possible. Less common options have to be specified in a compiler dependent way; therefore, prior to setting a compiler option for a less common option, one should perform a check for which OS is being executed. (See OS Specific Conditions.) Caution! In almost all cases these compiler options are lists that need to be appended to. If they are simply assigned new values, they will overwrite older options already defined. And..., in order to prevent repeating the same compile options several times, one should only append if the option does not exist. These options are available through a single function call: AppendUnique(). For example, to add a unique preprocessor definition to the compiler when compiling an application you would do:
This would add a -DTRAPF_FPE to the compiler options if one didn't already exist. For a complete version of these compiler options, see http://scons.org. Common Platform-independent Compiler OptionsThe following compiler options appear frequently enough that SCons takes care of converting them to the format required by the target compiler, and it is recommended that you use them when possible:
Less Common Platform-dependent Compiler OptionsThe following compiler options need to be specified differently, depending on which compiler is being used, and should therefore be wrapped around if statements:
External Libraries
All the directories containing external libraries are automatically added to the library path of the compiler. Each package only needs to add the library names it will need. For example, to add CLHEP to list of libraries linked to, you would use:
Also see: External Libraries: Directory Structure.
|