1
0
mirror of synced 2024-12-22 20:50:00 +01:00
modern-cmake/chapters/exporting.md

114 lines
5.6 KiB
Markdown
Raw Normal View History

2018-03-10 21:50:41 +01:00
# Exporting and Installing
There are three good ways and one bad way to allow others use your library:
## Find module (the bad way)
If you are the library author, don't make a `Find<mypackage>.cmake` script! These were designed for libraries whose authors did not support CMake. Use a `Config<mypackage>.cmake` instead as listed below.
## Add Subproject
A package can include your project in a subdirectory, and then use `add_directory` on the subdirectory. This useful for header-only and quick-to-compile libraries. Note that the install commands may interfere with the parent project, so you can add `EXCLUDE_FROM_ALL` to the `add_subdirectory` command; the targets you explicitly use will still be built.
In order to support this as a library author, make sure you use `CMAKE_CURRENT_SOURCE_DIR` instead of `PROJECT_SOURCE_DIR` (and likewise for other variables, like binary dirs). You can check `CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME` to only add options or defaults that make sense if this is a project.
Also, since namespaces are a good idea, and the usage of your library should be consistent with the other methods below, you should add
```cmake
add_library(MyLib::MyLib ALIAS MyLib)
```
to standardise the usage across all methods. This ALIAS target will not be exported below.
## Exporting
The second method is exporting targets and using them in the build directory. It usually is simply a by-product of setting up the third method (installing), but is useful for development and as a way to prepare to discuss the installation procedure.
First, you should make an export set, probably near the end of your main `CMakeLists.txt`:
```cmake
export(TARGETS MyLib1 MyLib2 NAMESPACE MyLib:: FILE MyLibTargets.cmake)
```
This puts the targets you've listed into a file in the build directory, and optionally prefixes them with a namespace. Now, to allow CMake to find this package, export the package into the `$HOME/.cmake/packages` folder:
```cmake
export(PACKAGE MyLib)
```
Now, if you `find_package(MyLib)`, CMake can find the build folder. Look at the generated MyLibTargets.cmake file to help you understand exactly what is created; it's just a normal CMake file, with the exported targets.
Note that there's a downside: if you have imported dependencies, they will need to be imported before you `find_package`. That will be fixed in the next method.
## Installing
Install commands cause a file or target to be "installed" into the install tree when you `make install`. Your basic target install command looks like this:
```cmake
install(TARGET MyLib
EXPORT MyLibTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
```
The various destinations are only needed if you have a library, static library, or program to install. The includes destination is special; since a target does not install includes. It only sets the includes destination on the exported target (which is often already set by `target_include_directories`, so check the MyLibTargets file and make sure you don't have the include directory included twice if you want clean cmake files).
It's usually a good idea to give CMake access to the version, so that `find_package` can have a version specified. That looks like this:
```cmake
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
MyLibConfigVersion.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY AnyNewerVersion
)
```
You have two choices next. You need to make a `MyLibConfig.cmake`, but you can do it either by exporting your targets directly to it, or by writing it by hand, then including the targets file. The later option is what you'll need if you have any dependencies, even just OpenMP, so I'll illustrate that method.
First, make an install targets file (very similar to the one you made in the build directory):
```cmake
install(EXPORT MyLibTargets
FILE MyLibTargets.cmake
NAMESPACE MyLib::
DESTINATION lib/cmake/MyLib
)
```
This file will take the targets you exported and put them in a file. If you have no dependencies, just use `MyLibConfig.cmake` instead of `MyLibTargets.cmake` here. Then write a custom `MyLibConfig.cmake` file in your source tree somewhere. If you want to capture configure time variables, you can use a `.in` file, and you will want to use the `@var@` syntax. The contents that look like this:
```cmake
include(CMakeFindDependencyMacro)
# Capturing values from configure (optional)
set(my-config-var @my-config-var@)
# Same syntax as find_package
find_dependency(MYDEP REQUIRED)
# Any extra setup
# Add the targets file
include("${CMAKE_CURRENT_LIST_DIR}/MyLibTargets.cmake")
```
Now, you can use configure file (if you used a `.in` file) and then install the resulting file.
Since we've made a `ConfigVersion` file, this is a good place to install it too.
```cmake
configure_file(MyLibConfig.cmake.in MyLibConfig.cmake @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/Minuit2ConfigVersion.cmake"
2018-03-10 22:05:33 +01:00
DESTINATION lib/cmake/MyLib
)
2018-03-10 21:50:41 +01:00
```
That's it! Now once you install a package, there will be files in `lib/cmake/MyLib` that CMake will search for (specifically, `MyLibConfig.cmake` and `MyLibConfigVersion.cmake`), and the targets file that config uses should be there as well.
When CMake searches for a package, it will look in the current install prefix and several standard places. You can also add this to your search path manually, including `MyLib_PATH`, and CMake gives the user nice help output if the configure file is not found.