Skip to content
Open
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
113 changes: 95 additions & 18 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use lazy_static::lazy_static;
use regex::Regex;
use std::default::Default;
use std::fmt::{self, Display};
use std::path::{Path, PathBuf};

/// basic pattern to match ssh style remote URLs
/// so that they can be fixed up
Expand Down Expand Up @@ -52,8 +53,15 @@ impl Display for GitPrefix {
}
}

/// converts a GIT URL to a Yocto GIT URL
pub fn git_to_yocto_git_url(url: &str, name: Option<&str>, prefix: GitPrefix) -> String {
/// converts a GIT URL to a Yocto GIT URL.
/// `subdir`, when set, tells bitbake to process only that sub directory of the
/// repository (used for git dependencies whose crate lives in a sub directory).
pub fn git_to_yocto_git_url(
url: &str,
name: Option<&str>,
prefix: GitPrefix,
subdir: Option<&str>,
) -> String {
// check if its a git@github.com:meta-rust/cargo-bitbake.git style URL
// and fix it up if it is
let fixed_url = if SSH_STYLE_REMOTE.is_match(url) {
Expand All @@ -77,10 +85,33 @@ pub fn git_to_yocto_git_url(url: &str, name: Option<&str>, prefix: GitPrefix) ->
// by default bitbake only look for SHAs and refs on the master branch.
let yocto_url = format!("{};nobranch=1", yocto_url);

if let Some(name) = name {
let yocto_url = if let Some(name) = name {
format!("{};name={};destsuffix={}", yocto_url, name, name)
} else {
yocto_url
};

if let Some(subdir) = subdir {
format!("{};subdir={}", yocto_url, subdir)
} else {
yocto_url
}
}

/// Determine the sub directory within a git checkout that holds the package
/// rooted at `package_dir`. Returns `None` when the package lives at the root
/// of its git repository, so that no `subdir` modifier is emitted.
pub fn git_repo_subdir(package_dir: &Path) -> Option<String> {
let repo = Repository::discover(package_dir).ok()?;
let workdir = repo.workdir()?;
let workdir = std::fs::canonicalize(workdir).unwrap_or_else(|_| workdir.to_path_buf());
let package_dir =
std::fs::canonicalize(package_dir).unwrap_or_else(|_| package_dir.to_path_buf());
let subdir = package_dir.strip_prefix(&workdir).ok()?;
if subdir.as_os_str().is_empty() {
None
} else {
Some(subdir.to_string_lossy().into_owned())
}
}

Expand All @@ -90,14 +121,40 @@ pub struct ProjectRepo {
pub branch: String,
pub rev: String,
pub tag: bool,
/// directory containing the package, relative to the root of the git
/// repository (empty when the package lives at the repository root)
pub cargo_src_dir: PathBuf,
/// the root of the git repository's working directory (empty when no git
/// repository could be found)
pub repo_dir: PathBuf,
}

impl ProjectRepo {
/// Attempts to guess at the upstream repo this project can be fetched from
pub fn new(gctx: &GlobalContext) -> CargoResult<Self> {
/// Attempts to guess at the upstream repo this project can be fetched from.
/// `package_dir` is the directory holding the package's `Cargo.toml`, used
/// to work out where the package sits within the git repository.
pub fn new(gctx: &GlobalContext, package_dir: &Path) -> CargoResult<Self> {
let repo = Repository::discover(gctx.cwd())
.context("Unable to determine git repo for this project")?;

// the git checkout (S = "${WORKDIR}/git") holds the whole repository,
// so the package may live in a sub directory of it that we need to
// record in CARGO_SRC_DIR
let repo_dir = repo
.workdir()
.map(|workdir| std::fs::canonicalize(workdir).unwrap_or_else(|_| workdir.to_path_buf()))
.unwrap_or_default();
let cargo_src_dir = if repo_dir.as_os_str().is_empty() {
PathBuf::new()
} else {
let package_dir =
std::fs::canonicalize(package_dir).unwrap_or_else(|_| package_dir.to_path_buf());
package_dir
.strip_prefix(&repo_dir)
.map(Path::to_path_buf)
.unwrap_or_default()
};

let remote = repo
.find_remote("origin")
.context("Unable to find remote 'origin' for this project")?;
Expand All @@ -114,7 +171,7 @@ impl ProjectRepo {
let uri = remote
.url()
.ok_or_else(|| anyhow!("No URL for remote 'origin'"))?;
let uri = git_to_yocto_git_url(uri, None, prefix);
let uri = git_to_yocto_git_url(uri, None, prefix, None);

let head = repo.head().context("Unable to find HEAD")?;
let branch = head
Expand All @@ -137,6 +194,8 @@ impl ProjectRepo {
branch: branch.to_string(),
rev: rev.to_string(),
tag: Self::rev_is_tag(&repo, &rev),
cargo_src_dir,
repo_dir,
})
}

Expand Down Expand Up @@ -165,31 +224,31 @@ mod test {
#[test]
fn remote_http() {
let repo = "http://github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git);
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git, None);
assert_eq!(url,
"git://github.com/rust-lang/cargo.git;protocol=http;nobranch=1;name=cargo;destsuffix=cargo");
}

#[test]
fn remote_https() {
let repo = "https://github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git);
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git, None);
assert_eq!(url,
"git://github.com/rust-lang/cargo.git;protocol=https;nobranch=1;name=cargo;destsuffix=cargo");
}

#[test]
fn remote_ssh() {
let repo = "git@github.com:rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git);
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git, None);
assert_eq!(url,
"git://git@github.com/rust-lang/cargo.git;protocol=ssh;nobranch=1;name=cargo;destsuffix=cargo");
}

#[test]
fn remote_http_nosuffix() {
let repo = "http://github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git);
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git, None);
assert_eq!(
url,
"git://github.com/rust-lang/cargo.git;protocol=http;nobranch=1"
Expand All @@ -199,7 +258,7 @@ mod test {
#[test]
fn remote_https_nosuffix() {
let repo = "https://github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git);
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git, None);
assert_eq!(
url,
"git://github.com/rust-lang/cargo.git;protocol=https;nobranch=1"
Expand All @@ -209,7 +268,7 @@ mod test {
#[test]
fn remote_ssh_nosuffix() {
let repo = "git@github.com:rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git);
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git, None);
assert_eq!(
url,
"git://git@github.com/rust-lang/cargo.git;protocol=ssh;nobranch=1"
Expand All @@ -219,39 +278,39 @@ mod test {
#[test]
fn cargo_http() {
let repo = "http://github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git);
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git, None);
assert_eq!(url,
"git://github.com/rust-lang/cargo.git;protocol=http;nobranch=1;name=cargo;destsuffix=cargo");
}

#[test]
fn cargo_https() {
let repo = "https://github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git);
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git, None);
assert_eq!(url,
"git://github.com/rust-lang/cargo.git;protocol=https;nobranch=1;name=cargo;destsuffix=cargo");
}

#[test]
fn cargo_ssh() {
let repo = "ssh://git@github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git);
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git, None);
assert_eq!(url,
"git://git@github.com/rust-lang/cargo.git;protocol=ssh;nobranch=1;name=cargo;destsuffix=cargo");
}

#[test]
fn cargo_ssh_with_port() {
let repo = "ssh://git@git.example.com:222/foo/bar.git";
let url = git_to_yocto_git_url(repo, Some("bar"), GitPrefix::Git);
let url = git_to_yocto_git_url(repo, Some("bar"), GitPrefix::Git, None);
assert_eq!(url,
"git://git@git.example.com:222/foo/bar.git;protocol=ssh;nobranch=1;name=bar;destsuffix=bar");
}

#[test]
fn cargo_ssh_with_port_nosuffix() {
let repo = "ssh://git@git.example.com:222/foo/bar.git";
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git);
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git, None);
assert_eq!(
url,
"git://git@git.example.com:222/foo/bar.git;protocol=ssh;nobranch=1"
Expand All @@ -261,8 +320,26 @@ mod test {
#[test]
fn remote_ssh_with_submodules() {
let repo = "git@github.com:rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::GitSubmodule);
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::GitSubmodule, None);
assert_eq!(url,
"gitsm://git@github.com/rust-lang/cargo.git;protocol=ssh;nobranch=1;name=cargo;destsuffix=cargo");
}

#[test]
fn cargo_https_with_subdir() {
let repo = "https://github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, Some("cargo"), GitPrefix::Git, Some("crates/cargo"));
assert_eq!(url,
"git://github.com/rust-lang/cargo.git;protocol=https;nobranch=1;name=cargo;destsuffix=cargo;subdir=crates/cargo");
}

#[test]
fn cargo_https_nosuffix_with_subdir() {
let repo = "https://github.com/rust-lang/cargo.git";
let url = git_to_yocto_git_url(repo, None, GitPrefix::Git, Some("crates/cargo"));
assert_eq!(
url,
"git://github.com/rust-lang/cargo.git;protocol=https;nobranch=1;subdir=crates/cargo"
);
}
}
76 changes: 39 additions & 37 deletions src/license.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ fn file_md5<P: AsRef<Path>>(license_file: P) -> Result<String, io::Error> {
Ok(format!("{:x}", context.compute()))
}

/// Given the top level of the crate at `crate_root`, attempt to find
/// the license file based on the name of the license in `license_name`.
pub fn file(crate_root: &Path, rel_dir: &Path, license_name: &str, single_license: bool) -> String {
/// Attempt to find the license file based on the name of the license in
/// `license_name`. The crate directory (`crate_root`, emitted relative to the
/// git checkout as `rel_dir`) is searched first; failing that the git
/// repository root (`repo_root`) is searched, which catches a shared license
/// at the top of a monorepo. Paths are emitted relative to S = "${WORKDIR}/git".
pub fn file(
crate_root: &Path,
rel_dir: &Path,
repo_root: &Path,
license_name: &str,
single_license: bool,
) -> String {
// CLOSED is a special case license (case sensitive) per
// http://www.yoctoproject.org/docs/2.3.2/mega-manual/mega-manual.html#sdk-license-detection
// that means this is closed source and there is no license
Expand All @@ -35,42 +44,35 @@ pub fn file(crate_root: &Path, rel_dir: &Path, license_name: &str, single_licens
return "".into();
}

// if the license exists at the top level then
// return the right URL to it. try to handle the special
// case license path we support as well
// candidate file names to look for, in priority order. We also handle the
// special case license path we support as well as a plain LICENSE file.
let special_name = format!("LICENSE-{}", license_name);
let lic_path = Path::new(license_name);
let spec_path = Path::new(&special_name);
let simple_path = Path::new("LICENSE");
let mut candidates: Vec<&Path> = vec![Path::new(license_name), Path::new(&special_name)];
if single_license {
candidates.push(Path::new("LICENSE"));
}

let lic_abs_path = crate_root.join(lic_path);
let spec_abs_path = crate_root.join(spec_path);
let simple_abs_path = crate_root.join(simple_path);
// where to look, with the prefix to emit for files found there. The crate
// directory wins over the repo root so a crate-local license takes priority.
let mut search: Vec<(&Path, &Path)> = vec![(crate_root, rel_dir)];
if !repo_root.as_os_str().is_empty() && repo_root != crate_root {
search.push((repo_root, Path::new("")));
}

if lic_abs_path.exists() {
let md5sum = file_md5(lic_abs_path).unwrap_or_else(|_| String::from("generateme"));
format!(
"file://{};md5={} \\\n",
rel_dir.join(lic_path).display(),
md5sum
)
} else if spec_abs_path.exists() {
// the special case
let md5sum = file_md5(spec_abs_path).unwrap_or_else(|_| String::from("generateme"));
format!(
"file://{};md5={} \\\n",
rel_dir.join(spec_path).display(),
md5sum
)
} else if simple_abs_path.exists() && single_license {
let md5sum = file_md5(simple_abs_path).unwrap_or_else(|_| String::from("generateme"));
format!(
"file://{};md5={} \\\n",
rel_dir.join(simple_path).display(),
md5sum
)
} else {
// fall through
format!("file://{};md5=generateme \\\n", license_name)
for (dir, prefix) in search {
for cand in &candidates {
let abs_path = dir.join(cand);
if abs_path.exists() {
let md5sum = file_md5(&abs_path).unwrap_or_else(|_| String::from("generateme"));
return format!(
"file://{};md5={} \\\n",
prefix.join(cand).display(),
md5sum
);
}
}
}

// fall through
format!("file://{};md5=generateme \\\n", license_name)
}
Loading