LCM

CMake Tutorial

Generating LCM type bindings with CMake

This tutorial will walk you through writing a `CMakeLists.txt` to generate bindings for your LCM message types. Please note that this is not meant to serve as a general CMake tutorial. This tutorial assumes that you are already familiar with CMake.

Note
  This tutorial assumes that you are using CMake 3.1 or later. Some of the features used, especially those related to the creation of a convenient `INTERFACE` library for C++ bindings, are not available in older versions of CMake. If you are using an older version of CMake, you may need to refer to the implementation details of LCM's helper functions, found in `lcmUtilities.cmake`, in order to manually accomplish the tasks that the helper functions would normally do. Note that all of the helper functions require CMake 3.1 or later on Windows, and that it is much more difficult to use `lcm-gen` on Windows from within CMake prior to CMake 3.1.

## Initial Setup

The very first thing you'll want to do is to find LCM and include its "use file". (A "use file" is a CMake script provided with some packages that adds utility functions for using that package. Many, including LCM's, provide additional documentation of their utility functions in the use file itself.) Depending on how you will be using LCM, as well as personal preference, this can be done in your project's root `CMakeLists.txt`. This is also a good time to look for additional language components you may want, such as Python and Java. (If you will not need bindings for these languages, you can omit those parts. Alternatively, you may want to make them `REQUIRED`.)

    find_package(lcm REQUIRED)
    include(${LCM_USE_FILE})

    find_package(PythonInterp)
    find_package(Java)

    if(JAVA_FOUND)
      include(UseJava)
    endif()

The rest of the CMake logic we will show will typically go in the `CMakeLists.txt` that is located with your LCM type files.

## A Simple Example

We'll start with a very simple example that generates a `STATIC` library of C language bindings:

    lcm_wrap_types(
      C_SOURCES c_sources
      C_HEADERS c_headers
      my_type_1.lcm
      my_type_2.lcm
      ...
    )

    lcm_add_library(my_lcmtypes C STATIC ${c_sources} ${c_headers})
    target_include_directories(my_lcmtypes INTERFACE
      $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)

The first function creates rules to invoke `lcm-gen` to generate the binding source files. The names `c_sources` and `c_headers` are the names of variables that will receive these lists of files. You can see where we use them again to create the C library. We chose these names as they are both simple and clear, but you can use whatever names you like.

The second function creates a C library, which is `STATIC`, from the specified sources. Including the header files is optional, but may be beneficial to some IDE's. This has a couple advantages over plain old `add_library`. First, it will link the library to LCM for you. Second, and more important, it will set up an additional target to ensure that all of the named bindings have been generated before the sources are compiled. This is important for some generators when one LCM type references another; otherwise, the build tool might try to compile the source file for one type before the header for the referenced type has been generated, resulting in a spurious build error.

Before we call it a day, it would be nice if consumers could find the library's headers. We do that by adding an interface include directory, like so:

    target_link_libraries(my_lcmtypes INTERFACE
      $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)

Note the use of `$<BUILD_INTERFACE>` to tell CMake that the include directory should not be included in the installed version of the library. This prevents details of your build from leaking into the install. We'll handle the include directory for the installed library differently. (If there are no local consumers of your library, you can skip this step.)

The include directory should be the directory where the bindings are generated. Since we didn't pass a `DESTINATION` to `lcm_wrap_types`, it defaulted to the current subdirectory of the build tree (`CMAKE_CURRENT_BINARY_DIR`).

## Shared C Libraries

Static libraries are ugly. They inflate the size of every binary that consumes them, and they can't be replaced with updated versions without recompiling every consumer.

Of course, as you probably know, creating a shared library on Windows, or if using ELF hidden symbol visibility (which you really, really should be doing), requires a) marking those parts of your API that should be exported, and b) specifying how to mark an exported function. You've almost certainly seen headers peppered with preprocessor symbols like `LCM_EXPORT`, and you probably know that the correct definition of such symbols depends on both the platform and whether you are building or consuming the library.

Fortunately, `lcm-gen` can take care of the first item for you with only a very little hand-holding, and as you probably already know, CMake has an excellent utility, `generate_export_header`, for the second. Let's update our example to use them:

    lcm_wrap_types(
      C_EXPORT my_lcmtypes
      C_SOURCES c_sources
      C_HEADERS c_headers
      my_type_1.lcm
      my_type_2.lcm
      ...
    )

    lcm_add_library(my_lcmtypes C ${c_sources} ${c_headers})
    generate_export_header(my_lcmtypes)
    target_include_directories(my_lcmtypes INTERFACE
      $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)

Note the addition of `C_EXPORT` to our invocation of `lcm_wrap_types`. This is designed to work with `generate_export_header`, and will specify both the symbol name and export header name as created by `generate_export_header`. In this instance, we are using the library name, but you can use whatever you like by passing the same name to `C_EXPORT` (`lcm_wrap_types`) and `BASE_NAME` (`generate_export_header`).

Note also that we didn't replace `STATIC` with `SHARED`, but rather just removed it. Just like a regular call to `add_library`, this will generate a library that is either static or shared depending on if `BUILD_SHARED_LIBS` is enabled. This approach is often preferred as it allows the user to decide if they want static libraries or shared libraries, but you can also use `SHARED` to enforce building of a shared library.

## C++

We probably want to generate at least C++ bindings also. Let's do that now:

    lcm_wrap_types(
      C_EXPORT my_lcmtypes
      C_SOURCES c_sources
      C_HEADERS c_headers
      CPP_HEADERS cpp_headers
      my_type_1.lcm
      my_type_2.lcm
      ...
    )

    # ...logic for C library...

    lcm_add_library(my_lcmtypes-cpp CPP ${cpp_headers})
    target_include_directories(my_lcmtypes-cpp INTERFACE
      $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)

None of this should look surprising after seeing how the C library was created. The additional argument to `lcm_wrap_types`, `CPP_HEADERS`, serves both to tell `lcm_wrap_types` to generate C++ bindings, and what variable should receive the list of C++ headers. `C_SOURCES` and `C_HEADERS` work very much the same way; by omitting both, we can skip generation of C bindings. (Unlike `CPP_HEADERS`, `C_SOURCES` and `C_HEADERS` are coupled; it is an error to specify one without the other.)

"But wait," you may be thinking to yourself, "aren't LCM C++ bindings header only?" Indeed they are, which is why this "library" is really an `INTERFACE` library. An `INTERFACE` library in CMake is just a fancy way of hanging usage requirements (like the `target_include_directories`, above) off of a target so that consumers can consume a logical target with the same convenience that they can consume targets that have actually binary objects backing them.

There is one critical caveat to this, however. Just like our C library needed to ensure that the bindings are generated before the sources are built, consumers of the C++ bindings need to ensure that the bindings are actually built before the consumer.

CMake 3.3 added the ability to hang dependencies off of `INTERFACE` libraries, and `lcm_add_library` will do this for you for the C++ library just as for the C library. Earlier versions of CMake lack this wonderfully convenient feature, but fortunately, LCM can help us out. If your project might be build with CMake earlier than 3.3, using `lcm_target_link_libraries` when linking a consumer to a C++ bindings library such as shown above will automagically set up a dependency to ensure that the bindings are generated before trying to build the consumer. Just use `lcm_target_link_libraries` with the same arguments as you would use `target_link_libraries`. (If your project enforces use of CMake 3.3 or later, or if the bindings you are consuming are build by an external project, just use `target_link_libraries`.)

## Python and Java

C and C++ are great, but you may well have users that want to consume your LCM types with Python or Java. Just as when we added C++, we'll start by adding some additional arguments to `lcm_wrap_types`. Depending on whether you generate these bindings always, or opportunistically when Python and/or Java are available, you may want to make this logic conditional, as in the approach shown here:

    if(PYTHONINTERP_FOUND)
      set(python_args PYTHON_SOURCES python_sources)
    endif()
    if(JAVA_FOUND)
      set(java_args JAVA_SOURCES java_sources)
    endif()

    lcm_wrap_types(
      C_EXPORT my_lcmtypes
      C_SOURCES c_sources
      C_HEADERS c_headers
      CPP_HEADERS cpp_headers
      ${python_args}
      ${java_args}
      my_type_1.lcm
      my_type_2.lcm
      ...
    )

If you require Python and/or Java, you can of course simply inline the corresponding arguments as is done for C and C++. Note that the main reason to require that Python is found is so that we can match its version in order to install things to the right place. If you have some other means of determining the correct install location, you could skip requiring that Python is found.

Python doesn't need to "build" anything (and we'll come back to installation in a moment), so for now, the only other change is to build the JAR:

    if(JAVA_FOUND)
      add_jar(my_lcmtypes-jar
        OUTPUT_NAME my_lcmtypes
        INCLUDE_JARS lcm-java
        SOURCES ${java_sources}
      )
    endif()

As before, if you require Java, you can omit the `JAVA_FOUND` check.

## Installing Everything

First, let's revisit our variable names:

    if(PYTHONINTERP_FOUND)
      set(python_args PYTHON_SOURCES python_install_sources)
    endif()
    if(JAVA_FOUND)
      set(java_args JAVA_SOURCES java_sources)
    endif()

    lcm_wrap_types(
      C_EXPORT my_lcmtypes
      C_SOURCES c_sources
      C_HEADERS c_install_headers
      CPP_HEADERS cpp_install_headers
      ${python_args}
      ${java_args}
      my_type_1.lcm
      my_type_2.lcm
      ...
    )

Note that we've added `_install_` to a few of the variable names. While this is in no way necessary (recall that the variable names can be whatever you like), it serves to clearly indicate which variables hold the names of files that need to be installed, and which are only files to be compiled.

We'll skip repeating the rest of the existing logic, but don't forget to change the names of these variables in the other locations they appear.

Now, we'll install everything:

    lcm_install_headers(DESTINATION include
      ${CMAKE_CURRENT_BINARY_DIR}/my_lcmtypes_export.h
      ${c_install_headers}
      ${cpp_install_headers}
    )

    if(PYTHONINTERP_FOUND)
      lcm_install_python(${python_install_sources})
    endif()

    install(TARGETS my_lcmtypes my_lcmtypes-cpp
      EXPORT ${PROJECT_NAME}Targets
      RUNTIME DESTINATION bin
      LIBRARY DESTINATION lib${LIB_SUFFIX}
      ARCHIVE DESTINATION lib${LIB_SUFFIX}
      INCLUDES DESTINATION include
    )

    if(JAVA_FOUND)
      install_jar(my_lcmtypes-jar share/java)
    endif()

The two LCM-provided helper functions we used here, `lcm_install_headers` and `lcm_install_python`, are convenience functions that will preserve subdirectory components (particularly, the package name subdirectory of the C++ headers) of the files being installed. Additionally, `lcm_install_python` chooses the correct destination directory by default. We also use `INCLUDES DESTINATION` when installing the libraries to set the interface include directories on the libraries. This directory should match the `DESTINATION` to which the headers are installed.

Note that we specified an `EXPORT` for the C/C++ libraries, but do not show installing the target exports file or creating build-tree exports. This is because these procedures a) are not specific to LCM, and b) must be done in a single location for the entire project. For similar reasons, we also did not show exporting the JAR file. (LCM itself may be used as an example of these tasks.)

Note that exporting JAR files requires CMake 3.7 or later, or copying `UseJava.cmake` into your project. See LCM itself for an example of the latter. (Note that consumers of exported JAR's don't require CMake 3.7; the created export files are perfectly usable with much older versions of CMake.)

## Other Useful Tidbits

We did not cover every possible argument to `lcm_wrap_types`. Most of the options accepted by `lcm-gen` are available through `lcm_wrap_types`. See `lcmUtilities.cmake` and `lcm-gen --help` for more information on what options are available.