At work some of the teams are using CMake as a build tool, or meta build tool rather, CMake generates the necessary files to build your project on a particular platform, the files being Makefiles for *nix'es and Visual Studio projects on windows systems.
I haven't tried it at work yet, my team is only targeting the windows embedded xp platform, but i decided to take a stab at it for. I have been wanting to try working on Peter Jones' SafePt library for a long time, i really like the feel of the library (and the rest of Peter Jones' work), and i think a C++ threading library is really needed. Especially after seing the amount of thread use going on at work - and i haven't done that much thread coding yet, so i could use the practice.
Peter Jones has unfortunaetely ceased development on his open source projects, so SafePt is in need of a maintaner, or at least someone continuing development on it. One of the main things that Peter Jones didn't get around to doing was to implement a Win32 version of the library, i have had some experience with Win32 threading at work so i think i might be up for the task. Plus, i don't think i can use the library with confidence without it being cross-platform, the current SafePt implementation uses pthread for threading so that should work on *nix'es, BeOS and Mac OS X.
The reason for me wanting to use a threading library is actually related to the BeOS system mentioned above, recently i have been interested in the BeOS system, and especially the Haiku which seeks to create a binary compatiable open source implementation of the BeOS operating system. I began reading about the BeOS messaging system and decided that libAnyBus could use a messaging system like that. Threading and messageing are closely related, as i understand messaging in BeOS was put in to support a highly threaded operating system, so now i finally have a reason to start hacking on SafePt.
Wow, long post, but i will now return to talking about how i "CMake'ed" SafePt.
CMake's configuration-/Make-files are called CMakeLists.txt, and have an almost naively simple syntax: COMMAND(argument1 argument2 argument3 ...)
And that's it, of course there is some variable expansion in strings:
SET(SAFEPT_VERSION "${SAFEPT_VERSION_MAJOR}.${SAFEPT_VERSION_MINOR}.${SAFEPT_VERSION_PATCH}")
And that is all you need, a basic CMakeLists.txt file looks like this:
PROJECT (safept) SUBDIRS(src examples) INCLUDE_DIRECTORIES(${safept_SOURCE_DIR}/include) OPTION(BUILD_EXAMPLES "Build example programs" OFF) SET(SAFEPT_VERSION_MAJOR "0") SET(SAFEPT_VERSION_MINOR "1") SET(SAFEPT_VERSION_PATCH "0") SET(SAFEPT_VERSION "${SAFEPT_VERSION_MAJOR}.${SAFEPT_VERSION_MINOR}.${SAFEPT_VERSION_PATCH}")
So we first define a project name, and then we follow the Make approach of defining things in subdirectories. INCLUDE_DIRECTORIES is used to define include paths that all compilations should use. And OPTION is used to define boolean configuration parameters, just like the {{--enable-option/--disable-option}}} commands of an autotools enabled software package, the last OFF is the default value.
In sub-directory we can then start defining the stuff to build, in the src subdirectory CMakeLists.txt:
ADD_LIBRARY(safept callback.cxx condition.cxx mutex.cxx rwmutex.cxx
sigthread.cxx utility.cxx)
SET_TARGET_PROPERTIES(safept PROPERTIES
VERSION ${SAFEPT_VERSION}
SOVERSION ${SAFEPT_VERSION_MAJOR})
INSTALL(TARGETS safept
LIBRARY DESTINATION lib)
We have now told CMake how to build the safept library, both static and shared, how to install it, and how to maintain the version symbolic links used on *nix to allow several versions of the same shared library to be installed.
Some constants are defined automatically in CMake that allows you to do things based on the system your are trying to build on nix, Win32, whatever, this can be used in SafePt to tell it to link to the pthread library on nix - and something else on Win32 when i get to it:
IF(WIN32)
ELSE(WIN32)
LINK_LIBRARIES(safept pthread)
ENDIF(WIN32)
You will observe that even the IF construct also uses the COMMAND(argument) syntax, verbose, but readable.
Lets now see the CMakeLists.txt in the example subdirectory, as seen earlier we defined an option called BUILD_EXAMPLES that decides whether the example applications should be built or not, and this is used in the examples subdirectory:
IF(BUILD_EXAMPLES)
ADD_EXECUTABLE((test_condition condition.cxx)
TARGET_LINK_LIBRARIES(test_condition safept)
ENDIF(BUILD_EXAMPLES)
Really simple looking, we define a program name, and the source files to link into the program, and adds the safept library as a library to link to. And this works (supposedly) on Win32 as well, on *nix the library will be called libsafept.so.0.1.0 and will have symbolic links called libsafept.so and libsafept.so.0 pointing to it, and on Win32 i predict it will be something like safept.dll - and without me, the developer, even having to focus on any platform specific filenames, compiler switches or search paths.
Ok, now for the bad stuff, of course i wanted to build Debian packages for the library, the Debian build system i use (cdbs) does not support CMake out of the box but a nice person has posted the following little "cdbs class" to make it work: #makefile # -- mode: makefile; coding: utf-8 -- # Copyright (C) 2006 Peter Rockai <[EMAIL PROTECTED]> # Copyright (C) 2006 Fathi Boudra <[EMAIL PROTECTED]> # Description: A class for cmake packages # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307 USA.
ifndef _cdbs_bootstrap
_cdbs_scripts_path ?= /usr/lib/cdbs
_cdbs_rules_path ?= /usr/share/cdbs/1/rules
_cdbs_class_path ?= /usr/share/cdbs/1/class
endif
ifndef _cdbs_class_cmake
_cdbs_class_cmake := 1
include $(_cdbs_rules_path)/buildcore.mk$(_cdbs_makefile_suffix)
ifdef _cdbs_tarball_dir
DEB_BUILDDIR = $(_cdbs_tarball_dir)/obj-$(DEB_BUILD_GNU_TYPE)
else
DEB_BUILDDIR = obj-$(DEB_BUILD_GNU_TYPE)
endif
DEB_MAKE_INSTALL_TARGET = install DESTDIR=$(DEB_DESTDIR)
DEB_CMAKE_PREFIX =/usr
# Overriden from makefile-vars.mk
# We pass CFLAGS and friends to ./configure, so no need to pass them to make
DEB_MAKE_INVOKE = $(DEB_MAKE_ENVVARS) $(MAKE) -C $(DEB_BUILDDIR)
include $(_cdbs_class_path)/makefile.mk$(_cdbs_makefile_suffix)
ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
# well, -O0
endif
ifneq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
# semi-debug
else
# final
endif
ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
# debug
endif
#DEB_COMPRESS_EXCLUDE = .dcl .docbook -license .tag .sty .el
common-configure-arch common-configure-indep:: common-configure-impl
common-configure-impl:: $(DEB_BUILDDIR)/CMakeCache.txt
$(DEB_BUILDDIR)/CMakeCache.txt:
cd $(DEB_BUILDDIR) && cmake $(CURDIR)/$(DEB_SRCDIR) \
-DCMAKE_INSTALL_PREFIX="$(DEB_CMAKE_PREFIX)" \
$(DEB_CMAKE_EXTRA_FLAGS) -DCMAKE_CXX_FLAGS="$(CXXFLAGS)" \
-DCMAKE_C_FLAGS="$(CFLAGS)" -DCMAKE_VERBOSE_MAKEFILE=ON
mkdir -p $(DEB_DESTDIR)
cleanbuilddir::
-if test "$(DEB_BUILDDIR)" != "$(DEB_SRCDIR)"; then rm -rf $(DEB_BUILDDIR); fi
endif
All that is well and nice, i suspect the patch will arrive in cdbs soon, your debian/rules file will then be reduced to:
1 2 3 4 5 6 7 | #!/usr/bin/make -f include debian/cmake_cdbs.mk include /usr/share/cdbs/1/rules/debhelper.mk #include /usr/share/cdbs/1/class/cmake.mk DEB_DH_INSTALL_ARGS = --sourcedir=debian/tmp |
Simple! Not as simple as autotools (autotools doesn't need the DEB_DH_INSTALL_ARGS variable), but almost perfect, and i'm sure the cdbs guys will fix that quickly.
I don't know whether it is CMake or the experimental cdbs support for it, you need to add the following stuff to your CMakeLists.txt:
# compile in debug mode
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
FORCE)
ENDIF(NOT CMAKE_BUILD_TYPE)
I understand that a build type varialbe is being set, but why it is being set i don't know, so that definaetely needs to go, i will have no Debian stuff in my platform independent Makefiles! - unless of course CMake can start creating debian packages, then i might allow it.
Another irritating thing is that CMake stands very much up for the cached results it has calculated earlier, and since the Makefiles generated by CMake is the same as is being used by the Debian build system (which alters things like destination folder and such) CMake doesn't update the cached results when it generates the Makefiles, this leads to "strange errors" like the following:
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 | # First a standard run:
$ cmake .
-- Check for working C compiler: /usr/bin/distcc
-- Check for working C compiler: /usr/bin/distcc -- works
-- Check size of void*
-- Check size of void* - done
-- Check for working CXX compiler: /usr/bin/distcc
-- Check for working CXX compiler: /usr/bin/distcc -- works
-- Configuring done
-- Generating done
-- Build files have been written to: /home/rto/work/safept
$ make
Scanning dependencies of target safept
Building CXX object src/CMakeFiles/safept.dir/callback.o
Building CXX object src/CMakeFiles/safept.dir/condition.o
Building CXX object src/CMakeFiles/safept.dir/mutex.o
Building CXX object src/CMakeFiles/safept.dir/rwmutex.o
Building CXX object src/CMakeFiles/safept.dir/sigthread.o
Building CXX object src/CMakeFiles/safept.dir/utility.o
Linking CXX shared library ../build/libsafept.so
# All is well, let's try debuild
$ debuild
fakeroot debian/rules clean
test -x debian/rules
test "`id -u`" = 0
rmdir obj-i486-linux-gnu
rmdir: obj-i486-linux-gnu: Filkataloget er ikke tomt
make: [cleanbuilddir] Fejl 1 (ignoreret)
if test "obj-i486-linux-gnu" != "."; then rm -rf obj-i486-linux-gnu; fi
/usr/bin/make -C obj-i486-linux-gnu CFLAGS="-g -Wall -O2" CXXFLAGS="-g -Wall -O2" CPPFLAGS="" LDFLAGS="" -k clean
make: *** obj-i486-linux-gnu: Ingen sådan fil eller filkatalog. Stop.
make: [makefile-clean] Fejl 2 (ignoreret)
rm -f debian/stamp-makefile-build
dh_clean
dpkg-source -b safept
dpkg-source: warning: source directory `./safept' is not <sourcepackage>-<upstreamversion> `safept-0.1.0'
dpkg-source: building safept in safept_0.1.0.tar.gz
dpkg-source: building safept in safept_0.1.0.dsc
debian/rules build
test -x debian/rules
mkdir -p "obj-i486-linux-gnu"
cd obj-i486-linux-gnu && cmake /home/rto/work/safept/. \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_CXX_FLAGS="-g -Wall -O2" \
-DCMAKE_C_FLAGS="-g -Wall -O2" -DCMAKE_VERBOSE_MAKEFILE=ON
-- Configuring done
-- Generating done
-- Build files have been written to: /home/rto/work/safept/.
mkdir -p /home/rto/work/safept/debian/tmp/
/usr/bin/make -C obj-i486-linux-gnu CFLAGS="-g -Wall -O2" CXXFLAGS="-g -Wall -O2" CPPFLAGS="" LDFLAGS=""
make[1]: Går til katalog '/home/rto/work/safept/obj-i486-linux-gnu'
make[1]: *** Ingen angivne mål og ingen makefil fundet. Stop.
make[1]: Forlader katalog '/home/rto/work/safept/obj-i486-linux-gnu'
make: *** [debian/stamp-makefile-build] Fejl 2
debuild: fatal error at line 1224:
debian/rules build failed
|
From the look of it (apart from the horrible syntax highlighting, sorry), this looks like the Makefile is not generated, but after you delete the cached results (CMakeCache.txt), it runs perfectly, things like that need to work easier and simpler.
Another final complaint is that CMake creates a lot of files and directories inside your directory, there is probably a way to configure it to not do that, but i would rather have it do that by default.
Well, i will hopefully have time to start up my work laptop sometime soon and try CMake on a windows platform and see if it really is as good as it claims to be.
Enough techno-bable for one post, to those who have read this far, i encourage you to follow my example and get wasted tomorrow, there is a concert show being held in Aalborg tomorrow and we are a few geeks who are going to attend - say hello if you meet me, and i might blurb some sounds reminescent of actual language.
Oh, and a big congratulation to Michael Rasmussen for his wonderful performance on today's stage of Tour de France - a true champ!
Comments (1)
A late comment on two of your CMake concerns.
CMAKE_BUILD_TYPE is not set by default, the user building the package has to do that, unless you add the code that sets a default itself. Ideally, the cdbs script should probably just add -DCMAKE_BUILD_TYPE=RelWithDebInfo to its cmake call to choose the build type. The build type decides which flags are passed to the compiler (eg. RelWithDebInfo will use CMAKE_CFLAGS_RelWithDebInfo, IIRC in addition to the plain CMAKE_CFLAGS).
That lots of files are created is caused by you, doing in-source builds. These are possible but deprecated. Instead, create a directory in which you build your program, using [HTML_REMOVED]cmake /path/to/source[HTML_REMOVED] instead of [HTML_REMOVED]cmake .[HTML_REMOVED] That will also solve your conflicting settings for the cdbs build if you just use two different directories, one for building the plain version and one for building the debian version. Actually, AFAICT cdbs already uses a separate build dir, but as you do in-source builds, CMake will try to do an in-source build for the cdbs build as well, because it assumes that a directory with a CMakeCache.txt is a build dir and builds there to avoid possible conflicts.
Post comment
If you wish, you can use markdown syntax in the comment field.