Contents
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! $
specify link option
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.
- make install
- make check
- make dist
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
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)
