diff --git a/docs/source/building.rst b/docs/source/building.rst index 904e5e48a7..5068196da2 100644 --- a/docs/source/building.rst +++ b/docs/source/building.rst @@ -558,7 +558,7 @@ Options controlling Ceres dependency locations ---------------------------------------------- Ceres uses the ``CMake`` -`find_package `_ +`find_package `_ function to find all of its dependencies using ``Find.cmake`` scripts which are either included in Ceres (for most dependencies) or are shipped as standard with ``CMake`` @@ -621,37 +621,199 @@ Using Ceres with CMake ====================== Once the library is installed with ``make install``, it is possible to -use CMake with `FIND_PACKAGE() -`_ -in order to compile **user code** against Ceres. For example, for +use CMake with `find_package() +`_ +in order to compile **user code** against Ceres. For example, to compile `examples/helloworld.cc `_ -the following CMakeList.txt can be used: +in a separate, standalone project, the following CMakeList.txt can be used: .. code-block:: cmake - CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + cmake_minimum_required(VERSION 2.8) - PROJECT(helloworld) + project(helloworld) - FIND_PACKAGE(Ceres REQUIRED) - INCLUDE_DIRECTORIES(${CERES_INCLUDE_DIRS}) + find_package(Ceres REQUIRED) + include_directories(${CERES_INCLUDE_DIRS}) # helloworld - ADD_EXECUTABLE(helloworld helloworld.cc) - TARGET_LINK_LIBRARIES(helloworld ${CERES_LIBRARIES}) + add_executable(helloworld helloworld.cc) + target_link_libraries(helloworld ${CERES_LIBRARIES}) + +Understanding the CMake Package System +---------------------------------------- + +Although a full tutorial on CMake is outside the scope of this guide, here +we cover some of the most common CMake misunderstandings that crop up +when using Ceres. For more detailed CMake usage, the following references are +very useful: + +- The `official CMake tutorial `_ + + Provides a tour of the core features of CMake. + +- `ProjectConfig tutorial `_ and the `cmake-packages documentation `_ + + Cover how to write a ``ProjectConfig.cmake`` file, discussed below, for + your own project when installing or exporting it using CMake. It also covers + how these processes in conjunction with ``find_package()`` are actually + handled by CMake. The + `ProjectConfig tutorial `_ + is the older style, currently used by Ceres for compatibility with older + versions of CMake. + + .. NOTE :: **Targets in CMake.** + + All libraries and executables built using CMake are represented as + *targets* created using + `add_library() + `_ + and + `add_executable() + `_. + Targets encapsulate the rules and dependencies (which can be other targets) + required to build or link against an object. This allows CMake to + implicitly manage dependency chains. Thus it is sufficient to tell CMake + that a library target: ``B`` depends on a previously declared library target + ``A``, and CMake will understand that this means that ``B`` also depends on + all of the public dependencies of ``A``. + +When a project like Ceres is installed using CMake, in addition to the +public headers and compiled libraries, a set of CMake-specific project +configuration files are also installed to: ``/share/Ceres``. +When `find_package +`_ +is invoked, CMake checks various standard install locations (including +``/usr/local`` on Linux & UNIX systems), for installed CMake configuration files +for the project to be found (i.e. Ceres in the case of ``find_package(Ceres)``). +Specifically it looks for: + +- ``Config.cmake`` (or ``-config.cmake``) + + Which is written by the developers of the project, and is configured with + the selected options and installed locations when the project is built and + defines the CMake variables: ``_INCLUDE_DIRS`` & + ``_LIBRARIES`` which are used by the caller to import + the project. + +The ``Config.cmake`` typically includes a second file installed to +the same location: + +- ``Targets.cmake`` + + Which is autogenerated by CMake as part of the install process and defines + **imported targets** for the project in the caller's CMake scope. + +An **imported target** contains the same information about a library as a CMake +target that was declared locally in the current CMake project using +``add_library()``. However, imported targets refer to objects that have already +been built previously by a different CMake project. Principally, an imported +target contains the location of the compiled object and all of its public +dependencies required to link against it. Any locally declared target can +depend on an imported target, and CMake will manage the dependency chain, just +as if the imported target had been declared locally by the current project. + +Crucially, just like any locally declared CMake target, an imported target is +identified by its **name** when adding it as a dependency to another target. + +Thus, if in a project using Ceres you had the following in your CMakeLists.txt: + +.. code-block:: cmake + + find_package(Ceres REQUIRED) + message("CERES_LIBRARIES = ${CERES_LIBRARIES}") + +You would see the output: ``CERES_LIBRARIES = ceres``. **However**, here +``ceres`` is an **imported target** created when ``CeresTargets.cmake`` was +read as part of ``find_package(Ceres REQUIRED)``. It does **not** refer +(directly) to the compiled Ceres library: ``libceres.a/so/dylib/lib``. This +distinction is important, as depending on the options selected when it was +built, Ceres can have public link dependencies which are encapsulated in the +imported target and automatically added to the link step when Ceres is added +as a dependency of another target by CMake. In this case, linking only against +``libceres.a/so/dylib/lib`` without these other public dependencies would +result in a linker error. + +Although this description covers projects that are **installed** using CMake, it +also holds for projects that are **exported** using CMake using +`export() `_ +instead of +`install() `_. +When a project is *installed*, the compiled libraries and headers are copied +from the source & build directory to the install location, and it is these +copied files that are used by any client code. When a project is *exported*, +instead of copying the compiled libraries and headers, CMake creates an entry +for the project in ``/.cmake/packages`` which contains the path to +the project's build directory which will be checked by CMake during a call to +``find_package()``. The effect of which is that any client code uses the +compiled libraries and headers in the build directory directly, thus not +requiring the project to be installed to be used. + +Installing / Exporting a project that uses Ceres +-------------------------------------------------- + +As described in `Understanding the CMake Package System`_, the contents of +the ``CERES_LIBRARIES`` variable is the **name** of an imported target which +represents Ceres. If you are installing / exporting your *own* project which +*uses* Ceres, it is important to understand that: + +**imported targets are not (re)exported when a project which imported them is +exported**. + +Thus, when a project ``Foo`` which uses Ceres is exported, its list of +dependencies as seen by another project ``Bar`` which imports ``Foo`` via: +``find_package(Foo REQUIRED)`` will contain: ``ceres``. However, the +definition of ``ceres`` as an imported target is **not (re)exported** when Foo +is exported. Hence, without any additional steps, when processing ``Bar``, +``ceres`` will not be defined as an imported target. Thus, when processing +``Bar``, CMake will assume that ``ceres`` refers only to: +``libceres.a/so/dylib/lib`` (the compiled Ceres library) directly if it is on +the current list of search paths. In which case, no CMake errors will occur, +but ``Bar`` will not link properly, as it does not have the required public link +dependencies of Ceres, which are stored in the imported target defintion. + +The solution to this is for ``Foo`` (i.e., the project that uses Ceres) to +invoke ``find_package(Ceres)`` in ``FooConfig.cmake``, thus ``ceres`` will be +defined as an imported target when CMake processes ``Bar``. An example of the +required modifications to ``FooConfig.cmake`` are show below: + +.. code-block:: cmake + + # When configure_file() is used to generate FooConfig.cmake from + # FooConfig.cmake.in, @Ceres_DIR@ will be replaced with the current + # value of Ceres_DIR being used by Foo. This should be passed as a hint + # when invoking find_package(Ceres) to ensure that the same install of + # Ceres is used as was used to build Foo. + set(CERES_DIR_HINTS @Ceres_DIR@) + + # Forward the QUIET / REQUIRED options. + if (Foo_FIND_QUIETLY) + find_package(Ceres QUIET HINTS ${CERES_DIR_HINTS}) + elseif (Foo_FIND_REQUIRED) + find_package(Ceres REQUIRED HINTS ${CERES_DIR_HINTS}) + else () + find_package(Ceres HINTS ${CERES_DIR_HINTS}) + endif() + + # Add Ceres to the list of dependencies for Foo, which will be used + # by the calling project when adding Foo as a dependency to a target. + if (CERES_FOUND) + list(APPEND FOO_INCLUDE_DIRS ${CERES_INCLUDE_DIRS}) + list(APPEND FOO_LIBRARIES ${CERES_INCLUDE_DIRS}) + endif() Specify Ceres version --------------------- Additionally, when CMake has found Ceres it can check the package -version, if it has been specified in the `FIND_PACKAGE() -`_ +version, if it has been specified in the `find_package() +`_ call. For example: .. code-block:: cmake - FIND_PACKAGE(Ceres 1.2.3 REQUIRED) + find_package(Ceres 1.2.3 REQUIRED) The version is an optional argument. @@ -659,12 +821,12 @@ Local installations ------------------- If Ceres was installed in a non-standard path by specifying --DCMAKE_INSTALL_PREFIX="/some/where/local", then the user should add -the **PATHS** option to the ``FIND_PACKAGE()`` command, e.g., +``-DCMAKE_INSTALL_PREFIX="/some/where/local"``, then the user should add +the **PATHS** option to the ``find_package()`` command, e.g., .. code-block:: cmake - FIND_PACKAGE(Ceres REQUIRED PATHS "/some/where/local/") + find_package(Ceres REQUIRED PATHS "/some/where/local/") Note that this can be used to have multiple versions of Ceres installed.