Add External Library

Add external library to the wscript of your experiment

To add an external library to your experiment, you need to modify the build script of your experiment, named wscript. The standard way to do this is to create a new configuration file for the new dependency. In the waf build system, this is done by creating a python script (.py file), usually called libname.py (where libname is the name of the library), in the same directory as your experiment.

Warning

to activate this script, you need to activate your experiment when configuring limbo:

./waf configure --exp your_exp

This new file should have the following structure:

#!/usr/bin/env python
# encoding: utf-8

from waflib.Configure import conf

@conf
def check_libname(conf):
    # check if libname exists in the system
    try:
      res = check_if_libname_exists
    except:
      conf.fatal('libname not found')
      return

Where check_if_libname_exists is replaced with logic to find our library, as explained later. If the library is optional, the conf.fatal line can be removed.

Then add the following lines in the wscript:

# imports, etc, ...

# we assume that the configuration file is saved as libname.py
import libname

def configure(conf):
    conf.load('libname')
    conf.check_libname()
    # rest of the configuration

# rest of code

Libraries usually have the headers and the lib files in two different directories. However, header-only libraries only have includes, in this case, you can ignore the following section named “Check for lib files”.

Check for headers

To check for the headers of the library, you can add the following code to the check_libname function:

# previous code

@conf
def check_libname(conf):
    # possible path to find headers
    includes_check = ['path1', 'path2']
    try:
      conf.start_msg('Checking for libname includes')
      # include_files is a list with the headers we expect to find
      for file in include_files:
        conf.find_file(file, includes_check)
      conf.end_msg('ok')
      conf.env.INCLUDES_LIBNAME = includes_check
    except:
      conf.end_msg('Not found', 'RED')
      return
    # rest of check_libname

# rest of code

Check for lib files

To check for the lib files of the library, you can add the following code to the check_libname function:

# previous code

@conf
def check_libname(conf):
    # possible path to find lib files
    libs_check = ['path1', 'path2']
    try:
      conf.start_msg('Checking for libname libs')
      # lib_files is a list with the lib files we expect to find
      for file in lib_files:
        conf.find_file(file, libs_check)
      conf.end_msg('ok')
      conf.env.LIBPATH_LIBNAME = libs_check
      # list with the lib names the library has
      conf.env.LIB_LIBNAME = ['libname1', 'libname2']
    except:
      conf.end_msg('Not found', 'RED')
      return
    # rest of check_libname

# rest of code

Add configuration options

Additional configuration options are often needed when adding new libraries. For example, one useful option is to specify the location of the library headers and lib files. Adding options is easy: you only need to define a new function named options in the wscript and another one in the library configuration file. In the library’s configuration file (e.g., libname.py):

#imports, etc, ...

def options(opt):
    # add options to the configuration
    opt.add_option('cmd_option', type='option_type', help='info message',
        dest='destination_variable')

@conf
def check_libname(conf):
    # access options
    if conf.options.destination_variable == 'yes':
      print 'destination_variable found'
    # rest of check_libname

The options in the waf build system are using the python’s optparse. Check the official optparse documentation for more information.

Then add the following lines in the wscript of your experiment:

# imports, etc, ...

def options(opt):
    opt.load('libname')
    # rest of the options

# rest of the code

Example: Add ROS as external library

Here’s a small and quick example to add ROS as an external library to our experiment. We assume the following file structure (where main.cpp is C++ source code using limbo and ROS):

limbo
|-- exp
     |-- example
          +-- wscript
          +-- ros.py
          +-- main.cpp
|-- src
...

wscript:

#!/usr/bin/env python

import limbo
import ros

def options(opt):
    opt.load('ros')

def configure(conf):
    conf.load('ros')
    conf.check_ros()

def build(bld):
    libs = 'EIGEN BOOST ROS LIMBO'

    obj = bld(features = 'cxx cxxstlib',
              source = 'main.cpp',
              includes = '. .. ../../ ../../src',
              target = 'test_exec',
              uselib =  libs,
              use = 'limbo')

ros.py:

#!/usr/bin/env python
# encoding: utf-8

import os
from waflib.Configure import conf


def options(opt):
  opt.add_option('--ros', type='string', help='path to ros', dest='ros')

@conf
def check_ros(conf):
  # Get locations where to search for ROS's header and lib files
  if conf.options.ros:
    includes_check = [conf.options.ros + '/include']
    libs_check = [conf.options.ros + '/lib']
  else:
    if 'ROS_DISTRO' not in os.environ:
      conf.start_msg('Checking for ROS')
      conf.end_msg('ROS_DISTRO not in environmental variables', 'RED')
      return
    includes_check = ['/opt/ros/' + os.environ['ROS_DISTRO'] + '/include']
    libs_check = ['/opt/ros/' + os.environ['ROS_DISTRO'] + '/lib/']

  try:
    # Find the header for ROS
    conf.start_msg('Checking for ROS includes')
    conf.find_file('ros/ros.h', includes_check)
    conf.end_msg('ok')

    # Find the lib files
    libs = ['roscpp','rosconsole','roscpp_serialization','rostime','xmlrpcpp',
            'rosconsole_log4cxx', 'rosconsole_backend_interface']
    conf.start_msg('Checking for ROS libs')
    for lib in libs:
      conf.find_file('lib'+lib+'.so', libs_check)
    conf.end_msg('ok')

    conf.env.INCLUDES_ROS = includes_check
    conf.env.LIBPATH_ROS = libs_check
    conf.env.LIB_ROS = libs
    conf.env.DEFINES_ROS = ['USE_ROS']
  except:
    conf.end_msg('Not found', 'RED')
    return

The configuration and compilation of the experiment follows the usual procedure (assuming that we are in the limbo root folder):

./waf configure --exp example
./waf --exp example