Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rust: add support for workspaces #2842

Merged
merged 4 commits into from
Jan 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions snapcraft/internal/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,3 +736,19 @@ def get_brief(self) -> str:

def get_resolution(self) -> str:
return "Remove the suspect files from the snap using the `stage` or `prime` keywords."


class SnapcraftPluginAssertionError(SnapcraftException):
def __init__(self, *, name: str, reason: str, details: str = "") -> None:
self._name = name
self._reason = reason
self._details = details

def get_brief(self) -> str:
return f"Unable to build {self._name!r}: {self._reason}"

def get_details(self) -> str:
return self._details

def get_resolution(self) -> str:
return "Ensure the part's configuration and sources are correct."
83 changes: 72 additions & 11 deletions snapcraft/plugins/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@
import collections
import logging
import os
from pathlib import Path
from contextlib import suppress
from typing import List, Optional

import toml

import snapcraft
from snapcraft import sources
from snapcraft import shell_utils
from snapcraft import file_utils, shell_utils
from snapcraft.internal import errors

_RUSTUP = "https://sh.rustup.rs/"
Expand Down Expand Up @@ -194,21 +195,76 @@ def _get_target(self) -> str:
)
return rust_target.format("unknown-linux", "gnu")

def _project_uses_workspace(self) -> bool:
cargo_toml_path = Path(self.builddir, "Cargo.toml")

try:
config = open(cargo_toml_path).read()
except FileNotFoundError:
raise errors.SnapcraftPluginAssertionError(
name=self.name, reason="missing required Cargo.toml in source directory"
)

return "workspace" in toml.loads(config)

def _install_workspace_artifacts(self) -> None:
"""Install workspace artifacts."""
# Find artifacts in release directory.
release_dir = Path(self.builddir, "target", "release")

# Install binaries to bin/.
bins_dir = Path(self.installdir, "bin")
bins_dir.mkdir(parents=True, exist_ok=True)

# Install shared objects to usr/lib/<arch-triplet>.
# TODO: Dynamic library support needs to be properly added.
# Although we install libraries if we find them, they are most
# likely going to be missing dependencies, e.g.:
# /home/ubuntu/.cargo/toolchains/stable-x86_64-unknown-linux-gnu/lib/libstd-fae576517123aa4e.so
libs_dir = Path(self.installdir, "usr", "lib", self.project.arch_triplet)
libs_dir.mkdir(parents=True, exist_ok=True)

# Cargo build marks binaries and shared objects executable...
# Search target directory to get these and install them to the
# correct location (*.so to lib directory, otherwise bin directory).
executables = [
i
for i in release_dir.iterdir()
if os.path.isfile(i) and os.access(i, os.X_OK)
]
for exe_path in executables:
if exe_path.name.endswith(".so"):
file_utils.link_or_copy(exe_path.as_posix(), libs_dir.as_posix())
else:
file_utils.link_or_copy(exe_path.as_posix(), bins_dir.as_posix())

def build(self):
super().build()

# Write a minimal config.
self._write_cargo_config()

install_cmd = [
self._cargo_cmd,
"install",
"--path",
self.builddir,
"--root",
self.installdir,
"--force",
]
uses_workspaces = self._project_uses_workspace()
if uses_workspaces:
# This is a bit ugly because `cargo install` does not yet support
# workspaces. Alternatively, there is a perhaps better option
# to use `cargo-build --out-dir`, but `--out-dir` is considered
# unstable and unavailable for use yet on the stable channel. It
# may be better because the use of `cargo install` without `--locked`
# does not appear to honor Cargo.lock, while `cargo build` does by
# default, if it is present.
install_cmd = [self._cargo_cmd, "build", "--release"]
else:
install_cmd = [
self._cargo_cmd,
"install",
"--path",
self.builddir,
"--root",
self.installdir,
"--force",
]

toolchain = self._get_toolchain()
if toolchain is not None:
install_cmd.insert(1, "+{}".format(toolchain))
Expand All @@ -224,11 +280,16 @@ def build(self):
install_cmd.append(" ".join(self.options.rust_features))

# build and install.
self.run(install_cmd, env=self._build_env())
self.run(install_cmd, env=self._build_env(), cwd=self.builddir)

# Finally, record.
self._record_manifest()

if uses_workspaces:
# We need to install the workspace artifacts as a workaround until
# `cargo build` supports `out-dir` in "stable".
self._install_workspace_artifacts()

def _build_env(self):
env = os.environ.copy()

Expand Down
6 changes: 6 additions & 0 deletions tests/spread/plugins/rust/snaps/rust-workspace-fib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[workspace]

members = [
"fib",
"calc-fib"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "calc-fib"
version = "0.1.0"
authors = ["Chris Patterson <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
regex = "1"
fib = { path = "../fib" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::env;
extern crate fib;

fn main() {
let args: Vec<String> = env::args().collect();

if args.len() < 2 {
eprintln!("please specify number");
return
}

let num_string = &args[1];
let number: i32 = match num_string.parse() {
Ok(n) => {
n
},
Err(_) => {
eprintln!("error: not an integer");
return;
}
};

let sum = fib::fib(number);
println!("{}", sum);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "fib"
version = "0.1.0"
authors = ["Chris Patterson <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
13 changes: 13 additions & 0 deletions tests/spread/plugins/rust/snaps/rust-workspace-fib/fib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Terribly inefficient recursive fib routine.
pub fn fib(n: i32) -> u64 {
if n < 0 {
panic!("invalid: {}", n);
}
if n == 0 {
return 0;
} else if n == 1 {
return 1;
} else {
return fib(n - 1) + fib(n - 2)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: calc-fib
version: "1.0"
summary: Test the rust plugin with sworkspaces.
description: |
This is a basic rust snap with workspaces.

grade: devel
confinement: strict

apps:
calc-fib:
command: bin/calc-fib

parts:
calc-fib:
plugin: rust
source: .
36 changes: 36 additions & 0 deletions tests/spread/plugins/rust/workspace/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
summary: Build and run a workspace rust snap

systems:
- ubuntu-18.04
- ubuntu-18.04-64
- ubuntu-18.04-amd64
- ubuntu-18.04-i386
- ubuntu-18.04-armhf
- ubuntu-16.04
- ubuntu-16.04-64
- ubuntu-16.04-amd64
- ubuntu-16.04-i386
- ubuntu-16.04-armhf

environment:
SNAP_DIR: ../snaps/rust-workspace-fib

prepare: |
#shellcheck source=tests/spread/tools/snapcraft-yaml.sh
. "$TOOLS_DIR/snapcraft-yaml.sh"
set_base "$SNAP_DIR/snap/snapcraft.yaml"

restore: |
cd "$SNAP_DIR"
snapcraft clean
rm -f ./*.snap

#shellcheck source=tests/spread/tools/snapcraft-yaml.sh
. "$TOOLS_DIR/snapcraft-yaml.sh"
restore_yaml "snap/snapcraft.yaml"

execute: |
cd "$SNAP_DIR"
snapcraft
sudo snap install calc-fib_*.snap --dangerous
[ "$(calc-fib 8)" = "21" ]
Loading