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.
- copy an old project
- rip out all the unnecessary classes
- chop of the CMakeLists.txt files to get it down to a bare bones
- initialize a git repository
- make an initial commit
- create a build directory
- export my development root directory so cmake can find it
- 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
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.
1 2 3 4 5 |
#!/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.
1 2 3 4 5 6 |
#!/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.
1 2 3 4 5 6 7 8 |
#!/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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#!/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
400300FalseGtkmm Stub300200 TrueFalse TrueFalseHello World True True 0 TrueFalse2 False True 1
Comments
Comments powered by Disqus