My cmake Bootstrap Process


I’ve been using eclipse as an IDE for a long time. For Java, eclipse is ridiculously beautiful. It almost writes your programs for you. Eclipse CDT is lacking a some features I would really like to see, and it’s impossible to understand the code base and write plugins (and so… I have not implemented anything myself) however, as a traditionalist, I like my IDE to work on top of the command line tools, not in place of them. Also, I like that there are PHP, Javascript, XML, Perl, and Latex plugins… which means that my life is much easier given that I work on all my projects with the same familiar interface.

Lately, I’ve also started using CMake (instead of my ridiculously complicated but super-stream-lined personal makefile system). The CMake project generator for Eclipse is really awesome, and I’m completely sold on using cmake now. However, creating a new project from within eclipse doesn’t work as well as I’d like. Also, I have a couple of other files that I generally include in every project, so I’ve started bootstrapping my projects by using a template directory, and a pair of scripts.

Consequently I find myself repeating several tasks every time I start a new project… just like when I was starting off with programming.

  1. copy an old project
  2. rip out all the unnecessary classes
  3. chop of the CMakeLists.txt files to get it down to a bare bones
  4. initialize a git repository
  5. make an initial commit
  6. create a build directory
  7. export my development root directory so cmake can find it
  8. call cmake (which, for eclipse projects, is a chore)

All this gets a little tedious. I’d really like to have an eclipse plugin that does this… but I’m not that skilled in the advanced features of Eclipse, so I started automating this with a script. Most of the things that I’ve been working on lately have been Gtkmm programs. So I’ve created a barebones project directory with stubs for all of the common parts I need:

  • all the gtkmm cmake find modules
  • a stub CMakeFile.txt
  • a stub glade file
  • a stub UI xml file
  • a simple main.cpp
  • one class called Application to load the glade file and the UI
  • some git helper scripts
  • a .gitignore file
  • a doxyfile
  • a doxygen mainpage file
  • a bootstrap script

I also wrote a script which does all of the redundant tasks mentioned above. I’ll go through all the files in the template directory and the new project script describing what they’re for. The template files can be downloaded as a gzipped tarball here. The new project script can be retrieved here

Scripts I Always Use

countlines.pl

This is a simple script that I use to count the number of lines of code in a project. It reports the total number of lines of code as a raw number, exluding whitespace, and exluding comments.

#!usr/bin/perl
 
my @extensions  = ("cpp","h","hpp");
my @directories = ("src");
my @filelist;
 
foreach $directory( @directories )
{
    print "searching $directoryn";
    foreach $extension( @extensions )
    {
        print "   for *.$extensionn";
        open INLIST, " find $directory -name '*.$extension' |";
        while()
        {
            chomp;
            push(@filelist, $_);
        }
        close INLIST;
    }
}
 
$totallines     = 0;
$written        = 0;
$noncomment     = 0;
 
my $blockcomment    = 0;
my $comment         = 0;
 
print "parsing filesn";
 
foreach $file( @filelist )
{
    print "   $filen";
    open INFILE, "./" . $file or die "failed to open" . $file . "n";
 
    while()
    {
        chomp;
        s/s//g;
 
        $totallines++;
 
        $comment        = 0;
        if( /^(//)/ )   {$comment        = 1;  }
        if( /(/*)/ )    {$blockcomment   = 1;  }
        if( /(*/)/ )    {$blockcomment   = 0;  }
 
        $noncomment++   unless( length($_) == 0 || $comment || $blockcomment );
        $written++      unless( length($_) == 0 );
    }
 
    close INFILE;
}
 
print "n";
print "total:       " . $totallines     . "n";
print "written:     " . $written        . "n";
print "noncomment:  " . $noncomment     . "n";
 
$dummy = <>;

logChanges.sh

This is a helper script I use to format my git commits the way I like. I like to have a list of all the files changed at each commit stored right in the log. This script strips the comment hashes from the git status report.

#!/bin/bash
 
echo ""
echo "Files Changed:"
git status | sed -e "s/#t//" -e "/^#/d"

commitAll.sh

This is a script that I call to commit all changes to the git repository. It calls logChanges.sh to generate a changelog changes.txt, and then opens nano to add a nice comment to the log entry.

#!/bin/bash
 
git add -A
bash logChanges.sh > changes.txt
nano changes.txt
git commit -F changes.txt

bootstrap.sh

This is a script that calls cmake from the build directory. It prepends $HOME/Codes/devroot to the CMake prefix path so that it can find all of my develepment libraries. It adds the corresponding lib/pkgconfig directory to the PKG_CONFIG_PATH variable so that pkg-config looks for my development library versions before checking the system versions. It also sets the prefix-path directory to be the install prefix. Then it calls cmake to generate Eclipse CDT4 project files, along with a source project and the option to build with debug flags.

#!/bin/bash
 
export PREFIX=$HOME/Codes/devroot
 
export SCRIPT_DIR=`dirname $0`;
export CMAKE_PREFIX_PATH=$PREFIX:CMAKE_PREFIX_PATH
export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig/:PKG_CONFIG_PATH
cmake -G "Eclipse CDT4 - Unix Makefiles" -DECLIPSE_CDT4_GENERATE_SOURCE_PROJECT=TRUE -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$PREFIX $SCRIPT_DIR

Configuration Files

doxy.config.in

Doxygen configuration file. The project, version, paths, and some other things are set via cmake variables

#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING      = UTF-8
PROJECT_NAME           = "${PROJECT_NAME}"
PROJECT_NUMBER         = v${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_BUGFIX}
OUTPUT_DIRECTORY       = ./doc
CREATE_SUBDIRS         = NO
OUTPUT_LANGUAGE        = English
BRIEF_MEMBER_DESC      = YES
REPEAT_BRIEF           = YES
ABBREVIATE_BRIEF       =
ALWAYS_DETAILED_SEC    = YES
INLINE_INHERITED_MEMB  = NO
FULL_PATH_NAMES        = YES
STRIP_FROM_PATH        = ${CMAKE_CURRENT_SOURCE_DIR}/src
STRIP_FROM_INC_PATH    = {CMAKE_CURRENT_SOURCE_DIR}/src/
SHORT_NAMES            = NO
JAVADOC_AUTOBRIEF      = NO
QT_AUTOBRIEF           = NO
MULTILINE_CPP_IS_BRIEF = YES
INHERIT_DOCS           = YES
SEPARATE_MEMBER_PAGES  = NO
TAB_SIZE               = 4
ALIASES                =
OPTIMIZE_OUTPUT_FOR_C  = NO
OPTIMIZE_OUTPUT_JAVA   = NO
OPTIMIZE_FOR_FORTRAN   = NO
OPTIMIZE_OUTPUT_VHDL   = NO
EXTENSION_MAPPING      =
BUILTIN_STL_SUPPORT    = YES
CPP_CLI_SUPPORT        = NO
SIP_SUPPORT            = NO
IDL_PROPERTY_SUPPORT   = YES
DISTRIBUTE_GROUP_DOC   = NO
SUBGROUPING            = YES
TYPEDEF_HIDES_STRUCT   = NO
SYMBOL_CACHE_SIZE      = 0
 
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL            = YES
EXTRACT_PRIVATE        = YES
EXTRACT_STATIC         = NO
EXTRACT_LOCAL_CLASSES  = YES
EXTRACT_LOCAL_METHODS  = NO
EXTRACT_ANON_NSPACES   = NO
HIDE_UNDOC_MEMBERS     = NO
HIDE_UNDOC_CLASSES     = NO
HIDE_FRIEND_COMPOUNDS  = NO
HIDE_IN_BODY_DOCS      = NO
INTERNAL_DOCS          = NO
CASE_SENSE_NAMES       = NO
HIDE_SCOPE_NAMES       = NO
SHOW_INCLUDE_FILES     = YES
FORCE_LOCAL_INCLUDES   = NO
INLINE_INFO            = YES
SORT_MEMBER_DOCS       = YES
SORT_BRIEF_DOCS        = YES
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES       = YES
SORT_BY_SCOPE_NAME     = YES
GENERATE_TODOLIST      = YES
GENERATE_TESTLIST      = YES
GENERATE_BUGLIST       = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS       =
MAX_INITIALIZER_LINES  = 30
SHOW_USED_FILES        = YES
SHOW_DIRECTORIES       = YES
SHOW_FILES             = YES
SHOW_NAMESPACES        = YES
FILE_VERSION_FILTER    =
LAYOUT_FILE            =
 
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET                  = NO
WARNINGS               = YES
WARN_IF_UNDOCUMENTED   = YES
WARN_IF_DOC_ERROR      = YES
WARN_NO_PARAMDOC       = NO
WARN_FORMAT            = "$file:$line: $text"
WARN_LOGFILE           =
 
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ${CMAKE_CURRENT_SOURCE_DIR}/src
        ${CMAKE_CURRENT_SOURCE_DIR}/include
        ${CMAKE_CURRENT_SOURCE_DIR}/docs/pages
        ${CMAKE_CURRENT_BINARY_DIR}/src
        ${CMAKE_CURRENT_BINARY_DIR}/include
        ${CMAKE_CURRENT_BINARY_DIR}/docs/pages   
 
INPUT_ENCODING         = UTF-8
FILE_PATTERNS          = *.cpp
                         *.h
                         *.hpp
RECURSIVE              = YES
EXCLUDE                =
EXCLUDE_SYMLINKS       = NO
EXCLUDE_PATTERNS       =
EXCLUDE_SYMBOLS        =
EXAMPLE_PATH           =
EXAMPLE_PATTERNS       =
EXAMPLE_RECURSIVE      = NO
IMAGE_PATH             =
INPUT_FILTER           =
FILTER_PATTERNS        =
FILTER_SOURCE_FILES    = NO
 
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER         = YES
INLINE_SOURCES         = NO
STRIP_CODE_COMMENTS    = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION    = NO
REFERENCES_LINK_SOURCE = YES
USE_HTAGS              = NO
VERBATIM_HEADERS       = YES
 
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX     = NO
COLS_IN_ALPHA_INDEX    = 5
IGNORE_PREFIX          =
 
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML          = YES
HTML_OUTPUT            = html
HTML_FILE_EXTENSION    = .html
HTML_HEADER            =
HTML_FOOTER            =
HTML_STYLESHEET        =
HTML_TIMESTAMP         = YES
HTML_ALIGN_MEMBERS     = YES
HTML_DYNAMIC_SECTIONS  = NO
GENERATE_DOCSET        = NO
DOCSET_FEEDNAME        = "Doxygen generated docs"
DOCSET_BUNDLE_ID       = org.doxygen.Project
GENERATE_HTMLHELP      = NO
CHM_FILE               =
HHC_LOCATION           =
GENERATE_CHI           = NO
CHM_INDEX_ENCODING     =
BINARY_TOC             = NO
TOC_EXPAND             = NO
GENERATE_QHP           = NO
QCH_FILE               =
QHP_NAMESPACE          =
QHP_VIRTUAL_FOLDER     = doc
QHP_CUST_FILTER_NAME   =
QHP_CUST_FILTER_ATTRS  =
QHP_SECT_FILTER_ATTRS  =
QHG_LOCATION           =
GENERATE_ECLIPSEHELP   = NO
ECLIPSE_DOC_ID         = org.doxygen.Project
DISABLE_INDEX          = NO
ENUM_VALUES_PER_LINE   = 4
GENERATE_TREEVIEW      = YES
USE_INLINE_TREES       = NO
TREEVIEW_WIDTH         = 400
FORMULA_FONTSIZE       = 12
SEARCHENGINE           = NO
SERVER_BASED_SEARCH    = NO
 
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX         = NO
LATEX_OUTPUT           = latex
LATEX_CMD_NAME         = latex
MAKEINDEX_CMD_NAME     = makeindex
COMPACT_LATEX          = YES
PAPER_TYPE             = letter
EXTRA_PACKAGES         = amsmath
                         amsfonts
                         hyperref
LATEX_HEADER           =
PDF_HYPERLINKS         = YES
USE_PDFLATEX           = YES
LATEX_BATCHMODE        = YES
LATEX_HIDE_INDICES     = NO
LATEX_SOURCE_CODE      = NO
 
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF           = NO
RTF_OUTPUT             = rtf
COMPACT_RTF            = NO
RTF_HYPERLINKS         = NO
RTF_STYLESHEET_FILE    =
RTF_EXTENSIONS_FILE    =
 
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN           = NO
MAN_OUTPUT             = man
MAN_EXTENSION          = .3
MAN_LINKS              = NO
 
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML           = YES
XML_OUTPUT             = xml
XML_SCHEMA             =
XML_DTD                =
XML_PROGRAMLISTING     = NO
 
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF   = NO
 
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD       = NO
PERLMOD_LATEX          = NO
PERLMOD_PRETTY         = YES
PERLMOD_MAKEVAR_PREFIX =
 
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING   = YES
MACRO_EXPANSION        = NO
EXPAND_ONLY_PREDEF     = NO
SEARCH_INCLUDES        = YES
INCLUDE_PATH           =
INCLUDE_FILE_PATTERNS  =
PREDEFINED             =
EXPAND_AS_DEFINED      =
SKIP_FUNCTION_MACROS   = YES
 
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES               =
GENERATE_TAGFILE       = ${PROJECT_NAME}.tag
ALLEXTERNALS           = NO
EXTERNAL_GROUPS        = YES
PERL_PATH              = /usr/bin/perl
 
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS         = YES
MSCGEN_PATH            = ${CMAKE_MSCGEN_PATH}
HIDE_UNDOC_RELATIONS   = YES
HAVE_DOT               = NO
DOT_FONTNAME           = FreeSans
DOT_FONTSIZE           = 10
DOT_FONTPATH           =
CLASS_GRAPH            = YES
COLLABORATION_GRAPH    = YES
GROUP_GRAPHS           = YES
UML_LOOK               = NO
TEMPLATE_RELATIONS     = NO
INCLUDE_GRAPH          = YES
INCLUDED_BY_GRAPH      = YES
CALL_GRAPH             = NO
CALLER_GRAPH           = NO
GRAPHICAL_HIERARCHY    = YES
DIRECTORY_GRAPH        = YES
DOT_IMAGE_FORMAT       = png
DOT_PATH               = ${CMAKE_DOT_PATH}
DOTFILE_DIRS           =
DOT_GRAPH_MAX_NODES    = 50
MAX_DOT_GRAPH_DEPTH    = 0
DOT_TRANSPARENT        = NO
DOT_MULTI_TARGETS      = NO
GENERATE_LEGEND        = YES
DOT_CLEANUP            = YES

CMakeLists.txt

The root CMakeLists file. The project name is replaced by the new project script

cmake_minimum_required(VERSION 2.8)
 
# defines the project name
project (projectName)
set( ${CMAKE_PROJECT_NAME}_VERSION_MAJOR 0 )
set( ${CMAKE_PROJECT_NAME}_VERSION_MINOR 1 )
set( ${CMAKE_PROJECT_NAME}_VERSION_BUGFIX 0 )
 
# adds the project-specific cmake module directory cmake/Modules to the cmake
# search path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
 
# finds pkg-config
find_package(PkgConfig REQUIRED)
 
# add the src/ subdirectory to the list of directories cmake processes
add_subdirectory(src)
add_subdirectory(include)
 
# configure the doxygen configuration
configure_file(
    "${PROJECT_SOURCE_DIR}/doxy.config.in"
    "${PROJECT_BINARY_DIR}/doxy.config"
    )
 
# use Jan Woetzel's doxygen doc target
include("${CMAKE_MODULE_PATH}/TargetDoc.cmake" OPTIONAL)

src/CMakeLists.txt

The CMakeLists for the application.

find_package(GTKmm REQUIRED)
#find_package(Boost COMPONENTS iostreams REQUIRED)
 
include_directories(
#   ${Boost_INCLUDE_DIRS}
    ${GTKmm_INCLUDE_DIRS}
    )
 
set(LIBS ${LIBS}
#   ${Boost_LIBRARIES}
    ${GTKmm_LIBRARIES}
    )
 
add_executable( ${CMAKE_PROJECT_NAME} main.cpp Application.cpp)
 
target_link_libraries( ${CMAKE_PROJECT_NAME} ${LIBS})
 
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/mainwindow.glade
    ${CMAKE_CURRENT_BINARY_DIR}/mainwindow.glade COPYONLY )
 
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/mainwindow.xml
    ${CMAKE_CURRENT_BINARY_DIR}/mainwindow.xml COPYONLY )

New Project Script

start_new_project.sh

This is the script I use to start a new project. It copies the template directory to a new directory in $HOME/Codes/cpp/ named after the new project. It also replaces the project name in the root CMakeLists to the project name. It initializes a git repository in the source and make an initial commit with all the files. It then creates a build directory in $HOME/Codes/cpp/builds. It changes to that directory and then calls the bootstrap script to generate the project files and makefiles.

#!/bin/bash
 
EXPECTED_ARGS=1
if [ $# -ne $EXPECTED_ARGS ]
then
    echo "Usage: `basename $0` {arg}"
    exit 1
fi
 
PROJECT_DIR="$HOME/Codes/cpp/$1"
BUILD_DIR="$HOME/Codes/cpp/builds/$1"
TEMPLATE_DIR="$HOME/Codes/cpp/template"
 
if [ -d "$HOME/Codes/cpp/$1" ]
then
    echo "The directory '$PROJECT_DIR' already exists"
    exit 1
fi
 
echo "Making $PROJECT_DIR"
mkdir $PROJECT_DIR
 
echo "Copying files from $TEMPLATE_DIR to $PROJECT_DIR"
cp -rv $TEMPLATE_DIR/* $PROJECT_DIR/
 
echo "Setting project name in CMakeList.txt"
sed -i "s/projectName/$1/" $PROJECT_DIR/CMakeLists.txt
 
echo "Changing to project directory"
cd $PROJECT_DIR
echo "   pwd: `pwd`"
 
echo "Initializing the git repository"
git init
git add -A
git commit -m "Initial commit, template project"
 
echo "Changing to the build directory"
mkdir $BUILD_DIR
cd $BUILD_DIR
echo "   pwd: `pwd`"
 
echo "Bootstrapping cmake"
$PROJECT_DIR/bootstrap.sh

Program Source Files

src/main.cpp

/*
 *  file   main.cpp
 *  date   May 31, 2011
 */
 
#include 
#include "Application.h"
 
int main(int argc, char** argv)
{
    Gtk::Main kit(argc, argv);
    Application app;
    kit.run(app.getWindow());
}

src/Application.h

/*
 *  file   Application.h
 *  date   Mar 19, 2011
 */
 
#ifndef APPLICATION_H_
#define APPLICATION_H_
 
#include 
 
class Application
{
    private:
        Glib::RefPtr      m_builder;
        Glib::RefPtr    m_ui;
        Gtk::Window*                    m_mainWindow;
 
    public:
        Application();
        virtual ~Application();
 
        Gtk::Window& getWindow();
};
 
#endif /* Application_H_ */

src/Application.cpp

/*
 *  file   Application.cpp
 *  date   Apr 29, 2011
 */
 
#include "Application.h"
 
Application::Application():
    m_mainWindow(0)
{
    using namespace Glib;
    using namespace Gtk;
 
    m_builder = Builder::create_from_file("mainwindow.glade");
    m_mainWindow = 0;
    m_builder->get_widget("main_window",m_mainWindow);
 
    VBox* vbox = 0;
    m_builder->get_widget("main_vbox", vbox);
 
    m_ui = UIManager::create();
    m_ui->add_ui_from_file("mainwindow.xml");
    Gtk::Widget* menubar = m_ui->get_widget("/mb_main");
    Gtk::Widget* toolbar = m_ui->get_widget("/tb_main");
    vbox->pack_start(*menubar,false,false);
    vbox->pack_start(*toolbar,false,false);
    vbox->reorder_child(*menubar,0);
    vbox->reorder_child(*toolbar,1);
 
    m_mainWindow->show_all();
}
 
Application::~Application()
{
 
}
 
Gtk::Window& Application::getWindow(){ return *m_mainWindow; }

src/mainwindow.glade

<!--?xml version="1.0" encoding="UTF-8"?-->
 
  <!-- interface-naming-policy project-wide -->
  <object id="main_window" class="GtkWindow">400300FalseGtkmm Stub300200      <object id="main_vbox" class="GtkVBox">TrueFalse          <object id="label1" class="GtkLabel">TrueFalseHello World          </object>
 
True
True
0
 
          <object id="statusbar1" class="GtkStatusbar">TrueFalse2          </object>
 
False
True
1
 
      </object>
 
  </object>

src/mainwindow.xml

 
  1. #1 by yogesh on November 2, 2014 - 12:37 pm

    A Great Writeup!!/
    Thats a neat workflow

(will not be published)