I.Introduction
Once you have compiled a library for development, the can use it directly from the build tree for any other project that depends on it. It is usually not a good idea if you have several members in your dev team, if you use different systems, and/or if you want to distribute your work. That’s where the notion of install and versioning makes sense (1). Packaging (2) then allow you to install on a computer different than the one you built the project on. After you have installed a library (and corresponding headers and other needed files) it would be also nice to be able to import (3) it in a project easily.
The good news is that CMake handles all of that with (again!) a very few lines of code. Using the install() command you can define what to install, where to install it, and to some extend couple files per components for interactive installers. You might also remember that I told you in a previous post that CMake was kind of a trilogy (CMake/CTest/CDash). Well, there is a sequel called CPack. It’s not as good as the first ones (sequels rarely are), but it gets its own cmake variable prefix so I guess it’s cool 🙂 CPack handles the packaging part, which is build on top of the installation part. Now let’s dig in.
II.Installing targets, or files locally.
In our use of CMake, we do not have targets for each library or executable. Moreover, the tests are not relocatable easily, so it’s better not to try. Libraries can be installed in a flat directory structure, but headers need to follow a certain directory structure to be usable, so we will have to follow two different strategies there.Finally, the install() command has a lot of signature, so let’s focus on those which would be of use for us.
1. Versioning
The versioning follows the CMake convention (to be compatible with the other tools and command) as explained here.
- #———————————————————————————————–
- # Versioning
- set( WEBRTC_MAJOR_VERSION 0 ) # not fully tested yet
- set( WEBRTC_MINOR_VERSION 1 ) # really not fully tested, not fully implemented
- set( WEBRTC_BUILD_VERSION 1 ) # should be the SVN rev, but it s hard to get it automatically from the git commit msg.
- set( WEBRTC_VERSION
- ${WEBRTC_MAJOR_VERSION}.${WEBRTC_MINOR_VERSION}.${WEBRTC_BUILD_VERSION}
- )
- set( WEBRTC_API_VERSION
- # This is the ITK/VTK style where SOVERSION is two numbers…
- “${WEBRTC_MAJOR_VERSION}.${WEBRTC_MINOR_VERSION}”
- )
- set( WEBRTC_LIBRARY_PROPERTIES ${WEBRTC_LIBRARY_PROPERTIES}
- VERSION “${WEBRTC_VERSION}”
- SOVERSION “${WEBRTC_API_VERSION}”
- )
further reading:
- the original CMake Versioning file for GIT (super advanced)
2. set up destination folders per component types
Here again, nothing fancy, just following the CMake convention so that find_package can be used later (see find_package() documentation about the expected paths ).
- # ————————————————————————–
- # Configure the export configuration
- # WEBRTC_INSTALL_BIN_DIR – binary dir (executables)
- # WEBRTC_INSTALL_LIB_DIR – library dir (libs)
- # WEBRTC_INSTALL_DATA_DIR – share dir (say, examples, data, etc)
- # WEBRTC_INSTALL_INCLUDE_DIR – include dir (headers)
- # WEBRTC_INSTALL_CMAKE_DIR – cmake files (cmake)
- if( NOT WEBRTC_INSTALL_BIN_DIR )
- set( WEBRTC_INSTALL_BIN_DIR “bin” )
- endif()
- if( NOT WEBRTC_INSTALL_LIB_DIR )
- set( WEBRTC_INSTALL_LIB_DIR “lib” )
- endif()
- if( NOT WEBRTC_INSTALL_DATA_DIR )
- set( WEBRTC_INSTALL_DATA_DIR “share” )
- endif()
- if( NOT WEBRTC_INSTALL_INCLUDE_DIR )
- set( WEBRTC_INSTALL_INCLUDE_DIR “include” )
- endif( )
- if( NOT WEBRTC_INSTALL_CMAKE_DIR )
- set( WEBRTC_INSTALL_CMAKE_DIR “lib” )
- endif( )
3. Handle libraries
Just like we did for the tests, we will first need to import all the libraries name from the filesystem before we can do anything. Unlike for the test, where we had to worry about different arguments for each test, all libraries are treated equal so we can automate the process. The file( GLOB_RECURSE ) command does just that. Under mac, all the libs are at the root of the ninja build, but under windows, they are created on their respective subdirectory, so we need to use the GLOB_RECURSE and not just GLOB which would work only for mac.
- set(WEBRTC_BUILD_ROOT ${WebRTC_SOURCE_DIR}/src/out/${CMAKE_BUILD_TYPE}) # the CMAKE_BUILD_TYPE variable allow consistency with build target
- set(WEBRTC_LIB_EXT a) # the default
- if(WIN32)
- set(WEBRTC_LIB_EXT lib) # you’re on windows! you know who you are 🙂
- endif()
- file( GLOB_RECURSE # under windows, the libs are within the subfolders
- WEBRTC_LIBS # the output variable
- ${WEBRTC_BUILD_ROOT}/*.${WEBRTC_LIB_EXT} # the pattern, i.e. all files with the right extension under the build root.
- )
Now, we could directly feed this to the install() command:
- foreach( lib ${WEBRTC_LIBS}
- install(
- FILES ${lib}
- DESTINATION ${WEBRTC_INSTALL_LIB_DIR}
- COMPONENT Libraries
- )
- endforeach()
However, we want to remove the libraries that were used for the tests, and we have to prepare a list of libraries to populate a configuration file that will be installed along the libraries, and make it easy to use the installed version. The full version looks like that:
- set(WEBRTC_LIBRARIES “”) # prepare the config for the build tree
- foreach(lib ${WEBRTC_LIBS})
- string(FIND ${lib} “test” IS_TEST)
- if(IS_TEST EQUAL -1)
- get_filename_component(lib_name ${lib} NAME_WE)
- string(REPLACE “lib” “” lib_target_name ${lib_name})
- set(WEBRTC_LIBRARIES ${WEBRTC_LIBRARIES} ${lib_target_name})
- install(
- FILES ${WEBRTC_BUILD_ROOT}/${lib}
- DESTINATION ${WEBRTC_INSTALL_LIB_DIR}
- COMPONENT Libraries
- )
- endif()
- endforeach()
4. Handle headers files
The delicate part of handling headers, is that a specific root directory and subdirectories layout is expected by the files including them. The DEPS file give you some hints about which dir you should use for the includes:
- # Define rules for which include paths are allowed in our source.
- include_rules = [
- # Base is only used to build Android APK tests and may not be referenced by
- # WebRTC production code.
- ‘-base’,
- ‘-chromium’,
- ‘+gflags’,
- ‘+net’,
- ‘+talk’,
- ‘+testing’,
- ‘+third_party’,
- ‘+webrtc’,
- ]
Apart from the missing flags, those are all top level directories of the WebRTC source. A quick sanity check (grep -R -h \#include * | sort -u > log) confirms that it seems to be the layout expected by the #include lines.
so for each of /net, /talk, /testing, /third_party, /webrtc we need to walk the subdirectory layout and use it at install time (that’s the main difference with the libraries handling code). That will justify using the RELATIVE option of the file( GLOB_RECURSE ) command.
- file(
- GLOB_RECURSE header_files # output variable
- RELATIVE ${WebRTC_SOURCE_DIR}/src # the path will be relative to /src/, as expected by the #includes
- FOLLOW_SYMLINKS # we need to follow the symlinks to chromium subfolders
- ${WebRTC_SOURCE_DIR}/src/net/*.h
- ${WebRTC_SOURCE_DIR}/src/talk/*.h
- ${WebRTC_SOURCE_DIR}/src/testing/*.h
- ${WebRTC_SOURCE_DIR}/src/third_party/*.h
- ${WebRTC_SOURCE_DIR}/src/webrtc/*.h
- )
Now the install command is easy to write.
- foreach( f ${header_files} )
- get_filename_component( RELATIVE_PATH ${f} PATH ) # NOTE ALEX: it seems that newer versions of CMake use DIRECTORY instead of PATH …
- install(
- FILES ${WebRTC_SOURCE_DIR}/src/${f}
- DESTINATION ${WEBRTC_INSTALL_INCLUDE_DIR}/${RELATIVE_PATH} # that’s the tricky part here
- COMPONENT Headers
- )
- endforeach()
5. Are we there yet?
YES! We can now install. You have now an install target in your build system. Under mac, you can simply type “make install”, and under windows, if you used the default (ninja/MSVC) you will have an “INSTALL” target in the list of target in MSVC. It is not build by default, and yo need to trigger the build manually. Administrator rights will surely be needed. By default, everything is installed under /usr/local on mac and unix, and under “Program Files” for windows (with (x86) for the 32b builds).
In a following post, I will show how to package all those files to be installed on a remote computer.
This work by Dr. Alexandre Gouaillard is licensed under a Creative Commons Attribution 4.0 International License.
This blog is not about any commercial product or company, even if some might be mentioned or be the object of a post in the context of their usage of the technology. Most of the opinions expressed here are those of the author, and not of any corporate or organizational affiliation.