-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Package identification & build tasks for colcon-ros-cargo
This includes cargo-ros-install, a wrapper around cargo build which installs files into the correct places for ROS 2 tools to find them.
- Loading branch information
Showing
18 changed files
with
759 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Generated by Cargo | ||
# will have compiled files and executables | ||
debug/ | ||
target/ | ||
|
||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries | ||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html | ||
Cargo.lock | ||
|
||
# These are backup files generated by rustfmt | ||
**/*.rs.bk | ||
|
||
# MSVC Windows builds of rustc generate these, which store debugging information | ||
*.pdb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
cmake_minimum_required(VERSION 3.5) | ||
project(cargo-ros-install NONE) | ||
|
||
find_package(ament_cmake REQUIRED) | ||
|
||
configure_file(${CMAKE_SOURCE_DIR}/Cargo.toml ${CMAKE_BINARY_DIR}/Cargo.toml COPYONLY) | ||
configure_file(${CMAKE_SOURCE_DIR}/src/lib.rs ${CMAKE_BINARY_DIR}/src/lib.rs COPYONLY) | ||
configure_file(${CMAKE_SOURCE_DIR}/src/main.rs ${CMAKE_BINARY_DIR}/src/main.rs COPYONLY) | ||
add_custom_command( | ||
OUTPUT | ||
${CMAKE_BINARY_DIR}/target/release/cargo-ros-install | ||
COMMAND cargo build -q --release | ||
DEPENDS | ||
src/lib.rs | ||
src/main.rs | ||
Cargo.toml | ||
WORKING_DIRECTORY | ||
${CMAKE_BINARY_DIR} | ||
) | ||
|
||
add_custom_target( | ||
build_crate ALL | ||
DEPENDS | ||
${CMAKE_BINARY_DIR}/target/release/cargo-ros-install | ||
) | ||
|
||
install(FILES | ||
${CMAKE_BINARY_DIR}/target/release/cargo-ros-install | ||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE | ||
DESTINATION bin | ||
) | ||
ament_package() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "cargo-ros-install" | ||
version = "0.1.0" | ||
authors = ["nnmm <nnmmgit@gmail.com>"] | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
anyhow = "1" | ||
cargo-manifest = "0.2" | ||
fs_extra = "1" | ||
pico-args = "0.4" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0"?> | ||
<package format="2"> | ||
<name>cargo-ros-install</name> | ||
<version>0.0.0</version> | ||
<description>Cargo build/check wrapper that installs files as described in REP 122</description> | ||
<maintainer email="nnmmgit@gmail.com">nnmm</maintainer> | ||
<license>Apache License 2.0</license> | ||
|
||
<buildtool_depend>ament_cmake</buildtool_depend> | ||
|
||
<export> | ||
<build_type>ament_cmake</build_type> | ||
</export> | ||
</package> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
use anyhow::{anyhow, Context, Result}; | ||
use cargo_manifest::Product; | ||
|
||
use std::ffi::OsString; | ||
use std::fs::{DirBuilder, File}; | ||
use std::path::Path; | ||
use std::path::PathBuf; | ||
use std::process::Command; | ||
|
||
/// Arguments for both the wrapper and for `cargo build`. | ||
pub struct Args { | ||
/// The install base for this package (i.e. directory containing `lib`, `share` etc.) | ||
pub install_base: PathBuf, | ||
/// The build base for this package, corresponding to the --target-dir option | ||
pub build_base: PathBuf, | ||
/// Arguments to be forwarded to `cargo build`. | ||
pub forwarded_args: Vec<OsString>, | ||
/// "debug", "release" etc. | ||
pub profile: String, | ||
/// The absolute path to the Cargo.toml file. Currently the --manifest-path option is not implemented. | ||
pub manifest_path: PathBuf, | ||
} | ||
|
||
impl Args { | ||
pub fn parse() -> Result<Self> { | ||
let mut args: Vec<_> = std::env::args_os().collect(); | ||
args.remove(0); // remove the executable path. | ||
|
||
// Find and process `--`. | ||
let forwarded_args = if let Some(dash_dash) = args.iter().position(|arg| arg == "--") { | ||
// Store all arguments following ... | ||
let later_args: Vec<_> = args[dash_dash + 1..].to_vec(); | ||
// .. then remove the `--` | ||
args.remove(dash_dash); | ||
later_args | ||
} else { | ||
Vec::new() | ||
}; | ||
|
||
// Now pass all the arguments (without `--`) through to `pico_args`. | ||
let mut args = pico_args::Arguments::from_vec(args); | ||
let profile = if args.contains("--release") { | ||
String::from("release") | ||
} else if let Ok(p) = args.value_from_str("--profile") { | ||
p | ||
} else { | ||
String::from("debug") | ||
}; | ||
|
||
let build_base = args | ||
.opt_value_from_str("--target-dir")? | ||
.unwrap_or_else(|| "target".into()); | ||
let install_base = args.value_from_str("--install-base")?; | ||
|
||
let manifest_path = if let Ok(p) = args.value_from_str("--manifest-path") { | ||
p | ||
} else { | ||
PathBuf::from("Cargo.toml") | ||
.canonicalize() | ||
.context("Package manifest does not exist.")? | ||
}; | ||
|
||
let res = Args { | ||
install_base, | ||
build_base, | ||
forwarded_args, | ||
profile, | ||
manifest_path, | ||
}; | ||
|
||
Ok(res) | ||
} | ||
} | ||
|
||
pub fn cargo_build(args: &[OsString], is_pure_library: bool) -> Result<Option<i32>> { | ||
let mut cmd = Command::new("cargo"); | ||
// "check" and "build" are compatible | ||
if is_pure_library { | ||
cmd.arg("check"); | ||
} else { | ||
cmd.arg("build"); | ||
} | ||
for arg in args { | ||
cmd.arg(arg); | ||
} | ||
let exit_status = cmd | ||
.status() | ||
.context("Failed to spawn 'cargo build' subprocess.")?; | ||
Ok(exit_status.code()) | ||
} | ||
|
||
/// This is comparable to ament_index_register_resource() in CMake | ||
pub fn create_package_marker( | ||
install_base: impl AsRef<Path>, | ||
marker_dir: &str, | ||
package_name: &str, | ||
) -> Result<()> { | ||
let mut path = install_base | ||
.as_ref() | ||
.join("share/ament_index/resource_index"); | ||
path.push(marker_dir); | ||
DirBuilder::new() | ||
.recursive(true) | ||
.create(&path) | ||
.with_context(|| { | ||
format!( | ||
"Failed to create package marker directory '{}'.", | ||
path.display() | ||
) | ||
})?; | ||
path.push(package_name); | ||
File::create(&path) | ||
.with_context(|| format!("Failed to create package marker '{}'.", path.display()))?; | ||
Ok(()) | ||
} | ||
|
||
/// Copy the source code of the package to the install space | ||
pub fn install_package( | ||
install_base: impl AsRef<Path>, | ||
package_path: impl AsRef<Path>, | ||
package_name: &str, | ||
) -> Result<()> { | ||
let mut dest = install_base.as_ref().to_owned(); | ||
dest.push("share"); | ||
dest.push(package_name); | ||
dest.push("Rust"); | ||
fs_extra::dir::remove(&dest)?; | ||
DirBuilder::new().recursive(true).create(&dest)?; | ||
let mut opt = fs_extra::dir::CopyOptions::new(); | ||
opt.overwrite = true; | ||
for dir_entry in std::fs::read_dir(package_path)? { | ||
let dir_entry = dir_entry?; | ||
let src = dir_entry.path(); | ||
let filename = dir_entry.file_name(); | ||
// There might be a target directory after a manual build with cargo | ||
if filename == "target" { | ||
continue; | ||
} | ||
if src.is_dir() { | ||
fs_extra::dir::copy(&src, &dest, &opt).context("Failed to install package.")?; | ||
} else { | ||
let dest_file = dest.join(filename); | ||
std::fs::copy(&src, &dest_file).context("Failed to install package.")?; | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// Copy the binaries to a location where they are found by ROS 2 tools (the lib dir) | ||
pub fn install_binaries( | ||
install_base: impl AsRef<Path>, | ||
build_base: impl AsRef<Path>, | ||
package_name: &str, | ||
profile: &str, | ||
binaries: &[Product], | ||
) -> Result<()> { | ||
for binary in binaries { | ||
let name = binary | ||
.name | ||
.as_ref() | ||
.ok_or(anyhow!("Binary without name found."))?; | ||
let src_location = build_base.as_ref().join(profile).join(name); | ||
let mut dest = install_base.as_ref().join("lib").join(package_name); | ||
// Create destination directory | ||
DirBuilder::new().recursive(true).create(&dest)?; | ||
dest.push(name); | ||
std::fs::copy(&src_location, &dest).context(format!( | ||
"Failed to copy binary from '{}'.", | ||
src_location.display() | ||
))?; | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use anyhow::{anyhow, Context, Result}; | ||
|
||
use cargo_manifest::Manifest; | ||
use cargo_ros_install::*; | ||
|
||
fn main() { | ||
let exitcode = match fallible_main().context("Error in cargo-ros-install") { | ||
Ok(Some(code)) => code, | ||
Ok(None) => { | ||
eprintln!("'cargo build' was terminated by signal."); | ||
1 | ||
} | ||
Err(e) => { | ||
eprintln!("{:?}", e); | ||
1 | ||
} | ||
}; | ||
// No destructors left to run, so it's fine to exit. | ||
std::process::exit(exitcode); | ||
} | ||
|
||
fn fallible_main() -> Result<Option<i32>> { | ||
let args = Args::parse()?; | ||
let mut manifest = Manifest::from_path(&args.manifest_path)?; | ||
manifest.complete_from_path(&args.manifest_path)?; | ||
|
||
// Unwrap is safe since complete_from_path() has been called | ||
let is_pure_library = manifest.bin.as_ref().unwrap().is_empty(); | ||
let exitcode = cargo_build(&args.forwarded_args, is_pure_library)?; | ||
|
||
let package_name = &manifest | ||
.package | ||
.ok_or(anyhow!("Package has no name."))? | ||
.name; | ||
let package_path = args | ||
.manifest_path | ||
.parent() | ||
.ok_or(anyhow!("Manifest path must have a parent."))?; | ||
|
||
// Putting marker file creation after the actual build command means that | ||
// we create less garbage if the build command failed. | ||
create_package_marker(&args.install_base, "packages", package_name)?; | ||
// This marker is used by colcon-ros-cargo when looking for dependencies | ||
create_package_marker(&args.install_base, "rust_packages", package_name)?; | ||
install_package(&args.install_base, package_path, package_name)?; | ||
install_binaries( | ||
&args.install_base, | ||
&args.build_base, | ||
package_name, | ||
&args.profile, | ||
// Unwrap is safe since complete_from_path() has been called | ||
&manifest.bin.unwrap(), | ||
)?; | ||
Ok(exitcode) | ||
} |
Oops, something went wrong.