SCons

SCons is the build tool written by Python. The configuration of build is written by Python. In addition, it is simpler to construct building environment for software than Make, because SCons has the function of analyzing dependency.

Hello, World with SCons

Let's build the following simple C program.

hello.c

#include <stdio.h>

int main (int argc, char *argv[]) {
    printf("Hello, World!\n");
    return 0;
}

Let's create the file of the name of SConstruct. This file is equivalent to Makefile for Make.

SConstruct

Program('hello', 'hello.c') # hello is the target

What you have to do is only this. It's simpler than Makefile. Run scons to complete building the program.

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o hello.o -c hello.c
gcc -o hello hello.o
scons: done building targets.
$ ./hello
Hello, World!
$

specify compile option

Program('hello', 'hello.c', CCFLAGS='-Wall -O2')

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o hello.o -c -Wall -O2 hello.c
gcc -o hello hello.o
scons: done building targets.
$ ./hello
Hello, World!
$

Program('hello', 'hello.c', LINKFLAGS='-pg')

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o hello.o -c hello.c
gcc -o hello -pg hello.o
scons: done building targets.
$

specify library

Program('hello', 'hello.c', LIBS='m') # link to libm. 

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o hello.o -c hello.c
gcc -o hello hello.o -lm
scons: done building targets.
$ ./hello
Hello, World!
$

specify include path

Program('hello', 'hello.c', CPPPATH='/usr/local/include')

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o hello.o -c -I/usr/local/include hello.c
gcc -o hello hello.o
scons: done building targets.
$ ./hello
Hello, World!
$

clean up

$ scons -c
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Cleaning targets ...
Removed hello.o
Removed hello
scons: done cleaning targets.
$

compare Makefile and SConstruct

Then let's compare Makefile and SConstruct for dtl. dtl is the library written by C++, dtl is developed by cubicdaiya(the author of this article).

Makefile for dtl's example programs

CPP               = g++
CPPFLAGS          = -c -O2 -Wall
OBJS_FLAGS        = -O2 -o
STRDIFF_OBJS      = strdiff.o common.o
STRDIFF3_OBJS     = strdiff3.o common.o
UNIDIFF_OBJS      = unidiff.o common.o
UNISTRDIFF_OBJS   = unistrdiff.o common.o
INTDIFF_OBJS      = intdiff.o
INTDIFF3_OBJS     = intdiff3.o
PATCH_OBJS        = patch.o common.o
FPATCH_OBJS       = fpatch.o common.o
ALL_TARGETS       = strdiff strdiff3 unidiff unistrdiff intdiff intdiff3 patch fpatch

DEPENDENCY_FILE   = Makefile.depend

.cpp.o:
        $(CPP) $(CPPFLAGS) $<

all : $(ALL_TARGETS)

strdiff : $(STRDIFF_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(STRDIFF_OBJS)

strdiff3 : $(STRDIFF3_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(STRDIFF3_OBJS)

unidiff : $(UNIDIFF_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(UNIDIFF_OBJS)

unistrdiff : $(UNISTRDIFF_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(UNISTRDIFF_OBJS)


intdiff : $(INTDIFF_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(INTDIFF_OBJS)

intdiff3 : $(INTDIFF3_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(INTDIFF3_OBJS)

patch : $(PATCH_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(PATCH_OBJS)

fpatch : $(FPATCH_OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(FPATCH_OBJS)

clean:
        ${RM} *.o *~ $(ALL_TARGETS)

depend:
        @echo "generating dependency file"
        @$(CPP) -MM *.cpp > $(DEPENDENCY_FILE)

include $(DEPENDENCY_FILE)

Above file has many targets. These targets are used to building dtl's example programs. And the dependencies are output to Makefile.depend with the option(-MM) of GCC and the command 'make depend'.

SConstruct for dtl's example programs

env = Environment(CCFLAGS='-Wall -O2')
targets = { 'strdiff'    : ['strdiff.cpp',    'common.cpp'],
            'intdiff'    : ['intdiff.cpp'],
            'strdiff3'   : ['strdiff3.cpp',   'common.cpp'],
            'intdiff3'   : ['intdiff3.cpp',   'common.cpp'],
            'unidiff'    : ['unidiff.cpp',    'common.cpp'],
            'unistrdiff' : ['unistrdiff.cpp', 'common.cpp'],
            'patch'      : ['patch.cpp',      'common.cpp'],
            'fpatch'     : ['fpatch.cpp',     'common.cpp']
            }

[ env.Program(target, targets[target]) for target in targets ]

Do you recognize that SConstruct is simpler than Makefile? In addtion, SConstruct is more programatic than Makefile, because SConstruct is written by Python. Furthermore, the target of 'depend' is not neccesary, because SCons analyzes dependencies when the command of scons is executed.

And Let's see the Makefile and SConstruct for dtl's test programs.

Makefile for dtl's test program

# Makefile for test programs

CPP        = g++
CPPFLAGS   = -c -O2 -Wall
OBJS_FLAGS = -O2 -o
LIBS       = -lgtest
OBJS       = dtl_test.o

DEPENDENCY_FILE = Makefile.depend

.cpp.o:
        $(CPP) $(CPPFLAGS) $< $(LIBS)

dtl_test : $(OBJS)
        $(CPP) $(OBJS_FLAGS) $@ $(OBJS) $(LIBS)

check : dtl_test
        @./dtl_test

clean :
        ${RM} *.o *~ dtl_test

depend:
        @echo "generating dependency file"
        @$(CPP) -MM *.cpp > $(DEPENDENCY_FILE)

include $(DEPENDENCY_FILE)

Basically the concept of the above Makefile is same as the earlier Makefile. But above Make file has the target of check, because this is the test programs. If the command of 'scons check' is executed, dtl's test is executed.

SConstruct for dtl's test program

env  = Environment(CCFLAGS='-Wall -O2', LIBS='gtest')
test = env.Program('dtl_test', ['dtl_test.cpp'])
test_alias = env.Alias('check', test, test[0].abspath)
env.AlwaysBuild(test_alias)

The configuration of build is shorter as same as the configuration of build for example progurams. Insted, it is necessary to follow the troublesome procedure to do what is same as 'make check'.

Can SCons become the alternative tool of Make?

I note here that you can write the configuration of build simply, if you use SCons. And SCons is sperior to Make in term of extensibility and flexibility as SConstruct is written by Python

But there are the troublesome parts, compare to Make( and Autotools). For example, you have the opportunities that you execute the following commands if you use Autotools.

As the process that relevants these commands are generated automatically, You don't have to write. But you have to write with yourself, because SCons don't have such function. Refer to the configuration of build of dtl's test programs about the process like 'make check'.

Though it is necessary to write dependencies previously with Make, you don't have to write dependencies with SCons. And SCons detects the difference that OS is Windows or Linux(and other UNIXs), for examples. This is the great advantage, but SCons has many works. So, the build with SCons may take long time.

In fact, I heard SCons is extremely slow in big software project. This point is not the really matter in the build with Make as the dependencies are already written to Makefile.1

Reference

  1. Instead, shown by the above examples, you have to make the target of depend and execute the command 'make depend' regularly. This is troublesome. (1)

SCons/SConsForUserOfMake (last edited 2010-10-02 19:05:55 by bokko)