Oliver Kullmann wrote: > The directory structure is "fractal" (or "recursive") and "dynamic", so > for example modules have subdirectories Module/tests, containing generic > tests for the components provided by the modules, where Module/tests might > contain a tests-subdirectory itself for testing the testsystem etc.; additions > are detected automatically, no registrations whatsoever is required for tests, > but by running "make check" all new or changed tests are compiled and executed > (of course, the produced link-libraries, executables, text files etc. are > stored in some other directory-hierarchy, similar to the source-hierarchy, > but, > of course, also with many differences). > So in the first stage stage of the the build process here, from configuration > values, > parameter values, targets given and the current state of the file-system we > need > to compute dynamically the list of second-stage-targets and -rules, and then > we need > to run make (this is actually achieved by the 2-stage-process of make itself, > but it's quite clumsy).
This is not a problem. CMake provides FILE( GLOB ...) and similar constructs to do what you want. > It appears now that the computational power of cmake is rather restricted? Hardly. cmake's language has most of the features available on scripting languages (regexes, arrays, macros, variables, etc), albeit implemented in a somewhat more cumbersome syntax than, say, ruby. I'm using cmake on a huge project that requires swig, flex, bison, multiple APIs, 10 different libraries, a different build for each api, 3 OSes, and cmake has really excelled at it (this after having tried to use python's scons, ruby's rake and autoconf for it). scons and rake were not really up to the task. autoconf was, but maintaining it would have been too painful and windows would have needed to be handled separately. cmake has been a life saver. > There seem to be no functions?? (While we had hoped that there are many > more than in make?) Even just a shell-call seems to need to be programmed > by ourselves?? (I can't believe that, but couldn't find it.) CMake does not have functions, but it does have a very simple to use macro system which in practice acts like functions. Shell calls can be added by ADD_CUSTOM_COMMAND() or EXECUTE_PROGRAM(). The first one creates an actual Makefile rule. The second one gets run only when cmake is run. These functions are very easy to use and can automatically store the outputs and results into cmake variables. > > A lesser problem (likely) seems to be that there is no really fine-grained > control over where all those auxiliary files go? It seems the basic assumption > is that of having a build-directory (used only once), where all sort of stuff > can be dumped to, and then with the final "make install" the "relevant" > files are moved elsewhere? This does not neatly fit with our permanent use > of the build-system, but likely one could live with it (by automatically > always running "make install"). This is indeed somewhat of a problem with cmake. Out-of-source builds are not 'built-in' so you need to do something like: > mkdir buildir && cd buildir && cmake .. && make CMake provides some variables that can be set to control where output goes (see Wiki - useful variables). With them you can control where .a, .so, executables and the like get placed. The only thing you don't really have as much control over is where .o files go, as CMAKE_BINARY_DIR is more or less broken, but in principle you should not have to worry about that. I am attaching a simple 'mk' bash script I use to run cmake (and normal makefiles) in a cross-platform way (and to simplify the creating of out-of-source builds) and a simple optional FindBuildDir.cmake that, optionally, works with it. I think you'll find that useful in understanding how cmake can be made to work for a large project. > > Another, perhaps more serious problem: We are exploiting the tree structure of > the library, that is, at every point in the tree when calling make exactly > the sub-tree at this node is considered (everything down, nothing up). > This works very well for example for testing first the local module (we use > continuous testing), then going one step up (which needs more time), and > before > having a break we run "make check" at the root of the (source-)tree. To avoid > cluttering > the library with many makefiles, at every node (of the source-tree!) there > sits only a *link* > to one generic makefile (so we have only *one* makefile!), and that's it. > Now the cmake model seems to be, that either you clutter your source-tree with > make-files, and then you get the binaries etc. delivered to the source tree > (we > don't want that), or you create the make-files in the "build-tree", but then > you have to operate in that tree, not in the source-tree?!? (We find it very > convenient to only work in the source-sub-directory we are currently concerned > with, and the build system directs all output to the right places (somewhere > else); and having to "be" at two different places at the same time, in the > source-directory and in the related "build-directory", might open the > possibility for subtle bugs.) No, this is also trivial to do with cmake. My mk script does not handle this, but it should be easy to add. Rather than perhaps setting the variables from the bash script, you'd set them from your own cmake include file. Also note that cmake automatically generates rules for you (which you can easily list using 'make help', so running a particular test can also often be done from the root tree by doing "make <rule>"). > > Finally, it somehow seems to us that the conception of cmake is not really > that > of a powerful extension of make, but more of a convenient "user-interface", > with > the typical trade-off: What the user-interface does, it does convenient and > well, > but what it not does is very hard to integrate? > Pretty much. However cmake really does make creating Makefiles a breeze and helps to hide most of the limitations present in make and nmake, with a syntax that is much easier and readable than autohell (sorry, autoconf). Also, the Makefiles cmake creates are EXTREMELY efficient -- more even than those of autoconf. -- Gonzalo Garramuño [EMAIL PROTECTED] AMD4400 - ASUS48N-E GeForce7300GT Kubuntu Edgy
#!/bin/bash --norc # # Determine CPU architecture # KERNEL=`uname -s` if [[ $KERNEL == CYGWIN* ]]; then KERNEL=Windows RELEASE=(`cmd /c 'ver'`) RELEASE=${RELEASE[5]%.[0-9]*} elif [[ $KERNEL == MINGW* ]]; then KERNEL=Windows RELEASE=(`cmd /c 'ver'`) RELEASE=${RELEASE[4]%.[0-9]*} else RELEASE=`uname -r` fi CMAKE_OPTS=${CMAKE_OPTS=""} export CMAKE_NATIVE_ARCH=32 export CMAKE_BUILD_TYPE=Release export OS_32_BITS=1 export LDFLAGS= export CXXFLAGS= arch=`uname -a` if [[ $arch == *x86_64* ]]; then CMAKE_NATIVE_ARCH=64 export OS_64_BITS=1 else if [[ $arch == *ia64* ]]; then CMAKE_NATIVE_ARCH=64 export OS_64_BITS=1 unset OS_32_BITS fi fi export CMAKE_BUILD_ARCH=$CMAKE_NATIVE_ARCH OS=$KERNEL-$RELEASE usage() { cat <<EOF $0 $0 [options] [--] [make options] Wrapper around CMake to easily create out of source builds (ie. compilations where everything goes into a directory). For this platform (default settings): BUILD/$OS-$CMAKE_BUILD_ARCH/$CMAKE_BUILD_TYPE It must be run from a directory with a valid CMakeLists.txt file outside of the build tree. Options: -q quiet build -v verbose build (default) -j N number of cpus to use (default: ${CMAKE_PROCS=1}) -G <name> use a different cmake generator (default: Unix Makefiles) debug|release|reldebug|small debug - build a debug build release - build a release build (default) reldebug - build a release build with debug information small - build a small release build both|32|64 Builds both 32/64 bit versions, 32-bit only, 64-bit only (default: $CMAKE_BUILD_ARCH) cache - Cleans all CMakeCache.txt files swig - Cleans all swig files clean - Cleans BUILD/$OS-$CMAKE_BUILD_ARCH/$CMAKE_BUILD_TYPE cmake - Runs cmake only. EOF exit 1 } # # Parse command-line options # clean=0 if [[ $OS == Windows* ]]; then cmake_generator="NMake Makefiles" else cmake_generator="Unix Makefiles" fi opts='VERBOSE=1' RUN_MAKE=1 while [ $# -gt 0 ]; do case $1 in 64) shift CMAKE_BUILD_ARCH=64 ;; 32) shift CMAKE_BUILD_ARCH=32 export LDFLAGS=-m32 export CXXFLAGS=-m32 export CFLAGS=-m32 ;; both) shift CMAKE_BUILD_ARCH='Both' ;; cache) shift builddir=BUILD/$OS-$CMAKE_BUILD_ARCH/$CMAKE_BUILD_TYPE echo echo "Removing old cmake caches $builddir..." echo # Remove cache files find $builddir -name CMakeCache.txt -exec rm {} \; # Remove makefiles find $builddir -name Makefile -exec rm {} \; ;; clean) if [ -r CMakeLists.txt ]; then shift if [ -d BUILD ]; then clean=1 fi else break fi ;; cmake) shift RUN_MAKE=0 ;; debug) shift export CMAKE_BUILD_TYPE=Debug ;; release) shift export CMAKE_BUILD_TYPE=Release ;; small) shift export CMAKE_BUILD_TYPE=MinSizeRel ;; swig) shift # Remove swig wrappers builddir=BUILD/$OS-$CMAKE_BUILD_ARCH/$CMAKE_BUILD_TYPE echo echo "Removing old swig wrappers $builddir..." echo find $builddir -name '*_wrap.*' -exec rm {} \; ;; -h) shift usage ;; --help) shift usage ;; -j) shift if [ $# == 0 ]; then echo "Missing parameter for -j!" usage fi CMAKE_PROCS=$1 shift ;; -q) shift opts="" ;; -v) shift opts="VERBOSE=1" ;; -G) shift cmake_generator=$1 shift ;; --) shift break ;; *) break ;; esac done # # Simple function to run a command and print it out # run_cmd() { echo echo "> $*" echo eval command $* } # # Function to just run make on a dir with a Makefile # run_make() { if [ $RUN_MAKE == 0 ]; then return fi cmd='' if [[ $cmake_generator == NMake* ]]; then cmd="nmake $@" elif [[ $cmake_generator == Visual* ]]; then return else cmd="make -j ${CMAKE_PROCS=1} $@" fi run_cmd $cmd } # # Function to run cmake and then run make on generated Makefile # run_cmake() { builddir=BUILD/$OS-$CMAKE_BUILD_ARCH/$CMAKE_BUILD_TYPE echo "Buildir ${builddir}" if [ ! -d $builddir ]; then cmd="mkdir -p $builddir $builddir/bin $builddir/lib $builddir/tmp" run_cmd $cmd fi cmd="cd $builddir/tmp" run_cmd $cmd if [ -r Makefile ]; then run_make $@ cd ../../../.. return fi pwd=$PWD if [[ $KERNEL == Windows* ]]; then pwd=`cygpath -d $PWD` CMAKE_OPTS="-DCMAKE_CFG_INTDIR=/Release ${CMAKE_OPTS}" fi cmd="cmake ../../../.. -DCMAKE_SYSTEM=$OS -DEXECUTABLE_OUTPUT_PATH=$builddir/bin -DLIBRARY_OUTPUT_PATH=$builddir/lib -DCMAKE_LIBRARY_PATH=$builddir/lib -DCMAKE_NATIVE_ARCH=$CMAKE_NATIVE_ARCH -DCMAKE_BUILD_ARCH=$CMAKE_BUILD_ARCH -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -G '${cmake_generator}' ${CMAKE_OPTS}" run_cmd $cmd if [ $? != 0 ]; then exit fi run_make $@ cd ../../../.. } # # Function used to clean the build directory and exit # run_clean() { if [ $CMAKE_BUILD_ARCH == "Both" ]; then CMAKE_BUILD_ARCH=64 run_clean CMAKE_BUILD_ARCH=32 fi builddir=$OS-$CMAKE_BUILD_ARCH/$CMAKE_BUILD_TYPE cd $builddir run_make clean echo "Cleaned BUILD/$builddir" # rm -rf BUILD/$builddir } # # Main routine # if [ $clean == 1 ]; then run_clean exit 0 fi if [ -r Makefile ]; then run_make $opts $@ else if [ ! -r CMakeLists.txt ]; then echo "No Makefile or CMakeLists.txt in current directory!" exit 1 fi if [ ! -d BUILD ]; then mkdir BUILD fi if [ $CMAKE_BUILD_ARCH == 'Both' ]; then export CMAKE_BUILD_ARCH=32 export LDFLAGS=-m32 export CXXFLAGS=-m32 export CFLAGS=-m32 run_cmake $opts $@ export CMAKE_BUILD_ARCH=64 export LDFLAGS= export CXXFLAGS= export CFLAGS= run_cmake $opts $@ else run_cmake $opts $@ fi fi
#-*-cmake-*- # # This simple CMake extension makes sure that builds get # created inside a BUILD/os-osversion-arch directory # and that executables, obj files and lib files get placed # correctly in subdirectories. # # # Macro to check architecture # MACRO( CHECK_ARCHITECTURE ) SET( CMAKE_BUILD_ARCH $ENV{CMAKE_BUILD_ARCH} ) IF( NOT CMAKE_BUILD_ARCH ) SET( CMAKE_BUILD_ARCH "Native" ) ENDIF( NOT CMAKE_BUILD_ARCH ) IF( NOT CMAKE_BUILD_ARCH MATCHES "^(Native|64|32)$" ) MESSAGE( FATAL_ERROR "CMAKE_BUILD_ARCH set but invalid. " "Only Native, 64 and 32 are valid settings" ) ENDIF( NOT CMAKE_BUILD_ARCH MATCHES "^(Native|64|32)$" ) # # Set Native architecture based on void* size # INCLUDE(CheckTypeSize) CHECK_TYPE_SIZE(void* SIZEOF_VOID_PTR) IF( ${SIZEOF_VOID_PTR} MATCHES "^8$" ) SET( OS_32_BITS 0 ) SET( OS_64_BITS 1 ) SET( CMAKE_NATIVE_ARCH 64 ) ELSE( ${SIZEOF_VOID_PTR} MATCHES "^8$" ) SET( OS_32_BITS 1 ) SET( OS_64_BITS 0 ) SET( CMAKE_NATIVE_ARCH 32 ) ENDIF( ${SIZEOF_VOID_PTR} MATCHES "^8$" ) IF( UNIX ) EXECUTE_PROCESS( COMMAND uname -a OUTPUT_VARIABLE OS_ARCH ) # # For Linux # IF( OS_ARCH MATCHES ".*Linux.*" ) IF( OS_ARCH MATCHES ".*x86_64.*" ) SET( OS_32_BITS 1 ) ELSEIF( OS_ARCH MATCHES ".*ia64.*" ) SET( OS_32_BITS 0 ) ENDIF( OS_ARCH MATCHES ".*x86_64.*" ) ENDIF( OS_ARCH MATCHES ".*Linux.*" ) # # For SUN # IF( OS_ARCH MATCHES ".*SunOS.*" ) EXECUTE_PROCESS( COMMAND isainfo -v OUTPUT_VARIABLE SUNOS_ARCH ) IF ( SUNOS_ARCH MATCHES ".*64-bit.*" ) SET( OS_32_BITS 1 ) ENDIF( SUNOS_ARCH MATCHES ".*64-bit.*" ) ENDIF( OS_ARCH MATCHES ".*SunOS.*" ) IF( APPLE ) # # @todo: add Apple 64-bit OS detection here # ENDIF( APPLE ) ELSE( UNIX ) # # @todo: add windows 64-bit OS detection here # ENDIF( UNIX ) IF ( CMAKE_BUILD_ARCH STREQUAL "Native" ) SET( CMAKE_BUILD_ARCH ${CMAKE_NATIVE_ARCH} ) ENDIF( CMAKE_BUILD_ARCH STREQUAL "Native" ) IF( CMAKE_BUILD_ARCH EQUAL 32 ) IF( NOT OS_32_BITS ) MESSAGE( FATAL_ERROR "Sorry, but this platform cannot compile 32-bit applications" ) ENDIF( NOT OS_32_BITS ) MESSAGE( STATUS "check compiler" ) IF( CMAKE_COMPILER_IS_GNUCXX ) ADD_DEFINITIONS( -m32 ) SET( LINK_FLAGS "-m32 ${LINK_FLAGS}" ) ELSE( CMAKE_COMPILER_IS_GNUCXX ) MESSAGE( STATUS "checked compiler" ) # # @todo: add 32-bit compile flags for non-GNU compilers here # ENDIF( CMAKE_COMPILER_IS_GNUCXX ) ENDIF( CMAKE_BUILD_ARCH EQUAL 32 ) ENDMACRO( CHECK_ARCHITECTURE ) CHECK_ARCHITECTURE() # # Store build type # IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) ENDIF(NOT CMAKE_BUILD_TYPE) IF( NOT CMAKE_SYSTEM ) MESSAGE( FATAL_ERROR "CMAKE_SYSTEM was not set" ) ENDIF( NOT CMAKE_SYSTEM ) # # @bug in cmake2.5 in windows (workaround) # IF( NOT CMAKE_SYSTEM_VERSION ) SET( CMAKE_SYSTEM_VERSION "5.1" ) SET( CMAKE_SYSTEM "${CMAKE_SYSTEM}-${CMAKE_SYSTEM_VERSION}" ) ENDIF( NOT CMAKE_SYSTEM_VERSION ) IF( NOT CMAKE_BUILD_TYPE ) MESSAGE( FATAL_ERROR "CMAKE_BUILD_TYPE was not set" ) ENDIF( NOT CMAKE_BUILD_TYPE ) IF( NOT CMAKE_BUILD_ARCH ) MESSAGE( FATAL_ERROR "CMAKE_BUILD_ARCH was not set" ) ENDIF( NOT CMAKE_BUILD_ARCH ) SET( BUILD_DIR "${CMAKE_SOURCE_DIR}/BUILD/${CMAKE_SYSTEM}-${CMAKE_BUILD_ARCH}/${CMAKE_BUILD_TYPE}" ) SET( PROJECT_BINARY_DIR "${BUILD_DIR}" ) SET( EXECUTABLE_OUTPUT_PATH "${BUILD_DIR}/bin" ) # SET( CMAKE_BINARY_DIR "${BUILD_DIR}/obj" ) SET( LIBRARY_OUTPUT_PATH "${BUILD_DIR}/lib" ) SET( CMAKE_LIBRARY_PATH ${LIBRARY_OUTPUT_PATH} ${CMAKE_LIBRARY_PATH} ) FILE( MAKE_DIRECTORY "${BUILD_DIR}" ) FILE( MAKE_DIRECTORY "${PROJECT_BINARY_DIR}" ) FILE( MAKE_DIRECTORY "${EXECUTABLE_OUTPUT_PATH}" ) FILE( MAKE_DIRECTORY "${LIBRARY_OUTPUT_PATH}" ) # # Macro used to easily add linker flags to a target # MACRO( TARGET_LINK_FLAGS TARGET FLAGS ) GET_TARGET_PROPERTY( OLD_FLAGS ${TARGET} LINK_FLAGS ) SET_TARGET_PROPERTIES( ${TARGET} PROPERTIES LINK_FLAGS "${FLAGS} ${OLD_FLAGS}" ) ENDMACRO( TARGET_LINK_FLAGS )
_______________________________________________ CMake mailing list CMake@cmake.org http://www.cmake.org/mailman/listinfo/cmake