API review
Contents
Proposer: Troy Straszheim
While attempting to implement REP 102; my changes quickly got out of hand and would have broken lots of stuff (for instance, all of the tutorials).
Brian asked me to start this API review as a way of iterating towards a possible "Rosbuild 2".
Currently I have a list of sketched "Buildsystem Desiderata" that are a mixture of use cases and coding standards, indended to:
- speed the build process
- improve maintainability
- improve interoperation of ROS's build with other build systems
- use cmake in ways that jibe with its design
- make ROS installable
Buildsystem Desiderata (Troy)
- Build with minimal subshelling: execute "cmake" once at configure time.
- Uniform and simple interface to the build system
- Full dependency checking at all times (e.g. you can't get 'library not found' errors because you ran 'make' instead of 'rosmake'). In this case the user has to know the build details of a project in order to diagnose a link error from 'make'.
- CMake should generate a subshell script that you can exit from via configure_file(). This script also can be used to execute individual processes: *all* environment setting throughout the ROS system, at build and run time, should happen in this generated script. At install time, an installed-friendly setup.sh is generated and installed by cmake.
- use this script to call any subshells from build process
- eliminiate -L, -l, and explicit handling of rpaths (these you can find in assorted manifest.xmls. CMake does the Right Thing here very consistently if you just give it the information it needs to do so.
- manifest.xml should contain information on as fine a granularity as possible
Remove need for backticks in manifest.xml. See toolset
Include paths (specified via -I) should be minimal.
- Increase granularity: individual libraries within a package should be able to have different compile flags/dependencies. Otherwise a single file (for instance a test) with dependencies on lots of stuff increases the compile/link lines for all files in the package, which slows down compile times and makes build problems harder to diagnose.
- Don't write build products to source directories. Out-of-source builds should be possible, this enables one to test on various compilers/platforms w/o checking code in to source control. Also allows one to easily see what files have been modified w/o maintaining a long list of ignores. And to have parallel debug/release build spaces: allows to swtich from release to debug builds without recompiling the universe. For python there is an import hook trick that allows generated messages and static code to appear to be part of the same package. Something similar might work to obsolete load_manifest() as well.
- Install via cmake-generated "make install", this puts cpack at your disposal and makes it possible to generate various installers for osx and windows as well as rpms and debs.
- As much as possible, information should be stored in one 'canonical' location and one only.
Remove set(LIBRARY_OUTPUT_DIRECTORY ...) from individual CMakeLists.txt
Remove cmake_minimum_required() from individual CMakeLists.txt
- Coding convention: cmake macros in lowercase, variables in uppercase.
toolset: detect available tools once, at beginning of cmake process, make them available in some uniform way to "clients" i.e. packages.
- usecase: multiple packages that build with CUDA. Each would need to include gobs of custom cmake code and wouldn't necessarily provide consistent hooks to tweak things.
- Files generated during testing should go someplace specific, not dropped randomly into the source hierarchy. Improper perturbance of the source hierarchy should be checked during testing.
- Files used by testing shouldn't be in source control (if they are sufficiently large) but pulled in via rsync/deb/wget/etc
- What is installed should be minimal
- One should be able to build and run individual test binaries
- Possibly use exported targets to break things up.
- ROS should build in a subdirectory of another cmake build.
- A package's build infrastructure should work in several scenarios:
- Building as part of a large build tree including all of ROS
- Building against a built (but not installed) ROS
- Building against a built and installed ROS
- build system should know if user has assembled a build that involves incompatible versions of packages and give a sensible error message
- build system should be able to build documentation in a variety of formats; docs should be linked together appropriately. This implies that docs are stored in source control with the code that they document.
Backward compatibility (Brian)
There's a LOT of existing code that uses the existing build system. To avoid massive breakage, we should provide a gentle migration path. We should continue to support the existing build system and workflow, with deprecation warnings as appropriate, for example:
- Manifests that contain backquote invocations, explicit rpath references, and other ugly stuff.
Being able to type make in a package source directory to build it, and make test to test it.
Using things like rospack export --lang=cpp --attrib=cflags foo to gather flags, which non-ROS projects can use to build against ROS packages.
install target Desiderata
(comments from the "LAAS infrastructure team")
FHS is mentionned in REP102, but it seems that the "ROS tree in PREFIX/lib/ros/*" that is suggested goes somehow against the FHS philosophy.
We strongly suggest to stay close the FHS: binaries should be installed in PREFIX/bin, libs in PREFIX/lib, includes in PREFIX/include (and maybe as well .msg, .srv, .action in PREFIX/share/<ros-package>/{msg|srv|action}). That where people expect to find binairies, libs, etc.
The main reason invoked for not doing that is that it would break a lot of packages that "refer resources in a package-relative manner". It may be possible to workaround that by symlinking installed resources in a "virtual ROS tree" to ease the transition towards the "no more relative path" status.- pkgconfig files/cmake Find*: producing these files would help a lot 3rd party software to actually make use of ROS packages. .pc files are straightforward to generate from the manifests.
- "stacks as install unit": while it make sense in several cases, I see it as well as an issue. opencv, pcl are examples: you may want to install them without installing everything, but more generally: it a question of: is rospack a generic package manager for robotics libraries and software (including all ROS stuff) or is it a ros centric system. In the first case, you may want to install pcl without pcl-ros. In the 2nd case, it makes sense to simply install the "pcl stack". We are much in favour of the first approach, being able to use the great libraries that are packaged in ROS without always installing all the ROS specific code. If packages are individually installable, it's still pretty easy to have meta-packages (...meta-debs) for stacks that depend on individual packages.
- Management of non-ROS build packages: quite a lot (if not most of) 3rd party packages do provide their own install targets. rosbuild should somehow be able to call them. Quoting Brian: "To take advantage of the 3rdparty install logic, we'd have to require that every non-rosbuild-controlled package supply an 'install' target, and that it be implemented correctly.". Most well designed 3rd party software packages should provide it (or it's a matter of submitting a bug report), so maybe we can assume these install target are there?
In our experience with robotpkg, we actually had to write a couple of specific Makefile rules to install unregular packages, but it was not that common.
Deb-building Support (Jeremy)
To play nicely with the deb-building infrastructure, we need to make sure we properly support the DESTDIR variable at install time. From the sound of it, CMake does this fairly cleanly so I'm not too worried about native packages. Where I expect we will run into problems is integration with 3rd-party packages -- some make-centric packages may not have been implemented with DESTDIR in mind. Creating a deb for such a package, generally implies fixing its build-system. This dramatically increases the burden of adding a 3rd-party package to ROS (at least if we want to be able to build a deb for it).
More desiderata
- No operating-system-specific scripting languages such as bash scripts and Makefiles. All scripting written in a language that is cross-platform (e.g. Python).
- Ensure pkg-config files are generated for all ROS packages for use by 3rd-party software.
- Formalise the package format to facilitate cooperation with other robot-oriented packaging systems (autopkg, robotpkg).
- Minimise how many 3rd party packages are added to ROS: try to work with existing systems instead whenever possible (e.g. if a specific version is required, insist on that being installed through the OS's package manager first, even if manually). Only fall back to a ROS version as a last resort.
Less re-invention on the package management side. Take a close look at Gentoo's Portage and Arch's Pacman. Both implement highly-effective source-based distribution with flexibility, generating binary packages, versioning, something like stacks, slotted installs, cross-compiling, seamless use of multiple package repositories, etc. (See e.g. Gentoo Prefix: http://www.gentoo.org/proj/en/gentoo-alt/prefix/index.xml)
kwc
Python
Python users should generally not notice any new build infrastructure. I should be able to checkout a package, modify the script that I'm running, and immediately run it without any building, copying, etc... As one example, I should be able to have two windows: an editor and a running interpreter, and keep reloading the module in my interpreter as I edit it.
Stacks vs. Packages
There is demand to release packages individually, but this is a major configuration management issue. We have to run approximately 12 regression jobs (devel/released/pre-release * lucid/maverick * i386/amd64) per stack on unstable, and 24 per stack in cturtle. These tests ensure that a stack actually installs, and they are what enables us to not break cturtle/unstable when doing new releases. Given that these regression jobs frequently find issues, it appears that they are necessary. From a configuration management point of view, it's not possible to scale this easily.
An alternative that we have yet to explore is unary stacks, where you can place both a stack.xml and manifest.xml in the same directory. This has some consequences for the build system and the rest of the ROS tools, but should be do-able. I think this approach balances the tension between packages that are truly standalone vs. the stacks that truly should be bundled.
(Josh)
I'd like to see all build-specific information removed from the manifest. Most of it (everything but dependencies) should be generated at build time into some other file (which cannot be pkg-config, since as far as I know it only supports C/C++).
Also, all the standard exported compile flags should be specified as their higher-level component (such as "include directories", "library", etc.) rather than the actual flag (-I<path>, -lfoo, etc.)
rosbuild design goals discussed at Willow Garage meeting 13 Dec 2010
- Out-of-source builds
- Why?
- make clean works trivially and completely (rm -rf in the build tree)
- supports cross-compilation
- supports multiple compiler / architecture builds
- it's the Right Way to use CMake
- Sure, but still: most users won't use or care about it.
- Why?
- One invocation of CMake
- Maintain the current ROS way of doing package-relative resource location
- Maintain the current in-source-tree workflow (for users who want it)
- Provide a proper install target
- support providing foo vs. foo-devel installations (and thus packages)
- need a way to run tests against installed system
- Smaller debs
- probably the most user-visible negative aspect of our build / package system
- Allow distro-specific binaries to be installed into ROS_ROOT or similar location
- Ease the uptake of ROS code into distributions (Ubuntu, Fedora, etc.)
- follow the FHS as much as possible
- Use CMake support for finding things like boost
- get rid of rosboost-cfg
- Get rid of backticks and other UNIXisms in manifests
- Support Windows
- Simplify maintenance of single-packages stacks, at least getting rid of the need to think of two names (e.g., gmapping and slam_gmapping).
- possible solution: "unary stacks" (directory with both manifest.xml and stack.xml)
Facilitate use of ROS code from outside ROS (e.g., provide FindFoo.cmake module for using in building against the ROS foo package)
TODO
- Get details on the Aldebaran guys' buildsystem... how can we best
- play nice?