Adding new Packages
- Creating a package
- Adding the package to the workspace
- Package Description and Dependencies
- Declaring a package
- Version Control Resolution
- Available Version Control Systems
As we already mentioned in the introduction, within Rock, none of the packages should actually rely on Autoproj, Rock's build system. C++ Rock packages for instance rely on common build systems such as CMake or autotools to build, and pkg-config or CMake mechanisms to handle cross-package dependencies. Autoproj is only handling the scheduling of the configuration and build of each the packages, so that build systems and environment variables are set up as required, and dependencies are handled properly.
As such, autoproj mainly needs to know two things about a package:
- what build system it uses. This is done in a Ruby DSL in a file that lies in
the package set, with a
.autobuild
extension. One usually starts with apackages.autobuild
file. The list of declarations in these autobuild files make up the list of package names Autoproj knows about. - how to download it. This is done in YAML in the package
set's
source.yml
file we created when we created the package set. When looking for a package's version control information. Autoproj allows to overlay version control information (for e.g. change the branch that is being checked out). As such, it follows resolution rules to list all version control entries that match the package name, and compute the corresponding version control setup.
Note for simplicity, it is possible to add autobuild files and define
version control information straight into the main build configuration (the
autoproj/
folder). The version control information in this case is stored in
the overrides.yml
file instead of source.yml
.
Creating a package
The first step in creating a package is to pick a name
If you create a package from scratch, Rock provides a set of command-line tools to generate package scaffolds for you:
rock-create-lib
for a C++ libraryrock-create-rubylib
for Ruby librariesrock-create-orogen
for an oroGen componentrock-create-bundle
for a bundle
Each tool follows the same workflow: run the tool with the package path, and answer the tool's questions, e.g.:
rock-create-lib drivers/imu-myahrs
While the other generation scripts create ready-to-use packages, the workflow
of the rock-create-orogen
tool is a bit different. It is covered
in the Components chapter
If you are integrating a package that already exists, it should be easy enough provided that the package uses widespread build systems and follows common conventions (such as having a separate source and build folder, and having an install step).
Adding the package to the workspace
To share the package with your teammates, you must define the package type and VCS information, as detailed in the following of this page.
However, when one starts developing on a new package, this is an unncessary step
if the package uses a common build system (all rock-generate package templates
do). Only add the package path to the layout
section of autoproj/manifest
For instance, when creating drivers/imu-myahrs
one would do
rock-create-lib drivers/imu-myahrs
and then add
layout:
- ...
- drivers/imu-myahrs
You will however have to define the package in the autobuild and source.yml
files before you push the updated manifest to your team members
Package Description and Dependencies
For documentation reason, the packages are expected to provide metadata such as a description of its purpose, the author(s), license, …
Additionally, it is common for packages to depend on each other, meaning that a package needs another package to be there first, for it to build and/or run successfully. Autoproj supports having dependencies between source packages under its control, as well as with packages from other package managers (i.e. the underlying operating system or language-specific managers such as RubyGems or PIP). You will see how these packages are defined later in the OS Dependencies section.
All this information is stored in a XML file whose format follows. If the
package has been created for Rock specifically, it is saved as manifest.xml
file directly at the root of the package. For packages that already exist but
are being integrated in Rock, the file should be saved in the package set under
manifests/package/name.xml
(e.g. simulation/gazebo
's manifest is saved in
manifests/simulation/gazebo.xml
).
<?xml version="1.0"?>
<package>
<description brief="one line of text">
long description goes here,
<em>XHTML is allowed</em>
</description>
<author>Alice/alice@somewhere.bar</author>
<author>Bob/bob@nowhere.foo</author>
<license>BSD</license>
<url>http://rock-robotics.org/</url>
<depend package="pkgname"/>
<depend package="common"/>
<!-- depend can handle both source and osdep packages -->
<depend package="ruby-dev" />
<!-- a dependency that will only be installed for testing -->
<test_depend package="minitest" />
<!-- a dependency that can be ignored if it is not available -->
<depend_optional package="gui/vizkit3d" />
</package>
The <test_depend …>
tag is used for dependencies that are specific to the
package's test suite. The <depend_optional
…>
allows to avoid building some dependencies within some builds.
Declaring a package
A package declaration has two functions: to tell autoproj what packages exist,
and to tell it how it is meant to be configured, built and installed. These
definitions are done in a Ruby DSL, within files with the .autobuild
extension
in the package set or in the main build configuration. While the file names can
be arbitrary, one usually would start with packages.autobuild
.
What we will see in this section is what type of packages exist, how each of them are declared and the principal build configuration options.
CMake packages
A CMake package is defined with
cmake_package "package_name"
More complex tweaking is achieved with
cmake_package "package_name" do |pkg|
[modify the pkg object]
end
In particular, CMake build options are given with
cmake_package "package_name" do |pkg|
pkg.define "VAR", "VALUE"
end
The above snippet being equivalent to calling cmake -DVAR=VALUE
Generally speaking, when integrating a CMake package, set as many of the optional feature knobs explicitly. See this page for a more in-depth discussion.
For instance, if you want some Python bindings to be built, resolve the Python
interpreter once and for all for your whole workspace and pass the path to the
interpreter explicitely. The rock.core
package has explicit support for this
mechanism for Python.
cmake_package 'some/package' do |pkg|
# Helper from rock.core that resolves the Python binary that should be
# used by packages, Returns nil if the build has python support disabled
# (through a configuration options)
bin, = Rock.activate_python_path(pkg)
pkg.define 'BINDINGS_PYTHON', bin
pkg.define 'PYTHON_EXECUTABLE', bin if bin
end
Autoproj being Ruby-based, the Ruby interpreter you should be using is available
within the Autoproj configuration. Rock's CMake macros control the build of Ruby
bindings (present in bindings/ruby
within the package) with the
BINDINGS_RUBY
variable. However, due to the heavily ruby-based nature of the
toolchain, Rock currently does not have an overarching configuration flag to
globally disable all Ruby bindings, so you would need to create one for your
specific needs.
cmake_package 'some/package' do |pkg|
# Assuming that you use the `mypackage_ruby_bindings' flag to
# control the bindings, control them with (enabled by default)
# pkg.define 'BINDINGS_RUBY', Autoproj.config.get('mypackage_ruby_bindings', true)
pkg.define 'RUBY_EXECUTABLE', Autoproj.config.ruby_executable
end
Autotools packages
autotools_package "package_name"
autotools_package "package_name" do |pkg|
pkg.configureflags << "--enable-feature" << "VAR=VALUE"
# additional configuration
end
Since autotools (and specifically, automake) environments are unfortunately not so reusable, autoproj tries to regenerate the autotools scripts forcefully. This can be disabled by setting the some flags on the package:
- using[:aclocal]: set to false if aclocal should not be run
- using[:autoconf]: set to false if the configure script should not be touched
- using[:automake]: set to false if the automake part of the configuration should not be touched
- using[:libtool]: set to false if the libtool part of the configuration should not be touched
For instance, one would add
autotools_package "package_name" do |pkg|
pkg.configureflags << "--enable-feature" << "VAR=VALUE"
# Do regenerate the autoconf part, but no the automake part
pkg.using[:automake] = false
end
Ruby packages
ruby_package "package_name"
ruby_package "package_name" do |pkg|
# additional configuration
end
This package handles pure ruby libraries that do not need to be installed at all. Autoproj assumes that the directory layout of the package follows the following convention:
- programs are in bin/
- the library itself is in lib/
If a Rakefile is present in the root of the source package, its default
task will be called during the build. The rake_setup_task
overrides this default
setting, and to nil
to avoid running a setup task at all.
ruby_package "package_name" do |pkg|
pkg.rake_setup_task = "setup"
end
If the ruby package has a test/
subfolder, Autoproj will configure the test
utility for this package, assuming that the tests are executed with rake test
and reports are saved in ${srcdir}/.test-results
. This behavior can be
disabled by setting pkg.rake_test_task
to nil, and the rake target can be set to
something else than test
if needed.
If you want to enable the tests even though there is no test
folder, set
pkg.test_utility.source_dir
explicitly to the folder that will contain the
test reports and call pkg.with_tests
ruby_package "something" do |pkg|
pkg.test_utility.source_dir = File.join(pkg.srcdir, ".test-results")
pkg.with_tests
end
Alternatively, if there are no tests, set no_results
to true
ruby_package "something" do |pkg|
pkg.test_utility.no_results = true
pkg.with_tests
end
If you want to override the complete process, that is run a different command than
rake
, set both source_dir
and define the test task:
ruby_package "something" do |pkg|
pkg.test_utility.source_dir = File.join(pkg.srcdir, ".test-output")
pkg.test_utility.task do
# Commands that will run the tests
end
end
oroGen packages
orogen_package "package_name"
orogen_package "package_name" do |pkg|
# customization code
end
oroGen packages are the packages where Rock components are implemented. The oroGen handler runs the code generation, configuration and build/install steps.
Simply checking out packages
The importer package type only checks it out, but does not perform any additional steps.
import_package 'package_name'
Custom package building
Post-processing steps can be performed after the checkout using post_install
:
import_package "package_name" do |pkg|
pkg.post_install do
# add commands to build and install the package
end
end
Version Control Resolution
The general format of version control entries is:
package_name:
type: IMPORTER_TYPE
url: IMPORTER_URL
<importer specific control options>
The package set that defines a package must have at least one matching entry in
the version_control
section of its source.yml
file.
version_control:
- package_name:
type: ...
Follow-up package sets (following the order of the build configuration's
manifest) might also have matching entries in their source.yml
files, in the
overrides:
section. These overrides
apply only to packages from other
package sets. Only entries in version_control
apply to the packages from the
same package set.
overrides:
- package_name:
type: ...
Finally, additional overrides may be specified in files in the
autoproj/overrides.d/
folder. The YAML files in this folder do not specify
sections, only a list of packages:
- package_name:
type: ...
Once all matching entries are found, Autoproj resolves the final configuration by
"overlaying" the found entries, starting with the package set that defines the package,
and finishing with autoproj/overrides.d
.
When overlaying definitions:
- if the
type
field is not specified, updating existing settings - if the
type
field is set, clearing existing settings and using the new ones.
For instance, the following two entries:
version_control:
- tools/syskit:
github: rock-core/tools-syskit
branch: master
and (in autoproj/overrides.d/50-syskit.yml
)
- tools/syskit:
branch: some_other_branch
Will be equivalent to
- tools/syskit:
github: rock-core/tools-syskit
branch: some_other_branch
At any time, matching entries as well as the resolved version control
configuration for a package can be displayed with autoproj show
. For
instance, autoproj show tools/roby
in the rock-gazebo build configuration
as of today gives:
source definition
type: git
url: https://github.com/rock-core/tools-syskit.git
branch: syskit2-test-refactoring
push_to: git@github.com:/rock-core/tools-syskit.git
repository_id: github:/rock-core/tools-syskit.git
retry_count: 10
first match: in rock.core (…/autoproj/remotes/rock.core/source.yml)
branch: $ROCK_BRANCH
github: rock-core/tools-$PACKAGE_BASENAME
overriden in main configuration (…/autoproj/overrides.d/01-syskit2.yml)
branch: syskit2-test-refactoring
The $VARIABLE
syntax will be clarified later. We will now see what version control
systems are supported by autoproj.
Available Version Control Systems
The package name may contain regular expression characters, in which case the package name will be used to match against the package name(s).
Note that in all cases, the operations must succeed without any password - the import will fail if a password is needed.
Git
The general setup for git imports is:
package_name:
type: git
url: repository_url_or_path
push_to: repository_url_or_path
branch: branch_name
tag: tag_name # it is branch OR tag
commit: commit_id # it is tag OR commit ID
with_submodules: false # true to checkout and update submodules
Autoproj will maintain an 'autobuild' remote on the checked out repository: i.e., it will make sure that the URL of this remote is always linked to the right URL from the config file, and will update the relevant branch on update (beware: the other branches won't get updated).
Moreover, autoproj will make sure that updating your local repository always resolves as a fast-forward. An update that would require a merge will fail.
GitHub
Autoproj allows to define shortcut handlers, that is to define custom entries
that are expanded into full version control configurations. The
autoproj/git_server_configuration
, which is required by the Rock build
configuration, defines one for github.
This means that one may use the github:
handler to check a package out from
github, rather than defining the type
and url
manually. This has the benefit
of setting the push_to
automatically to the SSH URL (by default) while the plain
URL is using https (usually faster).
package_name:
github: rock-core/buildconf
… rest of options same as git …
Note that Autoproj supports referring to the refs/pull/*
references GitHub
sets up for the pull requests. One can therefore point to a pull request with:
package_name:
github: rock-core/tools-syskit
remote_branch: refs/pull/52/head
local_branch: pr-52
Git LFS
Autoproj may automatically handle Git repositories that also use
git-lfs. You only need to add the following
line to your package set's init.rb
file:
require 'autobuild/import/git-lfs'
Autoproj will automatically checkout the LFS files if it detects that LFS is
enabled on a git repository. Note that because of Autoproj's use of the
autobuild
remote, git lfs
does not work from the command line - a known
limitation of git-lfs itself. You need to use aup
(with e.g. aup -n
to
update only a single package) to handle the LFS bits.
Common LFS configurations can be added to the source entry, or augmented
in autoproj/overrides.d/
using the following options:
lfs
allows to disable autoproj's LFS handling for this packagelfs_exclude
is a list of patterns of LFS-managed files that should not be checked out. Use this to avoid checking out huge files that are usually not used.lfs_include
is a list of patterns of LFS-managed files that should checked out, to possibly override files excluded by more encompassing patterns inlfs_exclude
For instance, in a package set's source.yml
:
package_name:
type: git
url: repository_url_or_path
lfs_exclude: # avoid checking out the blender files by default
- *.blend
Or, to avoid checking out any LFS file locally because you don't need them,
create autoproj/overrides.d/50-no-lfs.yml
:
package_name:
lfs: false
Tar archives
package_name:
type: archive
url: http://sourceforge.net/blablabla.tar.gz?option=value
filename: blabla.tar.gz # Optional: if the file name cannot be inferred from the URL
no_subdirectory: false # Optional. Set to true if there is not a leading directory in the archive
update_cached_file: false # Optional. Set to false to disable automatic updates
The importer expects that there is a leading subdirectory in the archive, under which all files. If that is not the case, i.e. if all files are in the root of the archive, do not forget to set the no_subdirectory option.
Autoproj tries to guess what is the name of the downloaded file by extracting it out of the URL. Sometimes, this does not work as the URL does not fit the expected scheme – in these cases you will get a tar error on update. To override this autodetection behaviour, set the filename option to the actual downloaded file name.
By default, autoproj will check if the downloaded file has been updated on the server, and will download it again if it is the case. If you are downloading release tarballs, this is unneeded as the archive should not be updated. In that case, set the update_cached_file option to false to save the time needed to check for the update (can be long on, for instance, sourceforge). The source will of course be updated if you change the URL (i.e. to download a new release of the same software).
Subversion
The general setup for subversion imports is:
package_name:
type: svn
url: repository_url_or_path
CVS
The general setup for CVS imports is:
package_name:
type: cvs
url: cvs_root
module: modulename
In case a :pserver: is used, it must be quoted - YAML would interpret it the wrong way otherwise:
package_name:
type: cvs
url: ":pserver:cvs@blabla:/"
module: modulename
Patching after checkout or update
It is possible to apply patches after a given package (imported by any of the
importer types) has been checked out/updated. To do so, simply add the option
patches:
to the importer configuration and list the patches which should be
applied:
package_name:
type: archive
url: http://sourceforge.net/blablabla
patches:
- $AUTOPROJ_SOURCE_DIR/blablabla-01.patch
- $AUTOPROJ_SOURCE_DIR/blablabla-02.patch
Note that in the example above, the patch is saved in the package set's folder (the value of AUTOPROJ_SOURCE_DIR). This is a highly recommended practice.
The provided patches are by default applied with a patch level of 0 (passed to patch through the -p option). This can be overriden on a patch-per-patch basis by making the patch name an array as well:
package_name:
type: archive
url: http://sourceforge.net/blablabla
patches:
- [$AUTOPROJ_SOURCE_DIR/blablabla-01.patch, 1]
- $AUTOPROJ_SOURCE_DIR/blablabla-02.patch
How to create a patch
If you are locally modifying a git
-based package, you can simply run git diff
,
redirecting it to the final patch place, e.g.
git diff > $AUTOPROJ_CURRENT_ROOT/remotes/my.set/my-package_add_pkgconfig.patch
The generated patch will need the patch level of 1 as described above.
If you are locally modifying a package based on an archive, use diff -ru
:
-
move the modified package into a
.new
folder:cd some/ mv package package.new
-
Checkout the fresh package with
aup -n my/package
-
Generate the diff
cd some diff -r -u package package.new > \ $AUTOPROJ_CURRENT_ROOT/remotes/my.set/my-package_add_pkgconfig.patch
-
Add the patch to the package set, using a patch level of 1 as well, and verify the result
aup -n my/package acd my/package diff -r -u . ../package.new