hwaf-tuto is a simple tutorial to show how to install hwaf and use it.
hwaf is a Go binary produced by the Go toolchain. So, if you have already the Go toolchain installed (see here for instructions) you just have to do:
$ go get github.com/hwaf/hwafto get the latest hwaf tool (and its git goodies) installed and ready.
Packaged up binaries for hwaf are also available here. Untar under some directory like so (for linux 64b):
$ mkdir local $ cd local $ curl -L \ http://cern.ch/hwaf/downloads/tar/hwaf-20130820-linux-amd64.tar.gz \ | tar zxf - $ export HWAF_ROOT=`pwd` $ export PATH=$HWAF_ROOT/bin:$PATHThe central tool in hwaf is the concept of the workarea, where locally checked out packages will live.
To create such a workarea:
$ cd dev # create a workarea named 'work' $ hwaf init work $ cd workhwaf supports multi-projects builds, that is: staged builds. When you create a workarea, you can instruct it to use the definitions and objects defined by parent projects, using the hwaf setup command:
$ cd work $ hwaf setup -p /path/to/a/project/installSimple builds, such as our tutorial, don't need that, so just do:
$ cd work $ hwaf setup $ hwaf show setup workarea=/home/binet/dev/work cmtcfg=x86_64-archlinux-gcc47-opt projects=We can run the configure command to test whether all dependencies are installed and generate the bootstrap code to be able to compile:
$ hwaf configure Setting top to : /home/binet/dev/work Setting out to : /home/binet/dev/work/__build__ Manifest file : /home/binet/dev/work/.hwaf/local.conf Manifest file processing : ok Checking for 'g++' (c++ compiler) : g++ Checking for 'gcc' (c compiler) : gcc ================================================================================ project : work-0.0.1 prefix : install-area pkg dir : src variant : x86_64-archlinux-gcc47-opt arch : x86_64 OS : archlinux compiler : gcc47 build-type : opt projects deps : None install-area : install-area njobs-max : 2 ================================================================================ [...] The equivalent of the good ol' Makefile for hwaf is the wscript file. It is (ATM) a python file with a few mandatory functions. hwaf ships with a command to create a new package. Let's do that:
$ cd work $ hwaf pkg create mytools/mypkg $ hwaf pkg ls src/mytools/mypkg (local) $ cat src/mytools/mypkg/wscript# -*- python -*- # automatically generated wscript import waflib.Logs as msg PACKAGE = { 'name': 'mytools/mypkg', 'author': ["Sebastien Binet"], } def pkg_deps(ctx): # put your package dependencies here. # e.g.: # ctx.use_pkg('AtlasPolicy') return def configure(ctx): msg.debug('[configure] package name: '+PACKAGE['name']) return def build(ctx): # build artifacts # e.g.: # ctx.build_complib( # name = 'mypkg', # source = 'src/*.cxx src/components/*.cxx', # use = ['lib1', 'lib2', 'ROOT', 'boost', ...], # ) # ctx.install_headers() # ctx.build_pymodule(source=['python/*.py']) # ctx.install_joboptions(source=['share/*.py']) returnpkg_deps is where one lists the package dependencies:
- build tools to use,
- external binaries, external libraries, ...
- 3rd-party
hwafbuild utils, ... - packages defining new build rules, ...
The argument to this function is a waf.Context object which:
- encapsulates the current environment of the build,
- gives access to the file system
- gives access to build/configure functions
Our simple mytools/mypkg package does not have any dependency, so nothing is required there.
configure is where one configures the package or project. There, we can discover external libraries/binaries, load new build tools/functions and/or define new environment variables.
Let's try to detect whether our system has CLHEP installed but don't fail the build if it does not find it.
Also, our package will build a C++ library with a few symbols exported w/o any mangling so that it can be imported and used from python. We then have to configure our package to check whether python can be detected, and declare the PYTHONPATH environment variable as a runtime one (so the runtime subshell can be properly setup) and add the directory where our python files will be installed to the PYTHONPATH variable.
Let's modify configure:
def configure(ctx): ctx.load('find_clhep') ctx.find_clhep(mandatory=False) ctx.start_msg("was clhep found ?") ctx.end_msg(ctx.env.HWAF_FOUND_CLHEP) if ctx.env.HWAF_FOUND_CLHEP: ctx.start_msg("clhep version") ctx.end_msg(ctx.env.CLHEP_VERSION) msg.info("clhep linkflags: %s" % ctx.env['LINKFLAGS_CLHEP']) msg.info("clhep cxxflags: %s" % ctx.env['CXXFLAGS_CLHEP']) from waflib.Utils import subst_vars ctx.load('find_python') ctx.find_python(mandatory=True) ctx.hwaf_declare_runtime_env('PYTHONPATH') pypath = subst_vars('${INSTALL_AREA}/python', ctx.env) ctx.env.prepend_value('PYTHONPATH', [pypath])Note that, as we added a new package, we must re-configure the workarea:
$ hwaf configure [...] Checking for program clhep-config : /usr/bin/clhep-config Checking for '/usr/bin/clhep-config' : yes Found clhep at : (local environment) Checking clhep version : ok clhep version : 2.1.3.1 was clhep found ? : ok clhep version : 2.1.3.1 clhep linkflags: ['-Wl,-O1,--sort-common,--as-needed,-z,relro'] clhep cxxflags: [] Checking for program python2 : /usr/bin/python2 checking for __extern_always_inline : ok Checking for program python : /usr/bin/python2 python executable '/usr/bin/python2' differs from system '/usr/bin/python' Checking for python version : (2, 7, 3, 'final', 0) Checking for library python2.7 in LIBDIR : yes Checking for program /usr/bin/python2-config,python2.7-config,python-config-2.7,python2.7m-config : /usr/bin/python2-config Checking for header Python.h : yes 'configure' finished successfully (2.058s)build is where one declares the build targets.
Let's create a simple shared library which will compute some float quantity:
$ touch src/mytools/mypkg/src/mypkgtool.cxx#include <cmath> extern "C" { float calc_hypot(float x, float y) { return std::sqrt(x*x + y*y); } } // EOF$ mkdir src/mytools/mypkg/python $ touch src/mytools/mypkg/python/__init__.py $ touch src/mytools/mypkg/python/pyhello.pyimport ctypes lib = ctypes.cdll.LoadLibrary('libhello-world.so') if not lib: raise RuntimeError("could not find hello-world") calc_hypot = lib.calc_hypot calc_hypot.argtypes = [ctypes.c_float]*2 calc_hypot.restype = ctypes.c_float import sys sys.stdout.write("hypot(10,20) = %s\n" % calc_hypot(10,20)) sys.stdout.flush() # EOF #and modify the build function like so:
def build(ctx): ctx(features = 'cxx cxxshlib', name = 'cxx-hello-world', source = 'src/mypkgtool.cxx', target = 'hello-world', ) ctx(features = 'py', name = 'py-hello', source = 'python/pyhello.py python/__init__.py', install_path = '${INSTALL_AREA}/python/mypkg', use = 'cxx-hello-world', ) returnRebuild and run:
$ hwaf [...] $ hwaf run python -c 'import mypkg.pyhello' hypot(10,20) = 22.360679626464844Note that we used the underlying features of waf to build and install the python module and the C++ library. This could be packaged up in a nice function instead.
At the moment, a few queries have been implemented:
# list the parent project of the current project $ hwaf show projects project dependency list for [work] (#projs=0) work 'show-projects' finished successfully (0.015s) # list the dependencies of a given package $ hwaf show pkg-uses mytools/mypkg package dependency list for [mytools/mypkg] (#pkgs=0) mytools/mypkg 'show-pkg-uses' finished successfully (0.015s) # print the value of some flags: C++ compilation, link, shared-lib $ hwaf show flags CXXFLAGS LINKFLAGS CXXFLAGS=['-O2', '-m64'] LINKFLAGS=[] # print the constituents of a project $ hwaf show constituents cxx-hello-world py-hello Assuming we consider the same little project, we may describe it using the Yaml syntax instead of wscripts.
You have just to replace the previously described wscript (thus in src/mytools/mypkg/wscript) by a hscript.yml script at the same location.
## -*- yaml -*- package: { name: "mytools/mypkg", authors: ["my"], } configure: { tools: ["compiler_c", "compiler_cxx", "python"], env: { PYTHONPATH: "${INSTALL_AREA}/python:${PYTHONPATH}" }, } build: { cxx-hello-world: { features: 'cxx cxxshlib', source: 'src/mypkgtool.cxx', target: 'hello-world', }, py-hello: { features: 'py', source: ['python/pyhello.py', 'python/__init__.py'], install_path: '${INSTALL_AREA}/python/mypkg', use: 'cxx-hello-world', }, } Everything described previously will be identical:
$ hwaf configure $ hwaf $ hwaf run python -c 'import mypkg.pyhello'