Locked History Actions

HowTo/NewPackage

How To Create a New C++ Package

Pick a package name, ideally as one lower-case word. Use this wherever <pkg> appears below.

Setup directory structure:

mkdir <pkg>
cd <pkg>
mkdir <pkg> src build

Create git package:

git init
cat > .gitignore
*~
# ignore files automatically generated by running autoreconf
autom4te.cache
^D
cat > build/.gitignore
# ignore everything here
*
# except a .gitignore file (which we use to force git to create this otherwise empty dir)
!.gitignore
^D
git add .
git commit -m 'Initial commit'

Setup autoconfigure by creating configure.ac, e.g. (replace likely with <pkg> in the AC_INIT line near the top):

#################################################################################
## Created 8-Aug-2011 by David Kirkby <dkirkby@uci.edu>
## Run autoreconf after making any change here.
#################################################################################

# This could probably be relaxed to an older version.
AC_PREREQ([2.68])

# Our package info.
AC_INIT([cosmo], [dev], [dkirkby@uci.edu])

# Checks for programs
AC_PROG_CXX

# Initialize libtool, which adds --enable/disable-shared configure options.
# The boost.m4 macros used below also need this.
LT_INIT

# Checks for libraries (there is no portable way to check for C++ classes in
# the library, so we just check that the linker can find the library using 'main')
AC_CHECK_LIB([m],[cos],,
        AC_MSG_ERROR([Cannot find the math library.]))

# We need a recent version of boost
BOOST_REQUIRE([1.45])

# Required header-only boost packages
BOOST_BIND
BOOST_FOREACH
BOOST_FUNCTION
BOOST_UTILITY
BOOST_SMART_PTR

# Configure automake
AC_CONFIG_FILES([Makefile])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
# See http://www.gnu.org/s/hello/manual/automake/maintainer_002dmode.html
AM_MAINTAINER_MODE

AC_OUTPUT

Setup automake by creating Makefile.am, e.g. (replace cosmo with <pkg> throughout):

# global compile and link options
AM_CPPFLAGS = $(BOOST_CPPFLAGS)
AM_LDFLAGS = -llikely

# targets to build and install
lib_LTLIBRARIES = libcosmo.la
bin_PROGRAMS = cosmocalc

# extra targets that should not be installed
##noinst_PROGRAMS = demo1 demo2

# instructions for building the library
libcosmo_la_SOURCES = \
        cosmo/AbsHomogeneousUniverse.cc

# library headers to install (nobase prefix preserves any subdirectories)
# Anything that includes config.h should *not* be listed here.
# and is not part of the package public API.
nobase_include_HEADERS = \
        cosmo/cosmo.h \
        cosmo/types.h \
        cosmo/RuntimeError.h \
        cosmo/AbsHomogeneousUniverse.h

# instructions for building each program

cosmocalc_SOURCES = src/cosmocalc.cc
cosmocalc_DEPENDENCIES = $(lib_LIBRARIES)
cosmocalc_LDADD = -lboost_program_options -L. -lcosmo

Install auxiliary autotools files using:

autoreconf --install

The following messages are safe to ignore:

libtoolize: Consider adding `AC_CONFIG_MACRO_DIR([m4])' to configure.ac and
libtoolize: rerunning libtoolize, to keep the correct libtool macros in-tree.
libtoolize: Consider adding `-I m4' to ACLOCAL_AMFLAGS in Makefile.am.

Finally, we are ready to add some code:

touch <pkg>/<pkg>.h <pkg>/types.h
../templar/mkskel <pkg>/RuntimeError.h > <pkg>/RuntimeError.h
...

The RuntimeError header-only exception class should look like this (with appropriate <pkg> substitutions):

// Created 08-Aug-2011 by David Kirkby (University of California, Irvine) <dkirkby@uci.edu>

#ifndef COSMO_RUNTIME_ERROR
#define COSMO_RUNTIME_ERROR

#include <stdexcept>
#include <string>

namespace cosmo {
        class RuntimeError : public std::runtime_error {
        public:
                explicit RuntimeError(std::string const &reason);
                virtual ~RuntimeError() throw ();
        private:
        }; // RuntimeError
        
        inline RuntimeError::RuntimeError(std::string const &reason)
        : std::runtime_error(reason) { }

    inline RuntimeError::~RuntimeError() throw () { }
} // cosmo

#endif // COSMO_RUNTIME_ERROR

Here is a skeleton program source using the boost program_options library:

// Created 08-Aug-2011 by David Kirkby (University of California, Irvine) <dkirkby@uci.edu>

#include "cosmo/cosmo.h"

#include "boost/program_options.hpp"

#include <iostream>

namespace po = boost::program_options;

int main(int argc, char **argv) {
    
    // Configure command-line option processing
    po::options_description cli("Cosmology calculator");
    cli.add_options()
        ("help,h", "Prints this info and exits.")
        ("verbose", "Prints additional information.")
        ;

    // do the command line parsing now
    po::variables_map vm;
    try {
        po::store(po::parse_command_line(argc, argv, cli), vm);
        po::notify(vm);
    }
    catch(std::exception const &e) {
        std::cerr << "Unable to parse command line options: " << e.what() << std::endl;
        return -1;
    }
    if(vm.count("help")) {
        std::cout << cli << std::endl;
        return 1;
    }
    bool verbose(vm.count("verbose"));
 
    return 0;
}

To build the package:

cd build
../configure
make
./cosmocalc --help

Remember to regularly commit to git from the top-level package directory.

After adding any new source files, run autoreconf from the top-level package directory then re-run ../configure from the build directory.

Refer to HowTo/GitWorkflow for details on adding this package to the server git repository.

Older Version of this Recipe Using Custom Makefile Instead of Autotools

Pick a package name, ideally as one lower-case word. Use this wherever <pkg> appears below.

mkdir <pkg>
cd <pkg>

Expand newpkg.tar.gz into your new top-level directory, e.g.

tar -zxf /tmp/newpkg.tar.gz

(In case you copied newpkg.tar.gz into your package directory, delete it now.)

Replace changeme with your chosen package name near the top of the Makefile and setup your package to use GIT:

make setup

You now have the following subdirs:

  • <pkg>/: source used to build package library

  • src/: one .cc file per executable, containing a main(...)

  • bin/: executables

  • build/: temporary files

For each library class, create a .h and .cc file with the same root name under <pkg>/. For each executable, create a .cc file under src/. By convention, you should provide a single package header file <pkg>.h that includes the headers of all library classes that an external package user might need. Edit the Makefile to add compile and link flags for any external libraries.

Running make will build the library (lib<pkg>.a at the top level) and then any executables (under bin/). The contents of bin/ and build/ are not tracked by GIT (because of .gitignore files in those subdirs). Use make clean to remove all the temporary files under build/ and force the next make to rebuild everything from scratch.

TODO:

  • Link to a GIT workflow HowTo

  • Link to a Templar HowTo

  • Describe how to add modules within package