|Title:||ROS Install Target|
|Author:||Brian Gerkey <gerkey at openrobotics.org>|
This REP describes an 'install' target for ROS. This proposal describes a file/directory layout policy and extensions to the rosbuild API to support installation. It also notes some known and expected issues with installation, particularly regarding updating existing code to support installation.
Installation is done relative to a prefix, ROS_INSTALL_PREFIX, which is an absolute path. The prefix is specified externally (e.g., by the user doing the install, or by a packaging tool like pbuilder). For system installs, ROS_INSTALL_PREFIX is often /usr, sometimes /usr/local. For a non-system install, it might be anything.
In ROS code, the stack is the installation unit; it is at the stack level that we make releases, check versions, create binary packages, etc. So the install target also works at a stack level. To install a stack:
roscd <stack> make install
The install target is not supported at the level of an individual package (though an install target does exist there, for use by the stack-level install target).
The overall layout is:
ROS_INSTALL_PREFIX/ bin/ : "important" executables, such as rospack and rostopic lib/ : all C/C++ shared and static libraries from all packages ros/ : the ROS package tree, grouped into stacks ros/ros : the ros stack; ROS_ROOT points here ros/<stack> : other stacks are siblings to the ros stack
So a package 'foo', contained in a stack 'bar', can be found at ROS_INSTALL_PREFIX/lib/ros/bar/foo. And ROS_ROOT is always ROS_INSTALL_PREFIX/lib/ros/ros.
Within a package, the layout is:
ROS_INSTALL_PREFIX/lib/ros/<stack>/<package>/ bin/ : C/C++ executables
Other package content can be organized in whatever way is convenient.
The installation system takes a "whitelist" approach. Only those elements that are specifically called out for installation via the rosbuild installation API (see below) are installed. The following package elements are installed automatically, into the package directory:
NOTE: while rosbuild will install the .msg and .srv files, the individual client libraries' CMake code that arranges for code generation should install the generated files. It's the client libraries that know exactly which files will be generated. In this way, we can avoid the recursive copy. This behavior is not currently present in the reference implementation.
Nothing else is installed automatically. So, for example, if you want to install a library, you need to call rosbuild_install_library(); see below for details.
So that executables can be used from a package source tree, rosbuild makes extensive use of RPATH entries. These entries are added at link time, and constitute paths to be searched at runtime for libraries that are needed by the executable. Reliance on RPATHS is unusual, brittle (e.g., a built ROS tree can't be moved and reused without cleaning and rebuilding), and discouraged by distribution maintainers (e.g., Debian's stance , which is probably inherited by Ubuntu).
The proposed install target will strip RPATH entries from all executables (including shared libraries). This is done via CMake's support for modifying RPATH during installation . Because all libraries are installed into ROS_INSTALL_PREFIX/lib, the installed RPATH-less executables can be used so long as ROS_INSTALL_PREFIX/lib is in the dynamic linker's search path. For a system install, where ROS_INSTALL_PREFIX is something like /usr, ROS_INSTALL_PREFIX/lib is in the standard search path. For a custom install, the user might need to add ROS_INSTALL_PREFIX/lib to LD_LIBRARY_PATH (or equivalent).
The macros below will be added to rosbuild. These macros can be called from a package's CMakeLists.txt file.
rosbuild_install_executable(exe [INSTALL_TO_ROOT]): Install the C/C++ executable exe, stripped of RPATH entries, to the bin subdirectory of the package (i.e., ROS_INSTALL_PREFIX/lib/ros/<stack>/<package>/bin). If the INSTALL_TO_ROOT option is given, the executable is instead installed to ROS_INSTALL_PREFIX/bin; this option is intended only for "important" executables, such as rospack and rostopic.
rosbuild_install_library(lib [INSTALL_TO_PACKAGE]): Install the C/C++ library lib, stripped of RPATH entries, to ROS_INSTALL_PREFIX/lib. If the INSTALL_TO_PACKAGE option is given, then the library is instead installed to the lib subdirectory of the package (i.e., ROS_INSTALL_PREFIX/lib/ros/<stack>/<package>/lib); use of this option is discouraged, as it puts the library in a non-standard place.
rosbuild_install_files(files...): Install the listed files to the package directory. Each file is installed in the same package-relative location that it currently occupies. Intermediate directories are created automatically if needed. Files are installed without execute permission. For example:
would install the file bat to the directory ROS_INSTALL_PREFIX/lib/ros/<stack>/<package>/foo/bar/.
rosbuild_install_programs(programs...): Same as rosbuild_install_files(), but the files are installed with execute permission.
rosbuild_install_directory(dir): Recursively install the directory dir to the package directory. The directory is installed to the same package-relative location that it currently occupies. Intermediate directories are created automatically if needed. Directories and files are installed using the same permissions that they currently have. For examples:
would install the directory baz, and all of its contents, to the directory ROS_INSTALL_PREFIX/lib/ros/<stack>/<package>/foo/bar.
Some packages don't use rosbuild, and so can't make use of the installation API described above. The installation system identifies a package as rosbuild-controlled if there is a CMakeLists.txt file in the top of the package directory; otherwise, the package is considered to be non-rosbuild-controlled.
There are two general categories of non-rosbuild-controlled packages:
Both kinds of packages are handled in the same fashion, which is to recursively copy the entire package directory from the source tree to the install tree (directories named build and .svn are filtered out during the copy).
KNOWN ISSUE: 3rdparty packages can include C/C++ shared libraries, which should be moved to ROS_INSTALL_PREFIX/lib. The details for doing that move are TBD. One possibility is to move anything that's found in a directory that the package exports as a library search (-L) directory. Just moving "*.so" isn't good enough, because it's often a symlink, and you need want the various versioned files (libfoo.so.0, libfoo.so.0.0.0, etc.). A further point is that, if a 3rdparty package depends on another ROS package (which should be rare), then the executables (including shared libraries) that are produced by that 3rdparty package might have RPATH entries, which should be stripped.
ROS code is organized into distributions (e.g., boxturtle, cturtle). It may be desirable to maintain installations of multiple distributions on a single system. The current binary .debs allow this kind of installation by segmenting each distribution into its own subdirectory (e.g., /opt/ros/boxturtle, /opt/ros/cturtle). That approach could be taken for the installed package tree (e.g., ROS_INSTALL_PREFIX/lib/ros/boxturtle, ROS_INSTALL_PREFIX/lib/ros/cturtle), but the C/C++ libraries would still conflict with each other in ROS_INSTALL_PREFIX/lib.
The present proposal envisions a single ROS installation in any given ROS_INSTALL_PREFIX. No support is provided for multiple installation in one prefix. Multiple installations would have to go into multiple ROS_INSTALL_PREFIXes, which would likely entail the use of LD_LIBRARY_PATH at runtime.
To date, there has been no 'make install' for ROS code. The ROS build system and associated tools were designed to support building in and running from a packages source tree. This design works well in research-like environments, where everybody has his or her own copy of the code checked out from version control. But it is not appropriate for providing system installations of ROS, e.g., as Debian packages (.debs). The current best practice for such installations is to recursively copy from a built package source tree, combined with heuristic filtering and RPATH-rewriting.
The overall goal of install target is to allow for the installation of ROS code. More specifically, the install target should:
To understand the implications of these specific goals, we'll take a couple of strawman examples:
If nothing is done to an existing package, then what happens during 'make install' will depend on what kind of package it is:
The right way to add installation support to an existing package is to call the appropriate rosbuild_install_*() macros for each of the package elements that are required at runtime. A common list of things to consider for installation:
In the end, it is up to the package or stack maintainer to decide what should be installed, and there are some gray areas (e.g., small example files that aren't strictly needed, but are not obtrusive and might be useful).
Recognizing that it will be a significant effort to update all packages' CMakeLists.txt to enumerate what needs to be installed, an option was added to rosbuild_init. This option enables an easy, automatable way of adding naive installation support to existing packages.
The INSTALL_EVERYTHING option essentially treats the package as non-rosbuild-controlled, but it is implemented in such a way that C/C++ libraries and executables are stripped of RPATH entries and relocated (i.e., libraries go to ROS_INSTALL_PREFIX/lib and executables go to ROS_INSTALL_PREFIX/lib/ros/<stack>/<package>/bin).
The INSTALL_EVERYTHING option is a crutch, and should not live long. In fact, it may be removed before deployment, depending on how many problems arise from using it.
CMake doesn't like target names with slashes in them (I believe that this is official policy, but can't find a reference for it). We often use slashes when asking to build executables in subdirectories, e.g.:
rosbuild_add_executable(bin/drop src/drop.cpp) target_link_libraries(bin/drop topic_tools)
Until now, this usage has caused problems only in isolated circumstances, usually involving CMakeLists.txt in subdirectories. But CMake's installation system does not properly treat executables with target names that have slashes. In particular, it doesn't remove RPATH entries from them during installation. For example, asking to install the executable:
will simply copy it, without removing the RPATH entry.
So existing packages must also be updated to not use slashes in target names. This can be done either via the per-target RUNTIME_OUTPUT_DIRECTORY property:
rosbuild_add_executable(drop src/drop.cpp) target_link_libraries(drop topic_tools) set_target_properties(drop PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin) rosbuild_install_executable(drop)
or via the global EXECUTABLE_OUTPUT_PATH variable:
set(EXECUTABLE_OUTPUT_PATH bin) rosbuild_add_executable(drop src/drop.cpp) target_link_libraries(drop topic_tools) rosbuild_install_executable(drop)
An implementation is in progress at https://code.ros.org/svn/ros/stacks/ros/branches/install_target (rev 11159 at time of writing). To try the implementation:
svn co https://code.ros.org/svn/ros/stacks/ros/branches/install_target ros export ROS_ROOT=`pwd`/ros export PATH=$ROS_ROOT/bin:$PATH export PYTHONPATH=$ROS_ROOT/core/roslib/src unset ROS_PACKAGE_PATH cd ros make install
The ROS_INSTALL_PREFIX is currently hardcoded as /tmp/ros-installed. To use the installation, configure your environment, including LD_LIBRARY_PATH (needed because ROS_INSTALL_PREFIX is not a system location, such as /usr):
export ROS_MASTER_URI=http://localhost:11311 export ROS_ROOT=/tmp/ros-installed/lib/ros/ros export PATH=/tmp/ros-installed/bin:$PATH unset ROS_PACKAGE_PATH source $ROS_ROOT/tools/rosbash/rosbash export PYTHONPATH=$ROS_ROOT/core/roslib/src:$PYTHONPATH export LD_LIBRARY_PATH=/tmp/ros-installed/lib
Notes (valid at the time of writing):
|||Filesystem Heirarchy Standard (http://www.pathname.com/fhs/)|
|||Debian wiki: RPATH issue (http://wiki.debian.org/RpathIssue)|
|||CMake RPATH handling (http://www.vtk.org/Wiki/CMake_RPATH_handling)|
This document has been placed in the public domain.