Skip to content
This repository has been archived by the owner on May 19, 2022. It is now read-only.

Commit

Permalink
Support in-memory asset sources
Browse files Browse the repository at this point in the history
  • Loading branch information
kornelski committed Feb 25, 2018
1 parent 8493dd1 commit dabd655
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 31 deletions.
4 changes: 1 addition & 3 deletions src/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use error::*;
use std::os::unix::ffi::OsStrExt;
use archive::Archive;
use wordsplit::WordSplit;
use std::fs::metadata;

/// Generates an uncompressed tar archive with `control`, `md5sums`, and others
pub fn generate_archive(options: &Config, time: u64, asset_hashes: HashMap<PathBuf, Digest>, listener: &mut Listener) -> CDResult<Vec<u8>> {
Expand Down Expand Up @@ -83,8 +82,7 @@ fn generate_control(archive: &mut Archive, options: &Config, listener: &mut List

let installed_size = options.assets
.iter()
.filter_map(|asset| metadata(&asset.source_file).ok())
.map(|m| m.len())
.filter_map(|m| m.source.len())
.sum::<u64>() / 1024;

write!(&mut control, "Installed-Size: {}\n", installed_size)?;
Expand Down
7 changes: 3 additions & 4 deletions src/data.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::io::Write;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use md5::Digest;
use md5;
use file;
Expand Down Expand Up @@ -65,10 +65,9 @@ fn generate_copyright_asset(options: &Config) -> CDResult<()> {
fn archive_files(archive: &mut Archive, options: &Config, listener: &mut Listener) -> CDResult<HashMap<PathBuf, Digest>> {
let mut hashes = HashMap::new();
for asset in &options.assets {
let out_data = file::get(&asset.source_file)
.map_err(|e| CargoDebError::IoFile("unable to read asset to add to archive", e, asset.source_file.clone()))?;
let out_data = asset.source.data()?;

listener.info(format!("{} -> {}", asset.source_file.display(), asset.target_path.display()));
listener.info(format!("{} -> {}", asset.source.path().unwrap_or_else(|| Path::new("-")).display(), asset.target_path.display()));

hashes.insert(asset.target_path.clone(), md5::compute(&out_data));
archive.file(&asset.target_path, &out_data, asset.chmod)?;
Expand Down
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,10 @@ pub fn strip_binaries(options: &Config, target: Option<&str>, listener: &mut Lis
}
}

for name in options.built_binaries() {
for path in options.built_binaries().into_iter().filter_map(|a| a.path()) {
Command::new(strip_cmd)
.arg("--strip-unneeded")
.arg(name)
.arg(path)
.status()
.and_then(|s| if s.success() {
Ok(())
Expand All @@ -199,12 +199,12 @@ pub fn strip_binaries(options: &Config, target: Option<&str>, listener: &mut Lis
.map_err(|err| {
if let Some(target) = target {
let conf_path = cargo_config.as_ref().map(|c|c.path()).unwrap_or(Path::new(".cargo/config"));
CargoDebError::StripFailed(name.to_owned(), format!("{}: {}.\nhint: Target-specific strip commands are configured in [target.{}] strip = \"{}\" in {}", strip_cmd, err, target, strip_cmd, conf_path.display()))
CargoDebError::StripFailed(path.to_owned(), format!("{}: {}.\nhint: Target-specific strip commands are configured in [target.{}] strip = \"{}\" in {}", strip_cmd, err, target, strip_cmd, conf_path.display()))
} else {
CargoDebError::CommandFailed(err, "strip")
}
})?;
listener.info(format!("Stripped '{}'", name.display()));
listener.info(format!("Stripped '{}'", path.display()));
}
Ok(())
}
85 changes: 65 additions & 20 deletions src/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::env::consts::{ARCH, DLL_PREFIX, DLL_SUFFIX};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::fs;
use std::collections::HashSet;
use std::borrow::Cow;
use listener::Listener;
use toml;
use file;
Expand All @@ -16,30 +18,73 @@ fn is_glob_pattern(s: &str) -> bool {
s.contains('*') || s.contains('[') || s.contains(']') || s.contains('!')
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum AssetSource {
/// Copy file from the path (and strip binary if needed).
Path(PathBuf),
/// Write data to destination as-is.
Data(Vec<u8>),
}

impl AssetSource {
pub fn path(&self) -> Option<&Path> {
match *self {
AssetSource::Path(ref p) => Some(p),
_ => None,
}
}

pub fn len(&self) -> Option<u64> {
match *self {
// FIXME: may not be accurate if the executable is not stripped yet?
AssetSource::Path(ref p) => {
fs::metadata(p).ok().map(|m| m.len())
},
AssetSource::Data(ref d) => {
Some(d.len() as u64)
},
}
}

pub fn data(&self) -> CDResult<Cow<[u8]>> {
Ok(match *self {
AssetSource::Path(ref p) => {
let data = file::get(p)
.map_err(|e| CargoDebError::IoFile("unable to read asset to add to archive", e, p.to_owned()))?;
Cow::Owned(data)
},
AssetSource::Data(ref d) => {
Cow::Borrowed(d)
},
})
}
}

#[derive(Debug, Clone)]
pub struct Asset {
pub source_file: PathBuf,
pub source: AssetSource,
pub target_path: PathBuf,
pub chmod: u32,
is_built: bool,
}

impl Asset {
pub fn new(source_file: PathBuf, mut target_path: PathBuf, chmod: u32) -> Self {
pub fn new(source: AssetSource, mut target_path: PathBuf, chmod: u32) -> Self {
// target/release is treated as a magic alias for the actual target dir
// (which may be slightly different in practice)
// and assume everything in there is built by Cargo
let is_built = source_file.starts_with("target/release");
let is_built = source.path().map_or(false, |p| p.starts_with("target/release"));

if target_path.is_absolute() {
target_path = target_path.strip_prefix("/").expect("no root dir").to_owned();
}
// is_dir() is only for paths that exist
if target_path.to_string_lossy().ends_with('/') {
target_path = target_path.join(source_file.file_name().expect("source must be a file"));
let file_name = source.path().and_then(|p| p.file_name()).expect("source must be a file");
target_path = target_path.join(file_name);
}
Self {
source_file,
source,
target_path,
chmod,
is_built,
Expand All @@ -51,7 +96,7 @@ impl Asset {
}

fn is_dynamic_library(&self) -> bool {
self.source_file.file_name()
self.target_path.file_name()
.and_then(|f| f.to_str())
.map_or(false, |f| f.ends_with(DLL_SUFFIX))
}
Expand Down Expand Up @@ -161,7 +206,7 @@ impl Config {
for word in self.depends.split(',') {
let word = word.trim();
if word == "$auto" {
for bname in &self.all_binaries() {
for bname in self.all_binaries().into_iter().filter_map(|p| p.path()) {
match resolve(bname, &self.architecture, listener) {
Ok(bindeps) => for dep in bindeps {
deps.insert(dep);
Expand All @@ -182,7 +227,7 @@ impl Config {
// The file is autogenerated later
let path = self.path_in_deb("copyright");
self.assets.push(Asset::new(
path,
AssetSource::Path(path),
PathBuf::from("usr/share/doc").join(&self.name).join("copyright"),
0o644,
));
Expand All @@ -193,29 +238,29 @@ impl Config {
if self.changelog.is_some() {
let temp_path = self.path_in_deb("changelog.gz");
self.assets.push(Asset::new(
temp_path,
AssetSource::Path(temp_path),
PathBuf::from("usr/share/doc").join(&self.name).join("changelog.gz"),
0o644,
));
}
}

/// Executables AND dynamic libraries
fn all_binaries(&self) -> Vec<&Path> {
fn all_binaries(&self) -> Vec<&AssetSource> {
self.binaries(false)
}

/// Executables AND dynamic libraries, but only in `target/release`
pub(crate) fn built_binaries(&self) -> Vec<&Path> {
pub(crate) fn built_binaries(&self) -> Vec<&AssetSource> {
self.binaries(true)
}

fn binaries(&self, built_only: bool) -> Vec<&Path> {
fn binaries(&self, built_only: bool) -> Vec<&AssetSource> {
self.assets.iter().filter_map(|asset| {
// Assumes files in build dir which have executable flag set are binaries
if (!built_only || asset.is_built) &&
(asset.is_dynamic_library() || asset.is_executable()) {
Some(asset.source_file.as_path())
Some(&asset.source)
} else {
None
}
Expand Down Expand Up @@ -435,7 +480,7 @@ impl Cargo {
target_path.clone()
};
all_assets.push(Asset::new(
source_file,
AssetSource::Path(source_file),
target_file,
mode
));
Expand All @@ -448,15 +493,15 @@ impl Cargo {
.filter_map(|t| {
if t.crate_types.iter().any(|ty|ty=="bin") && t.kind.iter().any(|k|k=="bin") {
Some(Asset::new(
options.path_in_build(&t.name),
AssetSource::Path(options.path_in_build(&t.name)),
PathBuf::from("usr/bin").join(&t.name),
0o755,
))
} else if t.crate_types.iter().any(|ty|ty=="cdylib") && t.kind.iter().any(|k|k=="cdylib") {
// FIXME: std has constants for the host arch, but not for cross-compilation
let lib_name = format!("{}{}{}", DLL_PREFIX, t.name, DLL_SUFFIX);
Some(Asset::new(
options.path_in_build(&lib_name),
AssetSource::Path(options.path_in_build(&lib_name)),
PathBuf::from("usr/lib").join(lib_name),
0o644,
))
Expand All @@ -468,7 +513,7 @@ impl Cargo {
if let Some(readme) = readme {
let target_path = PathBuf::from("usr/share/doc").join(&self.package.name).join(readme);
implied_assets.push(Asset::new(
PathBuf::from(readme),
AssetSource::Path(PathBuf::from(readme)),
target_path,
0o644,
));
Expand Down Expand Up @@ -633,14 +678,14 @@ fn match_arm_arch() {
#[test]
fn assets() {
let a = Asset::new(
PathBuf::from("foo/bar"),
AssetSource::Path(PathBuf::from("foo/bar")),
PathBuf::from("baz/"),
0o644,
);
assert_eq!("baz/bar", a.target_path.to_str().unwrap());

let a = Asset::new(
PathBuf::from("foo/bar"),
AssetSource::Path(PathBuf::from("foo/bar")),
PathBuf::from("/baz/quz"),
0o644,
);
Expand Down

0 comments on commit dabd655

Please sign in to comment.