Skip to content

Writing a configure script

Raphaël Londeix edited this page Mar 29, 2014 · 6 revisions

The configuration is done by the function main(project, build) found in the file .config/project.py.

For the purpose of this tutorial, let's say we will configure a project that create a library, its python binding and an executable that use the library. Finally, we will copy some script files in the build directory.

Some variables and imports

All variable defined at the root of the configure script are available from the environment.

# The project name
NAME = "my-fancy-project"
# The version name (could be "alpha" for example)
VERSION_NAME = "prototype"

configure.py provides some handy tools

from configure import tools, path, platform
from configure.lang import cxx
from configure.lang import c

The function itself

For example, we retrieve the build type (defaulting to 'DEBUG'), and print a message with tools.status().

def main(project, build):
  build_type = project.env.get('BUILD_TYPE', 'DEBUG')
  project.env.build_set('BUILD_TYPE', build_type)
  tools.status(
    "Configuring project", project.env.NAME, '(%s)' % project.env.VERSION_NAME,
    'in', build.directory, '(%s)' % build_type
  )

Choosing the right compiler

Let's say we need a C++ compiler, gcc everywhere but windows:

if platform.IS_WINDOWS:
    Compiler = cxx.msvc.Compiler
else:
    Compiler = cxx.gcc.Compiler

To build our Compiler class previously selected, we prepare some defines:

if build_type == 'DEBUG':
    defines = ['MY_PROJECT_DEBUG']

And now, the compiler itself:

compiler = Compiler(
    project, build,
    position_independent_code = True,
    standard = 'c++11',
    defines = defines,
    include_directories = [
        path.join(build.directory, 'src'),
        path.absolute(project.directory, 'src'),
    ],
    use_build_type_flags = True,
)
status("CXX compiler is", compiler.binary)

Let me explain each argument (see doc on C++ compilers for all possible arguments):

  • project and build: The variable given to your main function.
  • position_independent_code: see gcc -fPIC flag.
  • standard: The standard used, here c++11.
  • defines: A list of defines, here debug flags.
  • include_directories: Additional include directories.
  • use_build_type_flags: Use basic debugging flags and optimisation flags.
  • hidden_visibility: Hide not exported symbols.

Adding libraries

Searching for C library with libraries.simple() function:

libs = []
for name in ['z', 'bz2']:
    libs.append(c.libraries.simple(name, compiler, shared=True))

Complex libraries can be implemented separately, let's say we need Boost and Python:

boost = cxx.libraries.BoostLibrary(
    compiler,
    components=['system', 'filesystem', 'python3', 'thread'],
    preferred_shared=False,
    python3_shared=True,
)
python = c.libraries.PythonLibrary(compiler, shared=True)

Linking executables

Let's start with the library called my:

libmy = compiler.link_shared_library(
    'libmy',
    tools.glob("src/libmy/*.cpp", recursive=True),
    directory  = 'release/lib',
    libraries = libs + boost.libraries,
    defines = ['MY_BUILD_DYNAMIC_LIBRARY'],
)

A precompiled header:

boost_python_pch = compiler.generate_precompiled_header(
    "src/boost/python.hpp"
)

A Boost.Python module:

libmy_binding = compiler.link_dynamic_library(
   'my',
   ['src/my_binding.cpp'],
   ext = python.ext,
   directory = "release/lib/python",
   libraries = boost.libraries + python.libraries + [libmy] + libs,
   precompiled_headers = [boost_python_pch],
)

We force the extension to be the one that python understand to be a python module extension and use libraries and precompiled header defined earlier. We can finally build our executable:

my_client = compiler.link_executable(
    "my",
    ["src/my.cpp"],
    directory = "release/bin",
    libraries=[libmy] + libs + boost.libraries,
)

Let's copy some files (for this tutorial ;)):

for src in glob("scripts/*.py", recursive=True):
    build.fs.copy(src, path.join('release', src))