OWBuild

If your are looking for a short introduction, go here

OWBuild is a build system based on CMake (you need the last stable version: >= 2.4.4). OWBuild is not a fork of CMake, it is simply a set of macros for CMake that simplifies C/C++ CMake scripts writing.
OWBuild is under BSD license, check COPYING file

OWBuild Functionnalities

Writing OWBuild scripts has advantages over original CMake scripts

  • Very easy and clean syntax, a lot of work has been done on the API
  • Proper defines (compiler flags, link flags...) for Visual C++ and GNU GCC (can be very easily modified)
  • On the fly copy of binary files to ${BUILD_DIR} directory (see ow_post_build_copy_dir())
  • Handles .qm and .ts files generation (see ow_create_qt_qm_files())
  • MacOSX advanced support (frameworks, .dmg, .app) (see ow_change_install_name(), ow_use_private_frameworks()...)
  • Basic autoconf wrapper (see ow_autoconf())
  • Ensures unicity for include dirs, link/compile flags... (see ow_unique())
  • Easy functions to copy files inside ${BUILD_DIR} (see ow_copy_file(), ow_copy_dir()...)
  • Kind of oriented object: a project = an object = 1 CMakeLists.txt file (object with basic public/private attributes/functions)
  • Simple inheritance system (see ow_use_private_libraries() and ow_use_public_libraries())
  • SVN checkout function (see ow_svn_checkout())
  • Fully compatible with CMake and FindXXX.cmake modules: you can mix both and do whatever you want
  • Powerful '.dll problem' handling
  • Comes with a number of 3rdparty libraries FindXXX.cmake wrappers
  • Comes with a number of FindXXX.cmake files written by Andreas Schneider (in addition to the existing ones from CMake)

OWBuild is very small, they are only about 10 core functions that are not more than 20 lines each. All functions are well documented (there are even more comments than code actually) and source code is very clean and simple

OWBuild has been tested on:

  • Windows XP x86 32bits Visual C++ 7.1 (2003)
  • Windows XP x86 32bits Visual C++ 8.0 (2005)
  • Linux KUbuntu 4.10 x86 32bits GCC 4.0.2
  • Linux KUbuntu 6.10 AMD64 GCC 4.1.2
  • Linux Mandriva 2007 x86 GCC 3.3.6 & 4.1.1
  • MacOSX Tiger x86 32bits GCC 4.0.1
  • MacOSX Tiger PowerPC 32bits GCC 4.0.1

State

Beta, needs more testing (64 bits and MinGW)
OWBuild is used for WengoPhone? wiki:OWBuildWengoPhone? (complex program) and Qt Search & Replace (very basic program).

Known bugs and future improvements

  • ow_unique() seems buggy
  • ow_autoconf() is started at configuration state (should use add_command(POST_BUILD) or something in this way)
  • ow_copy_file() is started at configuration state (should use add_command(POST_BUILD) or something in this way)
  • This is for me: .dll handling to check again with a lot of sub-libraries

OWBuild is very interesting for complex projects, examples:

  • Your project contains several programs/libraries that depend on the Qt4 library, you need to specify special flags like -DQT_DLL, -DQT_NO_KEYWORDS for Qt4 and you want to link with QAxContainer (ActiveX component from Qt4 Commercial) under Windows.

If you use standard CMake scripts you have to write this code several times: http://dev.openwengo.com/trac/openwengo/trac.cgi/browser/owbuild/trunk/libs-3rdparty-cmakelists/qt4/CMakeLists.txt or you make a quick hack (e.g a simple macro that you call each time)
OWBuild is not a hack and handles it nicely:
you create a CMakeLists.txt qt4 project with proper defines, link flags, link libraries, include dirs using simple and straightforward functions like ow_add_public_definitions(), ow_add_public_include_dirs()... Then inside your programs/libraries that depend on the Qt4 library you simply write:

ow_create_executable(myProgramThatDependsOnQt4)

ow_use_private_libraries(
	qt4
)
  • You create a library with mylib.h and mylib.c that uses the cURL library.

Inside mylib.h you don't include curl.h thus programs that use mylib don't need to know anything about cURL, this is mylib own problem.

ow_create_shared_library(mylib)

ow_use_private_libraries(
	curl
)

mylib will use curl defines, link flags, link libraries, include dirs... but programs that use mylib won't know that mylib uses cURL since it is declared private. This functionnality avoids to have unnecessary include dirs and thus reduce compilation time

  • OWBuild permits to separate subproject and to make them totally independant

Your program myprogram uses mylib

ow_create_executable(myprogram)

ow_use_private_libraries(
	mylib
)

It does not matter if mylib is located on another svn repository (see ow_svn_checkout()), or different directories. You can reorganize your components without troubles

  • On the fly copy will copy mylib.dll (.so, .dylib) as soon as it is compiled automatically to ${BUILD_DIR} directory, same for myprogram.exe

You don't have to wait for the full build completion (via the install target) before to get easily access to your binaries. This allows easier developpement: component A does not compile, but B does and you can work on it.

Things to know about CMake

CMake is a makefile generator: you run command cmake from a build directory, it reads CMakeLists.txt files and generates makefiles for various compilers. This is a different approach than SCons, SCons handles everything from A to Z without relying on makefiles. In opposition to SCons which is based on Python language, CMake is based on a simple macro language. In a sense CMake is a "super" QMake

Scripts are named CMakeLists.txt, check WengoPhone? CMakeLists.txt files: there are easy to understand and you will learn a lot from them. CMake language is case insensitive but try to write scripts in lower-case, it's always easier to read (less aggressive for the reader). Here a few things about CMake macro language:

# Sets the variable tmp to value "hello"
set(tmp "hello")

# printf("%s", tmp)
message(STATUS ${tmp})

if (tmp MATCHES "hello")
	set(tmp "${tmp} world")
	# Now tmp contains string "hello world"
else (tmp MATCHES "hello")
	set(tmp "bonjour")
endif (tmp MATCHES "hello")

# Variable list is a list
set(list
	"hello"
	"bonjour"
	"hola"
)

# Simple loop
foreach (elem ${list})
	# printf() each element from the list
	message(STATUS ${elem})
endforeach (elem ${list})

History

QuteCom started using QMake, QMake is a very nice tool but not powerful enough. So we switched to SCons but it provides quite a low level API thus we decided to factorize code inside WengoSCons?. We faced the same problem than KDE with SCons: not very scallable, too slow. We finally decided to use CMake like the KDE project.

While CMake provides a good API, WengoPhone? project is about more than 100 build system scripts. OWBuild is a set of macros above CMake to factorize code and to make it easy to create build system scripts.

OWBuild Reference Documentation

CMake Documentation: http://cmake.org/HTML/Documentation.html
CMake Variables: http://www.cmake.org/Wiki/CMake_Useful_Variables http://www.cmake.org/Wiki/CMake_Useful_Variables/Get_Variables_From_CMake_Dashboards

Reference documentation is available as comments inside source code http://dev.openwengo.com/trac/openwengo/trac.cgi/browser/owbuild/trunk/owbuild/owbuild

For an easy search inside OWBuild source code, here everything inside 1 file (automatically generated by ow_dump_owbuild()): http://dev.openwengo.org/trac/openwengo/trac.cgi/browser/owbuild/trunk/owbuild/owbuild/owbuild-dump.txt

README file: http://dev.openwengo.org/trac/openwengo/trac.cgi/browser/owbuild/trunk/owbuild/owbuild/README

A project (library or executable) has several attributes/properties (like an object in OOP), they are internal to OWBuild but exploring them make it easy for the reader to understand the general idea:
- PROJECT_NAME = project name
- ${PROJECT_NAME}_PROJECT_TYPE = project type (Static, Shared, Plugin, Executable)
- ${PROJECT_NAME}_SRCS = list of sources
- ${PROJECT_NAME}_PUBLIC_INCLUDE_DIRS = public include directories
- ${PROJECT_NAME}_PRIVATE_INCLUDE_DIRS = private include directories
- ${PROJECT_NAME}_PUBLIC_LIBRARIES = public link libraries
- ${PROJECT_NAME}_PRIVATE_LIBRARIES = private link libraries
- ${PROJECT_NAME}_PUBLIC_LIBRARY_DIRS = public library directories
- ${PROJECT_NAME}_PRIVATE_LIBRARY_DIRS = private library directories
- ${PROJECT_NAME}_PUBLIC_DEFINITIONS = public compiler defines
- ${PROJECT_NAME}_PRIVATE_DEFINITIONS = private compiler defines
- ${PROJECT_NAME}_PRIVATE_COMPILE_FLAGS = private compiler flags
- ${PROJECT_NAME}_PUBLIC_LINK_FLAGS = public linker flags
- ${PROJECT_NAME}_PRIVATE_LINK_FLAGS = private linker flags
- ${PROJECT_NAME}_BUILD_VERSION = build version number
- ${PROJECT_NAME}_API_VERSION = API version number

Names were not taken randomly, they were carefully chosen to be compatible with FindXXX.cmake files: http://www.cmake.org/cgi-bin/viewcvs.cgi/Modules/readme.txt?root=CMake&view=markup

Check files ow_project_log() and ow_create_project()

These variables are modified by the functions below:

Creates an executable (.exe) given its name

Creates a plugin library (e.g a shared library) given its name

Creates a shared library (.dll, .dylib, .so) given its name

Creates a static library (.lib, .a) given its name

Creates an empty project given its name

Creates a binary (static/shared/plugin library or a executable) using the current project

Adds source files (.cpp, .c...) to the current project

Uses privately a library inside the current project: imports properties from a library to the current project

Uses publicly a library inside the current project: imports properties from a library to the current project

Adds private compilation flags to the current project

Adds private -D define flags to the current project

Adds public -D define flags to the current project

Adds private include directories to the current project

Adds public include directories to the current project

Adds private link libraries to the current project

Adds public link libraries to the current project

Adds private link flags to the current project

Adds public link flags to the current project

Copies a file to ${dst} only if ${src} is different (newer) than ${dst}

Copies a directory to ${dst} only if ${src} is different (newer) than ${dst}

Copies recursively a directory to ${dst} only if ${src} is different (newer) than ${dst}

Sets the library version (build + api) for the current project

Does a subversion checkout from an url to a destination directory ${CMAKE_CURRENT_SOURCE_DIR}/${dst}

Gets current subversion revision number

Specific to Qt4 library, updates .ts files and creates .qm files

Runs Autoconf tool

Defines general default CMake configuration options

Defines default compiler/linker flags

Defines global public variables

Ensures build directory is different from source directory

Declares a CMakeLists.txt that will be run when ${PROJECT_NAME}-install target is run

Shows general debug informations (OS, processor, compiler, build directory, build type...)

Detects system compiler and OS used

MacOSX Functions

Creates a .dmg for MacOSX

MacOSX only, uses privately a framework inside the current project: imports properties from a framework to the current project

MacOSX only, uses publicly a framework inside the current project: imports properties from a framework to the current project

MacOSX only, calls "install_name_tool -change ${src} ${dst} ${file}"

Utility Functions

Internal function, checks if ${string} is in ${list}

Internal function, makes the given list have only one instance of each unique element

Internal Functions

Checks whether the compiler supports a given flag

Internal function, shows debug informations about the current project

Internal function, copies a file to ${BUILD_DIR} using the current project

Internal function, copies a directory to ${BUILD_DIR} using the current project

Internal function, adds shared library definitions for declspec(dllimport) and declspec(dllexport)

Internal function, prepares the creation of a binary file, used by ow_create_binary()

Internal function, finishes the creation of a binary file, used by ow_create_shared_library(), ow_create_executable()...

Internal function, checks if the project is valid or not

Internal function, dump OWBuild source code in one file

Internal function, gets GNU GCC version number

Examples

Here is a simple example with 2 libraries named owcutil and owutil:

ow_create_shared_library(owcutil)

ow_add_public_include_dirs(
	${CMAKE_CURRENT_SOURCE_DIR}/include
)

ow_add_sources(
	src/fake.c
	src/strlcat.c
	src/strlcpy.c
)

if (MSVC)
	ow_add_public_include_dirs(
		${CMAKE_CURRENT_SOURCE_DIR}/include/cutil/msvc
	)
	ow_add_sources(
		src/msvc/dirent.c
	)
endif (MSVC)

ow_create_binary()
ow_create_shared_library(owutil)

ow_use_public_libraries(
	owcutil
	BOOST
)

ow_add_public_include_dirs(
	${CMAKE_CURRENT_SOURCE_DIR}/include
)

ow_add_sources(
	src/exception/Exception.cpp
	src/Base64.cpp
	src/CountryList.cpp
	src/Date.cpp
	src/File.cpp
	src/Logger.cpp
	src/OWPicture.cpp
	src/Path.cpp
	src/String.cpp
	src/StringList.cpp
	src/Time.cpp
	src/Uuid.cpp
	src/WebBrowser.cpp
)

if (WIN32)
	ow_add_sources(
		src/win/UuidWin.cpp
	)
	ow_add_private_libraries(
		Rpcrt4
	)
endif (WIN32)

if (APPLE)
	ow_add_sources(
		src/mac/UuidMac.cpp
	)
	ow_use_private_frameworks(
		CoreFoundation
	)
endif (APPLE)

if (LINUX)
	ow_use_public_libraries(
		UUID
	)
	ow_add_sources(
		src/unix/UuidUnix.cpp
	)
endif (LINUX)

ow_create_binary()

Here the equivalent without using OWBuild macros (original files owcutil and owutil):

project(owcutil)

set(OWCUTIL_INCLUDE_DIRS
	${CMAKE_CURRENT_SOURCE_DIR}/include
	CACHE INTERNAL "owcutil include directory"
)

set(OWCUTIL_LIBRARY
	owcutil
	CACHE INTERNAL "owcutil library"
)

set(OWCUTIL_LINK_LIBRARIES
	${OWCUTIL_LIBRARY}
)

set(owcutil_SRCS
	src/fake.c
	src/strlcat.c
	src/strlcpy.c
)

include_directories(${OWCUTIL_INCLUDE_DIRS})

if (MSVC)
	set(OWCUTIL_INCLUDE_DIRS
		${OWCUTIL_INCLUDE_DIRS}
		${owcutil_SOURCE_DIR}/include/cutil/msvc
		CACHE INTERNAL "owcutil include directory"
	)
	set(owcutil_SRCS
		${owcutil_SRCS}
		src/msvc/dirent.c
	)
endif (MSVC)

add_library(${OWCUTIL_LIBRARY} STATIC ${owcutil_SRCS})
project(owutil)

if (UNIX AND NOT WIN32 AND NOT APPLE)
	find_package(UUID REQUIRED)
endif (UNIX AND NOT WIN32 AND NOT APPLE)

set(OWUTIL_INCLUDE_DIRS
	${CMAKE_CURRENT_SOURCE_DIR}/include
	${OWCUTIL_INCLUDE_DIRS}
	${BOOST_INCLUDE_DIRS}
	CACHE INTERNAL "owutil include directories"
)

set(OWUTIL_LIBRARY
	owutil
	CACHE INTERNAL "owutil library"
)

set(OWUTIL_LINK_LIBRARIES
	${OWUTIL_LIBRARY}
	${OWCUTIL_LIBRARY}
	${BOOST_SIGNALS_LIBRARY}
)

set(owutil_SRCS
	src/exception/Exception.cpp
	src/Base64.cpp
	src/CountryList.cpp
	src/Date.cpp
	src/File.cpp
	src/Logger.cpp
	src/OWPicture.cpp
	src/Path.cpp
	src/String.cpp
	src/StringList.cpp
	src/Time.cpp
	src/Uuid.cpp
	src/WebBrowser.cpp
)

if (WIN32)
	set(owutil_SRCS ${owutil_SRCS}
		src/win/UuidWin.cpp
	)
	set(OWUTIL_LINK_LIBRARIES
		${OWUTIL_LINK_LIBRARIES}
		Rpcrt4
	)
endif (WIN32)


if (UNIX AND NOT WIN32)
	if (APPLE)
		set(owutil_SRCS ${owutil_SRCS}
			src/mac/UuidMac.cpp
		)
	else (APPLE)
		include_directories(${UUID_INCLUDE_DIRS})
		set(owutil_SRCS ${owutil_SRCS}
			src/unix/UuidUnix.cpp
		)

		set(OWUTIL_LINK_LIBRARIES
			${OWUTIL_LINK_LIBRARIES}
			${UUID_LIBRARIES}
		)
	endif (APPLE)
endif (UNIX AND NOT WIN32)

include_directories(
	${OWUTIL_INCLUDE_DIRS}
)

add_library(${OWUTIL_LIBRARY} STATIC ${owutil_SRCS})

target_link_libraries(${OWUTIL_LINK_LIBRARIES})

if (APPLE)
	macro_add_link_flags(owutil
		"-framework CoreFoundation"
	)
endif (APPLE)

OWBuild provides the notion of public/private (e.g internal/external TODO change the naming?) why?

Imagine you build a .dll under Windows (GCC4 provides this functionality via attribute) you need to use declspec(dllimport) when using the .dll and declspec(dllexport) when compiling it.

#ifdef OWCUTIL_DLL
	#ifdef BUILD_OWCUTIL_DLL
		#define OWCUTIL_API __declspec(dllexport)
	#else
		#define OWCUTIL_API __declspec(dllimport)
	#endif
#else
	#define OWCUTIL_API
#endif

when you build the .dll you need to define OWCUTIL_DLL and BUILD_OWCUTIL_DLL but when you use the .dll you only have to define OWCUTIL_DLL

This is exactly public/private is for:

ow_create_shared_library(owcutil)

ow_add_public_definitions(
	-DOWCUTIL_DLL
)

ow_add_private_definitions(
	-DOWCUTIL_DLL
	-DBUILD_OWCUTIL_DLL
)
ow_create_shared_library(owutil)

ow_use_public_libraries(
	owcutil
)

Library owutil will "inherit" (publicly here) everything that is public from library owcutil, thus owutil defines OWCUTIL_DLL but not BUILD_OWCUTIL_DLL since it is private.

The DLL example is good since OWBuild integrates an automatic system that permits to select which library has to be compiled as a shared or static library + it adds automatically the right definitions e.g -DOWCUTIL_DLL or -DBUILD_OWCUTIL_DLL

General Rules

1 CMakeLists.txt = 1 project

A project always starts by a:

ow_create_static_library(name)

or:

ow_create_shared_library(name)

or:

ow_create_plugin_library(name)

or:

ow_create_executable(name)

and finishes by:

ow_create_binary()

Everything after ow_create_binary() won't be handled, ow_create_binary() should be the last thing inside a CMakeLists.txt

find_package()

CMake provides a find_package() functionality for external libraries:

# Find and setup Boost library for this project
find_package(Boost REQUIRED)

# Now some variables are defined in order to use Boost library:
# BOOST_FOUND - System has Boost
# BOOST_INCLUDE_DIRS - Boost include directory
# BOOST_LIBRARIES - Link these to use Boost
# BOOST_LIBRARY_DIRS - The path to where the Boost library files are.
# BOOST_DEFINITIONS - Compiler switches required for using Boost

CMake includes a number of FindXXX.cmake files for a lot of libraries, however some are missing and we created them. In order to be more consistent, all FindXXX.cmake files/find_package() are wrapped inside libs-3rdparty-cmakelists/*/CMakeLists.txt files. You just need to write:

ow_use_public_libraries(
	boost
)

Note that word boost is lower-case, everything that comes from OWBuild 3rdparty CMakeLists.txt is lowercase to make the difference with variables from the 'outside world'. Why not use find_package() directly without libs-3rdparty-cmakelists/*/CMakeLists.txt? because they do a little bit more than just wrapping find_package(), for example the one for Boost:

if (NOT BOOST_FOUND)
	find_package(Boost REQUIRED)
endif (NOT BOOST_FOUND)

ow_create_project(boost)

ow_use_public_libraries(
	BOOST
)

if (NOT MSVC)
	set(boost_LIBRARIES
		${BOOST_PROGRAM_OPTIONS_LIBRARY}
		${BOOST_REGEX_LIBRARY}
		${BOOST_SERIALIZATION_LIBRARY}
		${BOOST_SIGNALS_LIBRARY}
		${BOOST_THREAD_LIBRARY}
		CACHE INTERNAL "${PROJECT_NAME} libraries"
	)
endif (NOT MSVC)

ow_add_public_definitions(
	-DBOOST_ALL_DYN_LINK
	${BOOST_LIB_DIAGNOSTIC_DEFINITIONS}
)

if (MSVC)
	ow_add_public_definitions(
		/wd4251
		/wd4275
		/wd4675
	)

	ow_copy_dir(${BOOST_LIBRARY_DIRS}/*.dll .)
endif (MSVC)

There is another good reason to not use FindXXX.cmake directly, take FFmpeg: for WengoPhone? we need via FFMPEG_INTERNAL variable to compile FFmpeg from our repository or to use FFmpeg installed on the system thus we need several CMakeLists.txt (libs-3rdparty-cmakelists/ffmpeg/CMakeLists-internal-msvc.txt, libs-3rdparty-cmakelists/ffmpeg/CMakeLists-internal-macosx.txt, libs-3rdparty-cmakelists/ffmpeg/CMakeLists-external.txt...)

Useful variables

(Values are just indications)

  • BUILD_DIR = build/debug
  • BUILD_TYPE = same as CMAKE_BUILD_TYPE but lowercase (debug, release, minsizerel...)
  • SVN_REVISION = svn revision
  • CMAKE_SOURCE_DIR = root directory
  • CMAKE_CURRENT_SOURCE_DIR = current source directory
  • CMAKE_BINARY_DIR = build/ directory
  • CMAKE_CURRENT_BINARY_DIR = current binary directory
  • CMAKE_BUILD_TYPE = Debug, Release...
  • CMAKE_PROJECT_NAME = project name

Make targets

  • make

Compiles everything

  • make PROJECT_NAME

Compiles a project and its dependencies

  • make clean

Clean all compiled files

  • make install

Install target

  • make -DCMAKE_BUILD_TYPE=Release

Compiles everything in release mode (options are: None Debug Release RelWithDebInfo? MinSizeRel?)

FAQ - compilation corner cases

100% CPU during compilation under windows

If you get a 100% CPU under Windows during a compilation with CMake, it might be due to LVPrcSrv.exe This is the "Logitech Process Monitor" installed with some Logitech webcams. Don't ask why it does not behave well with CMake, but that's a fact. If you remove it from the list of active services, the problem should disappear. In order to remove it, open "services.msc" from Start/Run...

Attachments