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

misc changes #7

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
@@ -1,2 +1,4 @@
target
*.rs.bk
*.ebuild
!tests/*.ebuild
1 change: 1 addition & 0 deletions .travis.yml
@@ -1,5 +1,6 @@
sudo: false
language: rust
cache: cargo
dist: trusty
rust:
- nightly
Expand Down
1,222 changes: 856 additions & 366 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions Cargo.toml
Expand Up @@ -13,11 +13,15 @@ description = """
Generates an ebuild for a package using the in-tree eclasses.
"""

[badges]
travis-ci = { repository = "cardoe/cargo-ebuild" }
[badges.travis-ci]
repository = "cardoe/cargo-ebuild"

[dependencies]
cargo = "^0.21"
serde = "1"
serde_derive = "1"
cargo = "^0.27"
time = "^0.1"
quicli = "^0.2"
human-panic = "^0.4"
failure = "^0.1"

[dev-dependencies]
assert_cli = "^0.6"
77 changes: 77 additions & 0 deletions src/cargo_utils.rs
@@ -0,0 +1,77 @@
use cargo::core::registry::PackageRegistry;
use cargo::core::resolver::Method;
use cargo::core::{Package, PackageSet, Resolve, Workspace};
use cargo::ops;
use cargo::util::{important_paths, CargoResult};
use cargo::Config;
use std::path::PathBuf;

/// Represents the package we are trying to generate a recipe for
pub struct PackageInfo<'cfg> {
cfg: &'cfg Config,
current_manifest: PathBuf,
ws: Workspace<'cfg>,
}

impl<'cfg> PackageInfo<'cfg> {
/// creates our package info from the config and the manifest_path,
/// which may not be provided
pub fn new(config: &Config) -> CargoResult<PackageInfo> {
let root = important_paths::find_root_manifest_for_wd(&config.cwd())?;
let ws = Workspace::new(&root, config).unwrap();
Ok(PackageInfo {
cfg: config,
current_manifest: root,
ws,
})
}

/// provides the current package we are working with
pub fn package(&self) -> CargoResult<&Package> {
self.ws.current()
}

/// Generates a package registry by using the Cargo.lock or
/// creating one as necessary
pub fn registry(&self) -> CargoResult<PackageRegistry<'cfg>> {
let mut registry = PackageRegistry::new(self.cfg)?;
let package = self.package()?;
registry.add_sources(&[package.package_id().source_id().clone()])?;
Ok(registry)
}

/// Resolve the packages necessary for the workspace
pub fn resolve(&self) -> CargoResult<(PackageSet<'cfg>, Resolve)> {
// build up our registry
let mut registry = self.registry()?;

// resolve our dependencies
let (packages, resolve) = ops::resolve_ws(&self.ws)?;

// resolve with all features set so we ensure we get all of the depends downloaded
let resolve = ops::resolve_with_previous(
&mut registry,
&self.ws,
Method::Everything, // resolve it all
Some(&resolve), // previous
None, // don't avoid any
&[], // specs
true, // register patches
false, // warn
)?;

Ok((packages, resolve))
}

/// packages that are part of a workspace are a sub directory from the
/// top level which we need to record, this provides us with that
/// relative directory
pub fn rel_dir(&self) -> CargoResult<PathBuf> {
// this is the top level of the workspace
let root = self.ws.root().to_path_buf();
// path where our current package's Cargo.toml lives
let cwd = self.current_manifest.parent().unwrap();

Ok(cwd.strip_prefix(&root).map(|p| p.to_path_buf()).unwrap())
}
}
75 changes: 75 additions & 0 deletions src/ebuild.rs
@@ -0,0 +1,75 @@
//!
//! Ebuild management
//!
//! This module provides some very basic abstraction for getting the hands
//! dirty on gentoo's ebuild.
//!
//!

use std::io;

/// Hold all the usefull configurion of an ebuild
#[derive(Debug, Default)]
pub struct Ebuild {
name: String,
description: String,
homepage: String,
license: String,
crates: String,
version: String,
provider: String,
provider_version: String,
this_year: i32,
}

impl Ebuild {
/// Ebuild information
pub fn new(
name: &str,
description: &str,
homepage: &str,
license: &str,
crates: &str,
version: &str,
provider: &str,
provider_version: &str,
year: i32,
) -> Ebuild {
Ebuild {
name: name.to_string(),
description: description.to_string(),
homepage: homepage.to_string(),
license: license.to_string(),
crates: crates.to_string(),
version: version.to_string(),
provider: provider.to_string(),
provider_version: provider_version.to_string(),
this_year: year,
}
}

/// Get ebuild's name
pub fn name(&self) -> String {
self.name.clone()
}

/// Get ebuild's version
pub fn version(&self) -> String {
self.version.clone()
}

/// Write the ebuild from the template to a buffer
pub fn write(&self, file: &mut io::Write) -> io::Result<()> {
writeln!(
file,
include_str!("ebuild.template"),
description = self.description,
homepage = self.homepage,
license = self.license,
crates = self.crates,
provider = self.provider,
provider_version = self.provider_version,
this_year = self.this_year
)
}
}
2 changes: 1 addition & 1 deletion src/ebuild.template
@@ -1,7 +1,7 @@
# Copyright 2017-{this_year} Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

# Auto-Generated by cargo-ebuild {cargo_ebuild_ver}
# Auto-Generated by {provider} {provider_version}

EAPI=6

Expand Down
169 changes: 169 additions & 0 deletions src/lib.rs
@@ -0,0 +1,169 @@
extern crate cargo;
extern crate quicli;
extern crate time;
#[macro_use]
extern crate failure;

pub mod cargo_utils;
pub mod ebuild;

use quicli::prelude::*;

#[derive(StructOpt, Debug)]
pub struct Cli {
#[structopt(subcommand)] // the real cargo-ebuild commands
pub cmd: Command,

/// Prevent cargo-ebuild and cargo to use stdout
#[structopt(short = "q", long = "quiet")]
pub quiet: bool,

/// Verbose mode (-v, -vv, -vvv, -vvvv)
#[structopt(short = "v", long = "verbose", parse(from_occurrences))]
pub verbosity: u8,
}

#[derive(Debug, StructOpt)]
pub enum Command {
#[structopt(name = "build")]
/// Build an ebuild file from a cargo project
Build {
#[structopt(long = "unstable-flags", short = "Z")]
unstable_flags: Vec<String>,
},
}

use cargo::Config;
use cargo_utils::*;
use ebuild::*;
use failure::err_msg;
use failure::Error;
use std::result;

/// Quite huge all-in-one func that generates and Ebuild from some cli
/// cli configuration
pub fn run_cargo_ebuild(cli: Cli) -> result::Result<Ebuild, Error> {
// Here will be the match of the commands, now just example
let flags = match cli.cmd {
Command::Build { unstable_flags } => unstable_flags,
};

// build the crate URIs
let mut config = Config::default()?;

// setup cargo configuration
config.configure(
u32::from(cli.verbosity),
Some(cli.quiet),
&None, // color
false, // frozen
false, // locked
&flags,
)?;

// Build up data about the package we are attempting to generate a recipe for
let md = PackageInfo::new(&config)?;

// Our current package
let package = md.package()?;

// Look for Cargo.toml parent
let _crate_root = package
.manifest_path()
.parent()
.ok_or_else(|| err_msg(format_err!("Cargo.toml must have a parent")))?;

// Resolve all dependencies (generate or use Cargo.lock as necessary)
let resolve = md.resolve()?;

let mut git_crates = Vec::new();

// build the crates the package needs
let mut crates = resolve
.1
.iter()
.filter_map(|pkg| {
// get the source info for this package
let src_id = pkg.source_id();
if src_id.is_registry() {
// this package appears in a crate registry
Some(format!("{}-{}\n", pkg.name(), pkg.version()))
} else if src_id.is_path() {
// we don't want to spit out path based
// entries since they're within the crate
// we are packaging
None
} else if src_id.is_git() {
use cargo::sources::GitSource;

match GitSource::new(&src_id, &config) {
Ok(git_src) => git_crates.push(git_src.url().to_string()),
Err(err) => error!(
"Not able to find git source for {} caused by {}",
pkg.name(),
err
),
};

None
} else if src_id.is_alt_registry() {
Some(format!("{} \\\n", src_id.url().to_string()))
} else {
warn!("There is no method to fetch package {}", pkg.name());
None
}
})
.collect::<Vec<String>>();

// sort the crates
crates.sort();

// root package metadata
let metadata = package.manifest().metadata();

// package description is used as BitBake summary
let summary = metadata.description.as_ref().map_or_else(
|| {
debug!("No package.description set in your Cargo.toml, using package.name");
package.name().to_string()
},
|s| s.trim().to_string(),
);

// package homepage (or source code location)
let homepage = metadata
.homepage
.as_ref()
.map_or_else(
|| {
debug!("No package.homepage set in your Cargo.toml, trying package.repository");
metadata.repository.as_ref().ok_or_else(|| {
err_msg(format_err!("No package.repository set in your Cargo.toml"))
})
},
|s| Ok(s),
)?
.trim();

// package version
let version = package.manifest().version().to_string();

let license = metadata
.license
.as_ref()
.cloned()
.unwrap_or_else(|| String::from("unknown license"));

// write the contents out
Ok(Ebuild::new(
&package.name().to_string(),
summary.trim(),
homepage.trim(),
license.trim(),
&crates.join(""),
&version,
"cargo-ebuild",
env!("CARGO_PKG_VERSION"),
1900 + time::now().tm_year,
))
}