#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Michel Mooij, michel.mooij7@gmail.com
'''
Summary
-------
Setup and configure multiple C/C++ build environments and configure
common tools for C/C++ projects. When using this module the following
tools will be loaded and configured automatically:
- cmake
- codeblocks
- cppcheck
- doxygen
- eclipse
- indent
- makefile
- msdev
- bdist
- tree
Usage
-----
The code snippet below provides an example of how a complete build environment
can be created allowing you to build, not only for the host system, but also
for one or more target platforms using, for instance, a C/C++ cross compiler::
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import os, waftools
from waftools import ccenv
top = '.'
out = 'build'
ini = os.path.abspath('ccenv.ini').replace('\\', '/')
VERSION = '0.0.1'
APPNAME = 'example'
def options(opt):
opt.load('ccenv', tooldir=waftools.location)
def configure(conf):
conf.load('ccenv')
def build(bld):
ccenv.build(bld, trees=['components'])
for var in ccenv.variants(ini):
for ctx in ccenv.contexts():
name = ctx.__name__.replace('Context','').lower()
class _t(ctx):
__doc__ = "%ss '%s'" % (name, var)
cmd = name + '_' + var
variant = var
When loading and configuring the *ccenv* tool, as shown in the example above, all
required C/C++ tools for each build environment variant (i.e. native or cross-
compile) will be loaded and configured as well; e.g. compilers, makefile-, cmake-,
eclipse-, codeblocks- and msdev exporters, cppcheck source code checking, doxygen
documentation creation will be available for each build variant. Additional (ccross)
compile build environments can be specified in a seperate .INI file (named ccenv.ini
in the example above) using following syntax::
[arm]
prefix = arm-linux-gnueabihf
[msvc]
c = msvc
cxx = msvc
The section name, *arm* in the example above, specifies the name of the compile
build environment variant. The prefix combined with compiler type (c,cxx) will be
used in order to create the concrete names of the cross compile toolchain
binaries::
AR = arm-linux-gnueabihf-ar
CC = arm-linux-gnueabihf-gcc
CXX = arm-linux-gnueabihf-g++
Concrete build scripts (i.e. wscript files) for components can be placed somewhere
within the *components* sub-directory. Any top level wscript file of a tree (being
*components* in this example) will be detected and incorporated within the build
environment. Any wscript files below those top level script files will have to be
included using the *bld.recurse('../somepath')* command from the top level script
of that tree.
'''
import os
import sys
try:
import ConfigParser as configparser
except:
import configparser
from waflib import Scripting, Errors, Logs, Utils, Context
from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext
from waflib.Tools.compiler_c import c_compiler
from waflib.Tools.compiler_cxx import cxx_compiler
import waftools
from waftools.codeblocks import CodeblocksContext
from waftools.makefile import MakeFileContext
from waftools.eclipse import EclipseContext
CCENV_INI='ccenv.ini'
CCENV_ARG='--ccenv'
CCENV_OPT='ccenv'
[docs]def options(opt):
'''Adds default command line options and tools for C/C++ projects.
:param opt: options context
:type opt: waflib.Options.OptionsContext
'''
opt.add_option('--debug', dest='debug', default=False, action='store_true',
help='build with debug information.')
opt.add_option('--all', dest='all', default=False, action='store_true',
help='execute command for build environments (host and variants)')
opt.add_option('--cchost', dest='cchost', default=False,
action='store_true',
help='use the default (native) C/C++ compiler for this platform')
opt.add_option(CCENV_ARG, dest=CCENV_OPT, default=CCENV_INI, action='store',
help='path to ccenv configuration file')
opt.load('compiler_c')
opt.load('compiler_cxx')
opt.load('cmake', tooldir=waftools.location)
opt.load('codeblocks', tooldir=waftools.location)
opt.load('cppcheck', tooldir=waftools.location)
opt.load('doxygen', tooldir=waftools.location)
opt.load('eclipse', tooldir=waftools.location)
opt.load('makefile', tooldir=waftools.location)
opt.load('msdev', tooldir=waftools.location)
opt.load('bdist', tooldir=waftools.location)
opt.load('tree', tooldir=waftools.location)
opt.load('indent', tooldir=waftools.location)
[docs]def build(bld, trees=[]):
'''Performs build context commands for one or more C/C++
build environments using the trees argument as list of source
directories.
:param bld: build context
:type bld: waflib.Build.BuildContext
:param trees: top level directories containing projects to build
:type trees: list
'''
if bld.variant:
libs = bld.env.CCENV[bld.variant]['shlib']
for lib in libs:
bld.read_shlib(lib, paths=bld.env.LIBPATH)
if bld.options.all and not bld.variant:
if bld.cmd in ('build', 'clean', 'install', 'uninstall', 'codeblocks', 'makefile', 'eclipse'):
for variant in bld.env.CCENV.keys():
Scripting.run_command('%s_%s' % (bld.cmd, variant))
for tree in trees:
for script in waftools.get_scripts(tree, 'wscript'):
bld.recurse(script)
[docs]def get_ccenv(fname):
'''Returns dictionary of variant C/C++ build environments. In which the keys
are the name of the actual variant C/C++ build environments and its values the
settings for that variant build environment.
:param fname: Complete path to the configuration file.
:type fname: str
'''
if not os.path.exists(fname):
Logs.warn("CCENV: ini file '%s' not found!" % fname)
ccenv = {}
c = configparser.ConfigParser()
c.read(fname)
for s in c.sections():
ccenv[s] = {'prefix' : None, 'shlib' : [], 'env' : [], 'c': ['gcc'], 'cxx': ['g++', 'cpp']}
if c.has_option(s, 'c'):
ccenv[s]['c'] = c.get(s,'c').split(',')
if c.has_option(s, 'cxx'):
ccenv[s]['cxx'] = c.get(s,'cxx').split(',')
if c.has_option(s, 'prefix'):
ccenv[s]['prefix'] = c.get(s,'prefix')
if c.has_option(s, 'shlib'):
ccenv[s]['shlib'] = [l for l in str(c.get(s,'shlib')).split(',') if len(l)]
if c.has_option(s, 'env'):
ccenv[s]['env'] = [l.split('\t') for l in c.get(s,'env').splitlines() if len(l)]
if c.has_option(s, 'host'):
ccenv[s]['host'] = c.get(s,'host')
return ccenv
[docs]def variants(fname=None):
'''Returns a list of variant names; i.e. a list of names for build environments
that have been defined in the 'ccenv.ini' configuration file.
:param fname: Complete path to the configuration file
:type fname: str
'''
if not fname:
fname = CCENV_INI
opt = '%s=' % CCENV_ARG
for a in sys.argv:
if a.startswith(opt):
fname = a.replace(opt, '')
ccenv = get_ccenv(fname)
return list(ccenv.keys())
[docs]def contexts():
'''Returns a list of build contexts to be used for variant build
environments.
'''
return [ BuildContext, CleanContext, InstallContext, UninstallContext, CodeblocksContext, MakeFileContext, EclipseContext ]