Showing posts with label CMake. Show all posts
Showing posts with label CMake. Show all posts

Saturday, December 1, 2012

CMake: using an external library

There are plenty of software libraries designed to accomplish useful tasks, so using them is a common requirement. This is not particularly difficult to do, and after some testing I have developed the following solution. It works for any library, not only for wxWidgets ones, but for this example I will show how to use wxPdfDocument, an excellent library for PDF files creation.

Here is the CMakeFiles.txt code:

 # look for wxPdfDocument  
 #  ...include  
 find_path( WXPDFDOC_INCLUDE_PATH pdfdc29.h DOC "wxPdfDocument include files path" )  
 if( NOT WXPDFDOC_INCLUDE_PATH )  
  message( FATAL_ERROR "Unable to find wxPdfDocument include path" )  
 else( NOT WXPDFDOC_INCLUDE_PATH )  
  # up une folder to include <wx/....h>  
  set( WXPDFDOC_INCLUDE_PATH ${WXPDFDOC_INCLUDE_PATH}/.. )  
  message( STATUS "wxPdfDocument include path: " ${WXPDFDOC_INCLUDE_PATH} )  
 endif( NOT WXPDFDOC_INCLUDE_PATH )  
 #  ...libraries  
 if(WIN32)  
  find_file( WXPDFDOC_LIB_PATH_DEBUG wxcode_msw29ud_pdfdoc.lib DOC "Path to the DEBUG wxPdfDocument library" )  
  find_file( WXPDFDOC_LIB_PATH_RELEASE wxcode_msw29u_pdfdoc.lib DOC "Path to the RELEASE wxPdfDocument library" )  
  if( NOT WXPDFDOC_LIB_PATH_DEBUG OR NOT WXPDFDOC_LIB_PATH_RELEASE )  
   message( FATAL_ERROR "Unable to find wxPdfDocument library files" )  
  else ( NOT WXPDFDOC_LIB_PATH_DEBUG OR NOT WXPDFDOC_LIB_PATH_RELEASE )  
   message( STATUS "wxPdfDocument debug  library: " ${WXPDFDOC_LIB_PATH_DEBUG} )  
   message( STATUS "wxPdfDocument release library: " ${WXPDFDOC_LIB_PATH_RELEASE} )  
  endif ( NOT WXPDFDOC_LIB_PATH_DEBUG OR NOT WXPDFDOC_LIB_PATH_RELEASE )  
 else(WIN32)  
  find_library( WXPDFDOC_LIB_PATH libwxcode_gtk2u_pdfdoc-2.9.a )  
  if( NOT WXPDFDOC_LIB_PATH )  
   message( FATAL_ERROR "Unable to find wxPdfDocument library file" )  
  else( NOT WXPDFDOC_LIB_PATH )  
   message( STATUS "wxPdfDocument library: " ${WXPDFDOC_LIB_PATH} )  
  endif( NOT WXPDFDOC_LIB_PATH )  
 endif(WIN32)  
   
 # link and include  
 if(WIN32)  
  target_link_libraries( GeASt debug ${WXPDFDOC_LIB_PATH_DEBUG} optimized ${WXPDFDOC_LIB_PATH_RELEASE} )  
 else(WIN32)  
  target_link_libraries( GeASt ${WXPDFDOC_LIB_PATH} )  
 endif(WIN32)  
 include_directories( ${WXPDFDOC_INCLUDE_PATH} )  
 


The code looks for the include files and the library file(s), then it uses those information to set the include path and to link the libraries.

The behaviour is different for Windows and for *nix operating systems. Under Windows there are two different libraries, one for the debug and one for the release build. Under Linux and Mac there is a single library file.

An important note for wxWidgets users: always link the wxWidgets libraries AFTER all other libraries, otherwise you might get linker errors (undefined symbols). This happened to me under Linux.

This means that the

 target_link_libraries(GeASt ${wxWidgets_LIBRARIES})  
   


line must be the last TARGET_LINK_LIBRARIES line.


Thursday, January 6, 2011

CMake - Install - Linux and OS X

As described in previous posts deploying a program that uses an embedded copy of Firebird requires shipping some database server files. For Windows this can be done by the software that creates the installation package.

For Linux and OS X it is possible to use the CMake INSTALL command. This command creates an INSTALL target in your development tool: buiding that target will copy files to the folder specified by the CMAKE_INSTALL_PREFIX variable.
This can be used to copy the compiled program, the Firebird runtime and other files as needed to the install folder. Then the install folder will contain a ready to run copy of the program, with all the needed files.

First use a variable to store the path of the files used by the Firebird embedded server. I keep them in a folder that has the same structure as the deployed files. Here is an example:

  # look for the folder containing the firebird embedded files to ship with the program
  find_path( FB_EMBEDDED_PATH firebird.conf ${PROJECT_SOURCE_DIR}/firebird_runtime ${PROJECT_SOURCE_DIR}/../../firebird_embedded_2.0.4_runtime )
  message( STATUS "Embedded firebird files path: " ${FB_EMBEDDED_PATH} )

At the end of CMakeLists.txt add code like this:

install( TARGETS vvv DESTINATION . )

if( APPLE )
  # copy the Firebird runtime
  install( DIRECTORY ${FB_EMBEDDED_PATH}/ DESTINATION vvv.app/Contents/MacOS USE_SOURCE_PERMISSIONS )
  install( FILES ${FB_EMBEDDED_PATH}/firebird/firebird.msg DESTINATION vvv.app/Contents/MacOS/firebird/bin/firebird )
  # fix filename references in the runtime
  set( FIXUP_COMMAND ${PROJECT_SOURCE_DIR}/MACOSX_fixup_bundle.sh " " ${CMAKE_INSTALL_PREFIX}/vvv.app )
  install( CODE "execute_process( COMMAND ${FIXUP_COMMAND} )" )
endif( APPLE )

if( UNIX AND NOT APPLE )
  # copy the Firebird runtime
  install( DIRECTORY ${FB_EMBEDDED_PATH}/ DESTINATION . USE_SOURCE_PERMISSIONS )
  # copy other files used only for the Linux version
  install( FILES ${PROJECT_SOURCE_DIR}/linux_specific/readme.txt
                 ${PROJECT_SOURCE_DIR}/linux_specific/License.txt
                 DESTINATION . )
  install( FILES ${PROJECT_SOURCE_DIR}/linux_specific/vvv-start.sh
                 DESTINATION . PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE
                                           GROUP_READ WORLD_READ WORLD_EXECUTE )
endif( UNIX AND NOT APPLE )

if( UNIX )
  # set the installation path of the executable file and the resources (in OS X they are inside the bundle )
  if( APPLE )
    set( EXECUTABLE_INTALL_PATH vvv.app/Contents/MacOS )
    set( RESOURCES_INTALL_PATH vvv.app/Contents/Resources )
  else( APPLE )
    set( EXECUTABLE_INTALL_PATH . )
    set( RESOURCES_INTALL_PATH . )
  endif( APPLE )



  # copy other files to the installation path
  install( FILES ${PROJECT_SOURCE_DIR}/vvv-struct-update.fdb
                 ${PROJECT_SOURCE_DIR}/VVV.fbk
                 ${PROJECT_SOURCE_DIR}/help/en/vvv.htb
                 DESTINATION ${EXECUTABLE_INTALL_PATH} )


endif( UNIX )

Under OS X CMake will execute a script named MACOSX_fixup_bundle.sh to patch the dylibs as described here. CMake has some support to automatically fix things like these: I struggled for some time but I was not able to understand how it works. Documentation is scarce to I gave up and I decided to directly run a script.
Here is the script used by CMake:

#!/bin/bash

# this script will fix the components of the Firebird runtime to make it run from any location

# the script receives the path of the bundle to fix
BUNDLEPATH=$*

# the following line contains the name of the executable file that will be patched
# it is the only line that should be changed when copying this file to another project
EXECFILE=${BUNDLEPATH}/Contents/MacOS/vvv

LIBPATH=${BUNDLEPATH}/Contents/MacOS/firebird
LIBBINPATH=${BUNDLEPATH}/Contents/MacOS/firebird/bin
# path of library files relative to the main executable
NEWLIBPATH="@executable_path/firebird"
# path of library files relative to other library files
NEWLIBPATH_FOR_LIBS="@loader_path"
# path of library files relative to executables in the "firebird/bin" folder
NEWLIBPATH_FROM_BIN="@loader_path/.."
OLDLIBPATH="/Library/Frameworks/Firebird.framework/Versions/A/Libraries"
OLDLIBFBEMBEDFILENAME="/Library/Frameworks/Firebird.framework/Versions/A/Firebird"

# change the references in the files contained in the "firebird" folder
for TARGET in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
  LIBFILE=${LIBPATH}/${TARGET}
  OLDTARGETID=${OLDLIBPATH}/${TARGET}
  NEWTARGETID=${NEWLIBPATH}/${TARGET}
  NEWTARGETID_FOR_LIBS=${NEWLIBPATH_FOR_LIBS}/${TARGET}
  install_name_tool -id ${NEWTARGETID_FOR_LIBS} ${LIBFILE}
  install_name_tool -change ${OLDTARGETID} ${NEWTARGETID} ${EXECFILE}
  for POSSIBLECALLERNAME in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
    POSSIBLECALLERFILE=${LIBPATH}/${POSSIBLECALLERNAME}
    install_name_tool -change ${OLDTARGETID} ${NEWTARGETID_FOR_LIBS} ${POSSIBLECALLERFILE}
  done
done

# change the references in the files contained in the "firebird/bin" folder
for TARGET in gbak isql ; do
  FILE=${LIBBINPATH}/${TARGET}
  for POSSIBLECALLEDNAME in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
    OLDTARGETID=${OLDLIBPATH}/${POSSIBLECALLEDNAME}
    NEWTARGETID=${NEWLIBPATH_FROM_BIN}/${POSSIBLECALLEDNAME}
    install_name_tool -change ${OLDTARGETID} ${NEWTARGETID} ${FILE}
  done
  # change the reference to libfbembed into the program, that contains a reference to a different name (the framework name)
  NEWTARGETID=${NEWLIBPATH_FROM_BIN}/libfbembed.dylib
  install_name_tool -change ${OLDLIBFBEMBEDFILENAME} ${NEWTARGETID} ${FILE}
done

# change the reference to libfbembed into the caller program, that contains a reference to a different name (the framework name)
NEWTARGETID=${NEWLIBPATH}/libfbembed.dylib
install_name_tool -change ${OLDLIBFBEMBEDFILENAME} ${NEWTARGETID} ${EXECFILE}



You will need to edit the EXECFILE definition (near the file top) to change the name from "vvv" to your program's name.

Monday, December 20, 2010

CMake - Build an OS X Bundle

OS X applications are structured as bundles. A bundle is a folder with a predefined structure that contains the program and other files.
It is possible to create an application bundle with CMake using specific commands.

First we must specify the files containing the application icons. For example:

#--------------------------------------------------------------------------------
# For Apple set the icns file containing icons
if(APPLE)
  # icon files to copy in the bundle
  set( OSX_ICON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/graphics/vvv.icns ${CMAKE_CURRENT_SOURCE_DIR}/graphics/vvv-document.icns )
  # set where in the bundle to put the icns files
  set_source_files_properties( ${OSX_ICON_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
  # include the icns files in the target
  set( SRCS ${SRCS} ${OSX_ICON_FILES} )
ENDIF(APPLE)


add_executable( vvv WIN32 MACOSX_BUNDLE ${SRCS} )

This example defines two icons, one for the application and one for the application documents. The SRCS variable already contains the list of source files and this code adds the icons to the files list.
The ADD_EXECUTABLE command in the last line tells CMake to create an OS X bundle.

The following code will tell CMake where to find the Info.plist that will be copied into the bundle:

if(APPLE)
  # configure CMake to use a custom Info.plist
  set_target_properties( vvv PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/vvv-Info.plist )
ENDIF(APPLE)

Using the code above CMake will be able to create an OS X application bundle.

Friday, December 3, 2010

CMake - wxWidgets

CMake is released with a large set of source code that can be used to accomplish common tasks. One of this tasks is looking for a package used by your project.

For example, let's see how to look for the wxWidgets library. We can use the FIND_PACKAGE command for this purpose: CMake already knows how to handle wxWidgets.

Just add the following lines to CMakeLists.txt:

FIND_PACKAGE(wxWidgets REQUIRED html adv core base net aui xrc qa richtext )
INCLUDE(${wxWidgets_USE_FILE})
TARGET_LINK_LIBRARIES(myTarget ${wxWidgets_LIBRARIES})


The first line tells CMake to look for wxWidgets. The REQUIRED clause says that you require the specified modules.
The FIND_PACKAGE command executes some code and sets a number of variables if it finds the library.
The second line uses one of those variables to tell CMake where to look for include files.
The third line tells CMake how to link a target with wxWidgets.

Now let's see what happens when we run CMake from its GUI. Press Configure and you will probably see a number of red rows that need to be fixed. The rows content changes with the operating system.

Linux

I create two build folders, one for the debug configuration and one for the release one.

You need to look at the following variables:
  • CMAKE_BUILD_TYPE: set it to Debug for a build folder and to Release for the other.
  • wxWidgets_CONFIG_EXECUTABLE: it is the path to the wx-config script for the chosen copy of wxWidgets. For example, /usr/local/bin/wx-config for a library that has been installed, or /home/fulvio/wxSVN/buildgtk/wx-config for a library that has not been installed. In the library has not been installed you must point to the wx-config file in the folder with the right configuration (for example debug or not).
  • wxWidgets_USE_DEBUG: check it if you want to use a debug version of the library.
  • wxWIDGETS_USE_STATIC: check it if you ant to use a static version of the library.
  • wxWidgets_USE_UNICODE: check it if you want to use the Unicode version of the library. If you are using version 2.9 or later this setting does not make sense any more (the llibrary is only Unicode): I leave it checked and everything works well.
  • wxWidgets_wxrc_EXECUTABLE: this is probably a wxrc setting. Since I do not use it I left its value set to NOTFOUND.
OS X

I create two build folders, one for the debug configuration and one for the release one.

You need to look at the following variables:
  • CMAKE_BUILD_TYPE: set it to Debug for a build folder and to Release for the other.
  • CMAKE_OSX_ARCHITECTURES:  I set it to i386.
  • CMAKE_OSX_DEPLOYMENT_TARGET: I leave this blank.
  • wxWidgets_CONFIG_EXECUTABLE: it is the path to the wx-config script for the chosen copy of wxWidgets. For example /Users/fulvio/wxMac-2.8.10/buildgtk/wx-config for a library that has not been installed. In the library has not been installed you must point to the wx-config file in the folder with the right configuration (for example debug or not).
  • wxWidgets_wxrc_EXECUTABLE: this is probably a wxrc setting. Since I do not use it I left its value set to NOTFOUND.
Windows

In Windows I create a single build folder. It will contain a Visual Studio project file with both a debug and a release configuration.

You need to look at the following variables:
  • wxWidgets_CONFIGURATION: set to mswud, mswu or mswd. This is not very clear to me, but I set it to mswu and everything works well.
  • wxWidgets_ROOT_DIR: it is the path to the root folder of the wxWidgets installation, for example E:/wxWidgets-2.8.10. If CMake does not find the path you need to set it manually and configure again.
  • wxWidgets_LIB_DIR: it is the path of the folder that contains the libraries that will be linked to your program. Usually CMake can find this path by itself if it knows the root dir.
  • wxWidgets_USE_REL_AND_DBG is a boolean value. Check it if you want to have a project with both a debug and a release configuration. You will probably check it.
  • wxWidgets_wxrc_EXECUTABLE: this is probably a wxrc setting. Since I do not use it I left its value set to NOTFOUND.
As you can see configuring CMake to use your copy of wxWidgets is an easy task.
It is also easy to use different versions of wxWidgets with your project. Just build the different versions in different folders (do not install them in *nix), then create different build folders for the different wxWidgets versions. For each folder configure CMake to look for wxWidgets in the right folder, manually setting the required variables to the right value.

Thursday, December 2, 2010

CMake

My purpose is creating a program that can be compiled for Linux, OS X and Windows. If you have read older posts you already know that I was successful and I am already able to do it.

After some time spent developing a program for all these operating system it becomes obvious that handling the build files is a great problem. I have the Visual Studio project file for Windows, the KDevelop files for Linux and the XCode project for OS X. Maintaining all these files takes time and it is prone to errors. Moreover there are other kind of problems: I am still using Visual Studio 2003: if another developer checks out the source code and opens the project with a newer version of Visual Studio the project cannot be read by my older version any more. I could manually maintains different project files for different VS versions, but this is clearly a crazy thing to do.
Last but not least, creating the files for a new project is difficult: you have to copy the files from another project and carefully edit them.

In short, there must be a better way.

Some time ago I started looking for that way and I found a good solution: CMake. Maybe there are others, but I am very satisfied with this one.

The idea behind CMake is simple: use a single text file (CMakeLists.txt) in each folder to describe how the project must be built. Then let CMake create the build files for your preferred build tool.

You usually create a folder inside your project and instruct CMake to create the build files in that folder: all the build and compiled files will be in that folder, so your source code tree will be clean. You can even create different build folders for different build configurations.

There are plenty on information about CMake basic usage, so I will not write about it. I found little information about advanced usage, and I will post what I discovered with some work and experiments.

Using CMake is simple: just run the GUI program and it will show up its main window.
In the upper part you select your project's root folder (the upper one that contains a CMakeLists.txt file) and the build folder, where the build files will be created.

Below these paths there is grid that contains a list of couples: a variable and its value. You must check the values and set the missing or wrong ones. Here lies the magic: setting those variables tells CMake where to look for libraries or how to configure the build in your computer.

You must run the CMake GUI when you create the build folder and you will run it again when you change something, most often because you have added new source files.

In the lower part of the window there is a Configure button. When you press it CMake processes all the CMakeLists.txt files: if there are problems one or more rows in the grid will be colored in red. You need to fix the problem, for example editing the value of a variable, and press Configure again.
If everything is OK the Generate button is enabled: press it to create your build files, then move to the build folder and open the build files with your development tool.

Handling the project becomes a much easier task. For example, if you create some new source files just add the file names to the CMakeLists.txt file.
Then just run CMake in each operating system and configure again.

To create the build files CMake processes the CMakeLists.txt files found in each folder. Those files are written in the CMake language: the language is very powerful so the difficult part is learning it. I discovered that I used a relatively low number of features, but it took me a lot of work to learn how to use them correctly.