Skip to content
Mats Wichmann edited this page May 25, 2020 · 4 revisions

EXAMPLES

To help you get started using scons, this section contains a brief overview of some common tasks.

env = Environment()
env.Program(target = 'foo', source = 'foo.c')

Note: Build the file by specifying the target as an argument ("scons foo" or "scons foo.exe"). or by specifying a dot ("scons .").

env = Environment()
env.Program(target = 'foo', source = Split('f1.c f2.c f3.c'))
env = Environment(CCFLAGS = '-g')
env.Program(target = 'foo', source = 'foo.c')

Note: You do not need to set CCFLAGS to specify -I options by hand. scons will construct the right -I options from CPPPATH.

env = Environment(CPPPATH = ['.'])
env.Program(target = 'foo', source = 'foo.c')
env = Environment(CPPPATH = ['include1', 'include2'])
env.Program(target = 'foo', source = 'foo.c')
env = Environment()
env.StaticLibrary(target = 'foo', source = Split('l1.c l2.c'))
env.StaticLibrary(target = 'bar', source = ['l3.c', 'l4.c'])
env = Environment()
env.SharedLibrary(target = 'foo', source = ['l5.c', 'l6.c'])
env.SharedLibrary(target = 'bar', source = Split('l7.c l8.c'))
env = Environment(LIBS = 'mylib', LIBPATH = ['.'])
env.Library(target = 'mylib', source = Split('l1.c l2.c'))
env.Program(target = 'prog', source = ['p1.c', 'p2.c'])

Notice that when you invoke the Builder, you can leave off the target file suffix, and scons will add it automatically.

bld = Builder(action = 'pdftex < $SOURCES > $TARGET'
              suffix = '.pdf',
              src_suffix = '.tex')
env = Environment(BUILDERS = {'PDFBuilder' : bld})
env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex')

# The following creates "bar.pdf" from "bar.tex"
env.PDFBuilder(target = 'bar', source = 'bar')

Note also that the above initialization overwrites the default Builder objects, so the Environment created above can not be used call Builders like env.Program, env.Object, env.StaticLibrary etc.

bld = Builder(action = 'pdftex < $SOURCES > $TARGET'
              suffix = '.pdf',
              src_suffix = '.tex')
env = Environment()
env.Append(BUILDERS = {'PDFBuilder' : bld})
env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex')
env.Program(target = 'bar', source = 'bar.c')

You also can use other Pythonic techniques to add to the BUILDERS construction variable, such as:

env = Environment()
env['BUILDERS]['PDFBuilder'] = bld

The following example shows adding an extremely simple scanner (the kfile_scan() function) that doesn't use a search path at all and simply returns the file names present on any include lines in the scanned file. This would implicitly assume that all included files live in the top-level directory:

import re

include_re = re.compile(r'^include\s+(\S+)$', re.M)

def kfile_scan(node, env, path, arg):
    contents = node.get_text_contents()
    includes = include_re.findall(contents)
    return env.File(includes)

kscan = Scanner(name = 'kfile',
                function = kfile_scan,
                argument = None,
                skeys = ['.k'])

scanners = DefaultEnvironment()['SCANNERS']
scanners.append(kscan)
env = Environment(SCANNERS=scanners)

env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET')

bar_in = File('bar.in')
env.Command('bar', bar_in, 'kprocess $SOURCES > $TARGET')
bar_in.target_scanner = kscan

It is important to note that you have to return a list of File nodes from the scan function, simple strings for the file names won't do. As in the examples we are showing here, you can use the File() function of your current Environment in order to create nodes on the fly from a sequence of file names with relative paths.

Here is a similar but more complete example that adds a scanner which searches a path of directories (specified as the MYPATH construction variable) for files that actually exist:

import re
import os
include_re = re.compile(r'^include\s+(\S+)$', re.M)

def my_scan(node, env, path, arg):
    contents = node.get_text_contents()
    includes = include_re.findall(contents)
    if includes == []:
        return []
    results = []
    for inc in includes:
        for dir in path:
            file = str(dir) + os.sep + inc
            if os.path.exists(file):
                results.append(file)
                break
    return env.File(results)

scanner = Scanner(name='myscanner',
                  function=my_scan,
                  argument=None,
                  skeys=['.x'],
                  path_function=FindPathDirs('MYPATH')
                 )

scanners = DefaultEnvironment()['SCANNERS']
scanners.append(scanner)
env = Environment(SCANNERS=scanners, MYPATH=['incs'])

env.Command('foo', 'foo.x', 'xprocess < $SOURCES > $TARGET')

The FindPathDirs() function used in the previous example returns a function (actually a callable Python object) that will return a list of directories specified in the $MYPATH construction variable. It lets scons detect the file incs/foo.inc, even if foo.x contains the line include foo.inc only. If you need to customize how the search path is derived, you would provide your own path_function argument when creating the Scanner object, as follows:

# MYPATH is a list of directories to search for files in
def pf(env, dir, target, source, arg):
    top_dir = Dir('#').abspath
    results = []
    if 'MYPATH' in env:
        for p in env['MYPATH']:
            results.append(top_dir + os.sep + p)
    return results

scanner = Scanner(name='myscanner',
                  function=my_scan,
                  argument=None,
                  skeys=['.x'],
                  path_function=pf
                 )

Notice that the file names specified in a subdirectory's SConscript file are relative to that subdirectory.

SConstruct:

env = Environment()
env.Program(target = 'foo', source = 'foo.c')

SConscript('sub/SConscript')

sub/SConscript:

env = Environment()
# Builds sub/foo from sub/foo.c
env.Program(target = 'foo', source = 'foo.c')

SConscript('dir/SConscript')

sub/dir/SConscript:

env = Environment()
# Builds sub/dir/foo from sub/dir/foo.c
env.Program(target = 'foo', source = 'foo.c')

You must explicitly call Export and Import for variables that you want to share between SConscript files.

SConstruct:

env = Environment()
env.Program(target = 'foo', source = 'foo.c')

Export("env")
SConscript('subdirectory/SConscript')

subdirectory/SConscript:

Import("env")
env.Program(target = 'foo', source = 'foo.c')

Use the variant_dir keyword argument to the SConscript function to establish one or more separate variant build directory trees for a given source directory:

SConstruct:

cppdefines = ['FOO']
Export("cppdefines")
SConscript('src/SConscript', variant_dir='foo')

cppdefines = ['BAR']
Export("cppdefines")
SConscript('src/SConscript', variant_dir='bar')

src/SConscript:

Import("cppdefines")
env = Environment(CPPDEFINES = cppdefines)
env.Program(target = 'src', source = 'src.c')

Note the use of the Export method to set the "cppdefines" variable to a different value each time we call the SConscript function.

SConstruct:

env = Environment(LIBPATH=['#libA', '#libB'])
Export('env')
SConscript('libA/SConscript')
SConscript('libB/SConscript')
SConscript('Main/SConscript')

libA/SConscript:

Import('env')
env.Library('a', Split('a1.c a2.c a3.c'))

libB/SConscript:

Import('env')
env.Library('b', Split('b1.c b2.c b3.c'))

Main/SConscript:

Import('env')
e = env.Clone(LIBS=['a', 'b'])
e.Program('foo', Split('m1.c m2.c m3.c'))

The # in the LIBPATH directories specify that they're relative to the top-level directory, so they don't turn into Main/libA when they're used in Main/SConscript

Specifying only 'a' and 'b' for the library names allows scons to append the appropriate library prefix and suffix for the current platform (for example, liba.a on POSIX systems, a.lib on Windows).

The following would allow the C compiler to be specified on the command line or in the file custom.py.

vars = Variables('custom.py')
vars.Add('CC', help='The C compiler.')
env = Environment(variables=vars)
Help(vars.GenerateHelpText(env))

The user could specify the C compiler on the command line:

scons "CC=my_cc"

or in the custom.py file:

CC = 'my_cc'

or get documentation on the options:

$ scons -h

CC: The C compiler.
    default: None
    actual: cc

Since windows.h includes everything and the kitchen sink, it can take quite some time to compile it over and over again for a bunch of object files, so Microsoft provides a mechanism to compile a set of headers once and then include the previously compiled headers in any object file. This technology is called precompiled headers. The general recipe is to create a file named "StdAfx.cpp" that includes a single header named "StdAfx.h", and then include every header you want to precompile in "StdAfx.h", and finally include "StdAfx.h" as the first header in all the source files you are compiling to object files. For example:

StdAfx.h:

#include <windows.h>
#include <my_big_header.h>

StdAfx.cpp:

#include <StdAfx.h>

Foo.cpp:

#include <StdAfx.h>

/* do some stuff */

Bar.cpp:

#include <StdAfx.h>

/* do some other stuff */

SConstruct:

env=Environment()
env['PCHSTOP'] = 'StdAfx.h'
env['PCH'] = env.PCH('StdAfx.cpp')[0]
env.Program('MyApp', ['Foo.cpp', 'Bar.cpp'])

For more information see the document for the PCH builder, and the PCH and PCHSTOP construction variables. To learn about the details of precompiled headers consult the MSDN documentation for /Yc, /Yu, and /Yp.

Since including debugging information in programs and shared libraries can cause their size to increase significantly, Microsoft provides a mechanism for including the debugging information in an external file called a PDB file. scons supports PDB files through the PDB construction variable.

SConstruct:

env=Environment()
env['PDB'] = 'MyApp.pdb'
env.Program('MyApp', ['Foo.cpp', 'Bar.cpp'])
Clone this wiki locally