Difference between revisions of "Advanced CMake Features"

From wiki.emacinc.com
Jump to: navigation, search
m (Reviewed and put in some notes.)
 
(7 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{todo| Buggy (09.14.2015-11:59->KY+)(09.21.2015-11:08->KY+)(09.24.2015-18:15->MD-)|Klint Youngmeyer| project=OE 5.0,KY,MD }}
+
{{todo| Complete (09.14.2015-11:59->KY+)(09.21.2015-11:08->KY+)(09.24.2015-18:15->MD-);(11.16.2015-12:10->MD-);(11.16.2015-13:22->KY+);(11.17.2015-14:55->MD+);(11.17.2015-15:30->MG+)|Klint Youngmeyer| project=OE 5.0,KY,MD,MG,Complete }}
 
{{#seo:
 
{{#seo:
 
|title=Advanced CMake Features
 
|title=Advanced CMake Features
Line 18: Line 18:
 
<!-- /*********************************************************************************************************/ -->
 
<!-- /*********************************************************************************************************/ -->
 
{{:Templateimpl:bg | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
 
{{:Templateimpl:bg | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
This page is written with the assumption that the project being worked on has been created using the [[Creating_a_New_EMAC_OE_SDK_Project_with_CMake | oe_init_project]] script or using the EMAC New C/C++ Project in Qt Creator.
+
This page is written with the assumption that the project being worked on has been created using the [[Creating_a_New_EMAC_OE_SDK_Project_with_CMake | oe_init_project]] script or using the EMAC New C/C++ Project in Qt Creator. This page will explain the following CMake tasks:
{{warning | There should be some overview of the tasks which will be discussed here, so they know what to expect from the document.  A simple bulletted list will work nicely.}}
+
*Adding a version number
 
+
*Adding external library files
 +
*Adding C/C++ sources as libraries
 +
*Cross platform building
 
<!-- /*********************************************************************************************************/ -->
 
<!-- /*********************************************************************************************************/ -->
 
<!-- /*****************************************  General Information  *****************************************/ -->
 
<!-- /*****************************************  General Information  *****************************************/ -->
Line 31: Line 33:
 
{{:Templateimpl:using | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
 
{{:Templateimpl:using | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
 
===Adding a Version Number and Configured Header File===
 
===Adding a Version Number and Configured Header File===
{{warning| Why would someone want to add the version number here?  Some "great idea" examples will go a long way to bringing up the interest level of the reader.}}
+
It is generally a good practice to put a version number in a project, but maintaining several variables containing the version number can be time consuming. It is possible to provide the executable and project with a version number through CMake. While you can do this exclusively in the source code, doing it in the CMakeLists file provides more flexibility. To add a version number we modify the CMakeLists file as follows:
 
 
It is possible to provide the executable and project with a version number through CMake. While you can do this exclusively in the source code, doing it in the CMakeLists file provides more flexibility. To add a version number we modify the CMakeLists file as follows:
 
 
<syntaxhighlight lang="cmake">
 
<syntaxhighlight lang="cmake">
 
# The version number.
 
# The version number.
Line 59: Line 59:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
When CMake configures this header file the values for <code>@Example_VERSION_MAJOR@</code> and <code>@Example_VERSION_MINOR@</code> will be replaced by the values from the CMakeLists file. Next we modify main.c to include the configured header file and to make use of the version numbers. The resulting source code is listed below.
 
When CMake configures this header file the values for <code>@Example_VERSION_MAJOR@</code> and <code>@Example_VERSION_MINOR@</code> will be replaced by the values from the CMakeLists file. Next we modify main.c to include the configured header file and to make use of the version numbers. The resulting source code is listed below.
{{warning | Why is the formatting of this code funky?  It should be in a standard formatting.  Right now, it's a mix of 3 different styles (one of which is almost never used by anyone: the half-indented curly brackets).}}
 
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
// A simple program that computes the square root of a number
 
// A simple program that computes the square root of a number
Line 67: Line 66:
 
#include "ExampleConfig.h"
 
#include "ExampleConfig.h"
 
   
 
   
int main (int argc, char *argv[])
+
int main (int argc, char *argv[]) {
{
+
   
  if (argc < 2)
+
    if (argc < 2) {
    {    fprintf(stdout,"%s Version %d.%d\n",
+
        fprintf(stdout,"%s Version %d.%d\n", argv[0], Example_VERSION_MAJOR, Example_VERSION_MINOR);
            argv[0],
+
        fprintf(stdout,"Usage: %s number\n",argv[0]);
            Example_VERSION_MAJOR,
+
        return 1;
            Example_VERSION_MINOR);
 
    fprintf(stdout,"Usage: %s number\n",argv[0]);
 
    return 1;
 
 
     }
 
     }
  double inputValue = atof(argv[1]);
+
 
  double outputValue = sqrt(inputValue);
+
    double inputValue = atof(argv[1]);
  fprintf(stdout,"The square root of %g is %g\n",
+
    double outputValue = sqrt(inputValue);
          inputValue, outputValue);
+
    fprintf(stdout,"The square root of %g is %g\n",inputValue, outputValue);
  return 0;}
+
 
 +
    return 0;
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 
The main points of note are the inclusion of the <code>ExampleConfig.h</code> header file and printing out a version number as part of the usage message.
 
The main points of note are the inclusion of the <code>ExampleConfig.h</code> header file and printing out a version number as part of the usage message.
 
===Adding External Library Files===
 
===Adding External Library Files===
{warning | Instead of just diving right in, a little background would be good. "In most projects, 3rd party libraries are used. The linker needs to know how to find these libraries and link to them...  etc."}
+
A library is a collection of precompiled object files which can be linked into programs. The most common use of libraries is to provide system functions, such as the image resize function, <code>MagickResizeImage</code>, found in the ImageMagick library. The linker needs to know where to find these objects; CMake can accomplish this with the <code>INCLUDE_DIRECTORIES</code> and <code>TARGET_LINK_LIBRARIES</code> functions.
 
 
 
To add an external library to the CMake project, more changes will need to be made to the <code>CMakeLists.txt</code> file. Please refer to the code sample below. In this example, the ''ImageMagick'' library will be linked into the project.
 
To add an external library to the CMake project, more changes will need to be made to the <code>CMakeLists.txt</code> file. Please refer to the code sample below. In this example, the ''ImageMagick'' library will be linked into the project.
 
#The library directory must be included using the <code>INCLUDE_DIRECTORIES()</code> function. This function must be before the <code>ADD_EXECUTABLE()</code> function.
 
#The library directory must be included using the <code>INCLUDE_DIRECTORIES()</code> function. This function must be before the <code>ADD_EXECUTABLE()</code> function.
Line 97: Line 94:
  
 
TARGET_LINK_LIBRARIES(example MagickWand)
 
TARGET_LINK_LIBRARIES(example MagickWand)
 +
</syntaxhighlight>
 +
====Boost Library Support====
 +
To use the Boost libraries with CMake and EMAC OE, change the <code>CMakeLists.txt</code> file to reflect the following example code:
 +
<syntaxhighlight lang="cmake">
 +
### Boost Library Support
 +
SET(Boost_USE_STATIC_LIBS OFF)
 +
SET(Boost_USE_MULTITHREADED ON)
 +
SET(Boost_USE_STATIC_RUNTIME OFF)
 +
FIND_PACKAGE(Boost 1.40 COMPONENTS iostreams REQUIRED)
 +
 +
 +
# Here is how to include some other popular Boost packages
 +
#FIND_PACKAGE(Boost 1.40 COMPONENTS regex iostreams filesystem system REQUIRED)
 +
 +
 +
if(Boost_FOUND)
 +
    MESSAGE(STATUS "Boost libs found")
 +
    INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
 +
    # AUX_SOURCE_DIRECTORY(. SRC_LIST)# (OPTIONAL) This will grab every source file name and put it in the variable SRC_LIST.
 +
    # To use this option, change the ${SRC_FILE} variable in add_executable to ${SRC_LIST}
 +
    ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILE})
 +
    TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${Boost_LIBRARIES})
 +
else()
 +
    MESSAGE(FATAL_ERROR "ERROR: Boost libraries not found.")
 +
endif()
 +
###
 +
INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin)
 +
# This allows the use of 'make install'. By default, the binary will be installed
 +
# into the bin directory inside the project directory. The destination can be changed
 +
# to the directory of your choice (using absolute path).
 +
# Libraries and headers can be installed in similar ways:
 +
# INSTALL(TARGETS test DESTINATION lib)
 +
# INSTALL(FILES test.h DESTINATION include)
 +
</syntaxhighlight>
 +
An example "Hello World" program using the Boost libraries is shown in the following example code:
 +
<syntaxhighlight lang="cpp">
 +
#include <cstdlib>
 +
#include <fstream>
 +
#include <iostream>
 +
 +
using std::ostream;
 +
using std::ofstream;
 +
using std::cout;
 +
 +
#include <boost/iostreams/tee.hpp>
 +
#include <boost/iostreams/stream.hpp>
 +
 +
namespace bio = boost::iostreams;
 +
using bio::tee_device;
 +
using bio::stream;
 +
 +
int main(int argc, char* argv[])
 +
{
 +
    typedef tee_device<ostream, ofstream> TeeDevice;
 +
    typedef stream<TeeDevice> TeeStream;
 +
   
 +
    ofstream ofs("sample.txt");
 +
   
 +
    // Define an ostream tee which sends output to both cout and ofs
 +
    TeeDevice my_tee(cout, ofs);
 +
 +
    // Assign the tee to an ostream object.
 +
    TeeStream my_split(my_tee);
 +
 +
    my_split << "Hello, World!\\n";
 +
 +
    my_split.flush();
 +
    my_split.close();
 +
 +
    exit(EXIT_SUCCESS);
 +
}               
 
</syntaxhighlight>
 
</syntaxhighlight>
 
===Adding Additional C/C++ Source Files as Libraries===
 
===Adding Additional C/C++ Source Files as Libraries===
Line 110: Line 178:
 
     ${PROJECT_SOURCE_DIR}/include/tools.cpp
 
     ${PROJECT_SOURCE_DIR}/include/tools.cpp
 
     ${PROJECT_SOURCE_DIR}/include/funcs.cpp  
 
     ${PROJECT_SOURCE_DIR}/include/funcs.cpp  
    ${PROJECT_SOURCE_DIR}/include/tools.h
 
    ${PROJECT_SOURCE_DIR}/include/funcs.h
 
 
     )
 
     )
  
Line 125: Line 191:
 
TARGET_LINK_LIBRARIES(main tools funcs)
 
TARGET_LINK_LIBRARIES(main tools funcs)
 
</syntaxhighlight>
 
</syntaxhighlight>
 
{warning | Adding Boost Libraries can be tricky.  We should have a section on this as well, since we have it figured out.}
 
  
 
===Cross-Platform Building===
 
===Cross-Platform Building===
If a project is meant to be used on multiple different platforms, it is sometimes necessary to have different options for each platform. These options could be additional C flags, variables to #define what code is to be run, or various other tasks. The following example shows how to #define which architecture is being built based on the <code>ARCH</code> variable that is set by the EMAC toolchain:
+
If a project is meant to be used on multiple different platforms, it is sometimes necessary to have different options for each platform. These options could be additional C flags, variables to <code>#define</code> what code is to be run, or various other tasks. The following example shows how to <code>#define</code> which architecture is being built based on the <code>ARCH</code> variable that is set by the EMAC toolchain:
 
<syntaxhighlight lang="cmake">
 
<syntaxhighlight lang="cmake">
 
if(${ARCH} STREQUAL "x86")
 
if(${ARCH} STREQUAL "x86")
Line 163: Line 227:
 
<!-- /*********************************************************************************************************/ -->
 
<!-- /*********************************************************************************************************/ -->
 
{{:Templateimpl:conclusion | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
 
{{:Templateimpl:conclusion | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
As stated previously, this guide is just a small list of common task requests. Please refer to official or other third-party documentation for more information.
+
In this guide, several relatively common, advanced CMake topics were covered. It was demonstrated how to: add different types of library files, add a version number in a config file, and build with cross-platform support.
 +
 
 +
With the information provided on this page, you are well on your way to having the tools needed for building a large project with CMake. While EMAC fully supports CMake, we are not able to provide the full documentation. Please refer to official or other third-party documentation for more information.
 +
 
 
<!-- /*********************************************************************************************************/ -->
 
<!-- /*********************************************************************************************************/ -->
 
<!-- /******************************************  More Information  *****************************************/ -->
 
<!-- /******************************************  More Information  *****************************************/ -->
 
<!-- /*********************************************************************************************************/ -->
 
<!-- /*********************************************************************************************************/ -->
 
{{:Templateimpl:moreinfo | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
 
{{:Templateimpl:moreinfo | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
*  
+
* [https://cmake.org/cmake/help/v3.4/ CMake Official Documentation]
  
 
{{:Templateimpl:whatnext | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
 
{{:Templateimpl:whatnext | initials=KY | title=Advanced CMake Features | desc=Advanced features of the CMake build system for use with the EMAC SDK. | project=OE 5.0 }}
 
*[[Creating_a_New_EMAC_OE_SDK_Project_with_CMake | Creating a New EMAC OE SDK Project with CMake]]
 
*[[Creating_a_New_EMAC_OE_SDK_Project_with_CMake | Creating a New EMAC OE SDK Project with CMake]]
 
<br />
 
<br />
Some information on this page was provided by the official [http://www.cmake.org/cmake-tutorial/ CMake Documentation].
+
''Some information on this page was provided by the official [http://www.cmake.org/cmake-tutorial/ CMake Tutorial].''

Latest revision as of 16:27, 17 November 2015

TODO: {{#todo: Complete (09.14.2015-11:59->KY+)(09.21.2015-11:08->KY+)(09.24.2015-18:15->MD-);(11.16.2015-12:10->MD-);(11.16.2015-13:22->KY+);(11.17.2015-14:55->MD+);(11.17.2015-15:30->MG+)|Klint Youngmeyer|OE 5.0,KY,MD,MG,Complete}}

There are several advanced features of the CMake build system that may be of use on projects as they get larger. This is far from a comprehensive list, and information related to unlisted tasks may be found on official CMake documentation.

Background

This page is written with the assumption that the project being worked on has been created using the oe_init_project script or using the EMAC New C/C++ Project in Qt Creator. This page will explain the following CMake tasks:

  • Adding a version number
  • Adding external library files
  • Adding C/C++ sources as libraries
  • Cross platform building

Advanced CMake Features

Adding a Version Number and Configured Header File

It is generally a good practice to put a version number in a project, but maintaining several variables containing the version number can be time consuming. It is possible to provide the executable and project with a version number through CMake. While you can do this exclusively in the source code, doing it in the CMakeLists file provides more flexibility. To add a version number we modify the CMakeLists file as follows:

# The version number.
set (Example_VERSION_MAJOR 1)
set (Example_VERSION_MINOR 0) 

# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/ExampleConfig.h.in"
  "${PROJECT_BINARY_DIR}/ExampleConfig.h"  ) 


# add the binary tree to the search path for include files
# so that we will find ExampleConfig.h
include_directories("${PROJECT_BINARY_DIR}") 
# add the executable
add_executable(example main.c)

Since the configured file will be written into the binary tree we must add that directory to the list of paths to search for include files. We then create an ExampleConfig.h.in file in the source tree with the following contents:

// the configured options and settings for Tutorial
#define Example_VERSION_MAJOR @Example_VERSION_MAJOR@
#define Example_VERSION_MINOR @Example_VERSION_MINOR@

When CMake configures this header file the values for @Example_VERSION_MAJOR@ and @Example_VERSION_MINOR@ will be replaced by the values from the CMakeLists file. Next we modify main.c to include the configured header file and to make use of the version numbers. The resulting source code is listed below.

// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ExampleConfig.h"
 
int main (int argc, char *argv[]) {
    
    if (argc < 2) {
        fprintf(stdout,"%s Version %d.%d\n", argv[0], Example_VERSION_MAJOR, Example_VERSION_MINOR);
        fprintf(stdout,"Usage: %s number\n",argv[0]);
        return 1;
    }

    double inputValue = atof(argv[1]);
    double outputValue = sqrt(inputValue);
    fprintf(stdout,"The square root of %g is %g\n",inputValue, outputValue);

    return 0;
}

The main points of note are the inclusion of the ExampleConfig.h header file and printing out a version number as part of the usage message.

Adding External Library Files

A library is a collection of precompiled object files which can be linked into programs. The most common use of libraries is to provide system functions, such as the image resize function, MagickResizeImage, found in the ImageMagick library. The linker needs to know where to find these objects; CMake can accomplish this with the INCLUDE_DIRECTORIES and TARGET_LINK_LIBRARIES functions. To add an external library to the CMake project, more changes will need to be made to the CMakeLists.txt file. Please refer to the code sample below. In this example, the ImageMagick library will be linked into the project.

  1. The library directory must be included using the INCLUDE_DIRECTORIES() function. This function must be before the ADD_EXECUTABLE() function.
  2. Use the TARGET_LINK_LIBRARIES() function to link the desired library to the target binary. This function must be after the ADD_EXECUTABLE() function.
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} "/usr/include/ImageMagick")

ADD_EXECUTABLE(example main.cpp) # This should be here by default

TARGET_LINK_LIBRARIES(example MagickWand)

Boost Library Support

To use the Boost libraries with CMake and EMAC OE, change the CMakeLists.txt file to reflect the following example code:

### Boost Library Support
SET(Boost_USE_STATIC_LIBS OFF)
SET(Boost_USE_MULTITHREADED ON)
SET(Boost_USE_STATIC_RUNTIME OFF)
FIND_PACKAGE(Boost 1.40 COMPONENTS iostreams REQUIRED)


# Here is how to include some other popular Boost packages
#FIND_PACKAGE(Boost 1.40 COMPONENTS regex iostreams filesystem system REQUIRED)


if(Boost_FOUND)
    MESSAGE(STATUS "Boost libs found")
    INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
    # AUX_SOURCE_DIRECTORY(. SRC_LIST)# (OPTIONAL) This will grab every source file name and put it in the variable SRC_LIST.
    # To use this option, change the ${SRC_FILE} variable in add_executable to ${SRC_LIST}
    ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILE})
    TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${Boost_LIBRARIES})
else()
    MESSAGE(FATAL_ERROR "ERROR: Boost libraries not found.")
endif()
###
INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# This allows the use of 'make install'. By default, the binary will be installed
# into the bin directory inside the project directory. The destination can be changed
# to the directory of your choice (using absolute path).
# Libraries and headers can be installed in similar ways:
# INSTALL(TARGETS test DESTINATION lib)
# INSTALL(FILES test.h DESTINATION include)

An example "Hello World" program using the Boost libraries is shown in the following example code:

#include <cstdlib>
#include <fstream>
#include <iostream>

using std::ostream;
using std::ofstream;
using std::cout;

#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/stream.hpp>

namespace bio = boost::iostreams;
using bio::tee_device;
using bio::stream;

int main(int argc, char* argv[])
{
    typedef tee_device<ostream, ofstream> TeeDevice;
    typedef stream<TeeDevice> TeeStream;
    
    ofstream ofs("sample.txt");
    
    // Define an ostream tee which sends output to both cout and ofs
    TeeDevice my_tee(cout, ofs);

    // Assign the tee to an ostream object.
    TeeStream my_split(my_tee);

    my_split << "Hello, World!\\n";

    my_split.flush();
    my_split.close();

    exit(EXIT_SUCCESS);
}

Adding Additional C/C++ Source Files as Libraries

Adding additional source files is accomplished similarly to adding external library files. See above.

  1. All included files must be included in the SOURCES list.
  2. All header files must be included in the HEADER_FILES list.
  3. Each C/C++ source file must be added as a library before adding the executable.
  4. Add the executable, including the ${HEADER_FILES}.
  5. Link the target libraries to the executable.
SET(SOURCES 
    ${PROJECT_SOURCE_DIR}/include/tools.cpp
    ${PROJECT_SOURCE_DIR}/include/funcs.cpp 
    )

SET(HEADER_FILES
    ${PROJECT_SOURCE_DIR}/include/tools.h
    ${PROJECT_SOURCE_DIR}/include/funcs.h
    )

ADD_LIBRARY(tools include/tools.cpp ${HEADER_FILES})
ADD_LIBRARY(funcs include/funcs.cpp ${HEADER_FILES})
ADD_EXECUTABLE(example main.cpp ${HEADER_FILES})

TARGET_LINK_LIBRARIES(main tools funcs)

Cross-Platform Building

If a project is meant to be used on multiple different platforms, it is sometimes necessary to have different options for each platform. These options could be additional C flags, variables to #define what code is to be run, or various other tasks. The following example shows how to #define which architecture is being built based on the ARCH variable that is set by the EMAC toolchain:

if(${ARCH} STREQUAL "x86")
    ADD_DEFINITIONS(-DARCH_X86) # The define name will be ARCH_X86
elseif(${ARCH} STREQUAL "arm")
    ADD_DEFINITIONS(-DARCH_ARM) # The define name will be ARCH_ARM
elseif(${ARCH} STREQUAL "def")  
    ADD_DEFINITIONS(-DARCH_DESKTOP) # The define name will be ARCH_DESKTOP
else()
    MESSAGE(FATAL_ERROR "ERROR: Not a valid cross-platform option.")
endif()

In the project source code, it is now possible to make some code architecture dependent, as shown in the following example code:

#ifdef ARCH_X86
    //Run x86 specific code
#elif defined ARCH_ARM
    //Run arm specific code
#elif defined ARCH_DESKTOP
    //Run desktop specific code
#else
#error "Unknown platform"
#endif

Conclusion

In this guide, several relatively common, advanced CMake topics were covered. It was demonstrated how to: add different types of library files, add a version number in a config file, and build with cross-platform support.

With the information provided on this page, you are well on your way to having the tools needed for building a large project with CMake. While EMAC fully supports CMake, we are not able to provide the full documentation. Please refer to official or other third-party documentation for more information.

Further Information

Where to Go Next
Pages with Related Content


Some information on this page was provided by the official CMake Tutorial.