Showing posts with label SCons. Show all posts
Showing posts with label SCons. Show all posts

Tuesday, June 14, 2011

Creating a Custom Builder in SCons

Suppose we need to create a custom builder in SCons. This custom builder will read a text file and append an author name at the end of the file.

import os

def my_builder(target, source, env):
    for i in range(0, len(source)):
       txt = open(source[i].abspath).read()
       txt = txt + os.linesep + "Author: " + env['AUTHOR']
       open(target[i].abspath, "wb").write(txt)

env = Environment()
env['BUILDERS']['MyBuilder'] = Builder(action = my_builder)

env.MyBuilder(['output1.txt', 'output2.txt'], ['input1.txt', 'input2.txt'], 
              AUTHOR = "Foo")

The custom builder allows an argument to be passed in into our custom builder. How we pass the argument is by using the Python's keyword argument, e.g. AUTHOR = "Foo". The custom builder function can then read this value from the env, e.g. env["AUTHOR"].

Wednesday, May 4, 2011

Getting Started with SCons to Build C/C++ Applications

SCons is a wonderful cross-platform build tool, mostly used for building C/C++ applications similar to make. It's based on Python and Python is an excellent programming language. Since Python is a fully-fledged programming language, there are many things that can be done easily in SCons as opposed to writing a shell script. In this blog, I'm going to show some basic stuff to get started with SCons.

SCons requires a file normally called SConstruct. The file structure in the below example looks like this.
scons-project
  |--- src/Hello.cpp
  |        Main.cpp
  |--- include/Hello.h
  |--- SConstruct

Hello.h
#ifndef HELLO_H_
#define HELLO_H_

class Hello {
public:
    Hello();
    virtual ~Hello();
    void sayHello() const;
};

#endif /* HELLO_H_ */

Hello.cpp
#include "Hello.h"
#include <iostream>
using namespace std;

Hello::Hello() {
}

Hello::~Hello() {
}

void Hello::sayHello() const {
    cout << "Hello!" << endl;
}

Main.cpp
#include "Hello.h"
#include <iostream>
using namespace std;

int main() {
    Hello hello;
    hello.sayHello();

#ifdef DEBUG
    cout << "Debugging..." << endl;
#endif
}

This is the simplest SConstruct file.
SConstruct
import os

env = Environment(CCFLAGS="-g -O0 -DDEBUG", CPPPATH="./include")

env.Program("bin/Main", ["src/Hello.cpp", "src/Main.cpp"])
This will produce an executable called Main. The CPPPATH here is to tell where the header files are located. And the CCFLAGS are the compiler flags used by the GCC compiler that tells the compiler to add the debug statement and turn off the optimization.

If we need to control how the object files are created, e.g. we want to put the object files in a directory called obj. We can use the Object node in our SConstruct. The SConstruct will look like this.
import os

env = Environment(CCFLAGS="-g -O0 -DDEBUG", CPPPATH="./include")

hello_obj = env.Object("obj/Hello", "src/Hello.cpp")
main_obj = env.Object("obj/Main", "src/Main.cpp")
env.Program("bin/Main", main_obj + hello_obj, LIBPATH="lib", LIBS="hello")

If we need to build a static library, i.e. libhello.a that can be linked against Main, we can modify the SConstruct to look this.
import os

env = Environment(CCFLAGS="-g -O0 -DDEBUG", CPPPATH="./include")

env.StaticLibrary("lib/hello", ["src/Hello.cpp"])
env.Program("bin/Main", ["src/Main.cpp"], LIBPATH="lib", LIBS="hello")

To build a shared library, i.e. libhello.so, we just need to change from StaticLibrary to SharedLibrary. The SConstruct will look like this.
import os

env = Environment(CCFLAGS="-g -O0 -DDEBUG", CPPPATH="./include")

env.SharedLibrary("lib/hello", ["src/Hello.cpp"])
env.Program("bin/Main", ["src/Main.cpp"], LIBPATH="lib", LIBS="hello")