Skip to content

Commit

Permalink
Merge #55
Browse files Browse the repository at this point in the history
55: Various refactoring r=taiki-e a=taiki-e

Closes #50
Closes #10

Co-authored-by: Taiki Endo <te316e89@gmail.com>
  • Loading branch information
bors[bot] and taiki-e authored Aug 11, 2021
2 parents 6c6a2fb + e2b0de4 commit a405503
Show file tree
Hide file tree
Showing 63 changed files with 2,421 additions and 348 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com

## [Unreleased]

- You can now merge the coverages generated under different test conditions by using `--no-report` and `--no-run`.

```sh
cargo clean
cargo llvm-cov --no-report --features a
cargo llvm-cov --no-report --features b
cargo llvm-cov --no-run --lcov
```

- [Fix "Failed to load coverage" error when together used with trybuild.](https://github.com/taiki-e/cargo-llvm-cov/pull/49)

- [Recognize rustflags and rustdocflags set by config file.](https://github.com/taiki-e/cargo-llvm-cov/pull/52)
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ serde = { version = "1.0.103", features = ["derive"] }
serde_json = "1"
shell-escape = "0.1.5"
termcolor = "1.1"
toml = "0.5.2"
tracing = { version = "0.1.21", default-features = false, features = ["std"] }
tracing-subscriber = { version = "0.2.16", default-features = false, features = ["ansi", "env-filter"] }
walkdir = "2"
Expand All @@ -44,5 +43,7 @@ walkdir = "2"
lazy_static = "1.4"

[dev-dependencies]
easy-ext = "0.2"
fs-err = "2"
once_cell = "1"
tempfile = "3"
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,11 @@ OPTIONS:
--doctests
Including doc tests (unstable)

--no-report
Run tests, but don't generate coverage reports

--no-run
Compile, but don't run tests (unstable)
Generate coverage reports without running tests

--no-fail-fast
Run all tests regardless of failure
Expand Down Expand Up @@ -232,6 +235,15 @@ With lcov report (if `--output-path` is not specified, the report will be printe
cargo llvm-cov --lcov --output-path lcov.info
```

You can merge the coverages generated under different test conditions by using `--no-report` and `--no-run`.

```sh
cargo clean
cargo llvm-cov --no-report --features a
cargo llvm-cov --no-report --features b
cargo llvm-cov --no-run --lcov
```

### Continuous Integration

Here is an example of GitHub Actions workflow that uploads coverage to [Codecov].
Expand Down
89 changes: 67 additions & 22 deletions src/cargo.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,68 @@
use std::env;
use std::{
env,
ffi::{OsStr, OsString},
};

use anyhow::{format_err, Result};
use camino::Utf8Path;
use serde::Deserialize;

use crate::{
cli::{Args, Coloring},
context::EnvironmentVariables,
context::Env,
process::ProcessBuilder,
};

fn ver(key: &str) -> Result<Option<String>> {
match env::var(key) {
Ok(v) if v.is_empty() => Ok(None),
Ok(v) => Ok(Some(v)),
Err(env::VarError::NotPresent) => Ok(None),
Err(e) => Err(e.into()),
}
#[derive(Debug)]
pub(crate) struct Cargo {
path: OsString,
pub(crate) nightly: bool,
}

// =============================================================================
// Cargo manifest
// https://doc.rust-lang.org/nightly/cargo/reference/manifest.html
impl Cargo {
pub(crate) fn new(env: &Env, workspace_root: &Utf8Path) -> Result<Self> {
let mut path = env.cargo();
let version = cmd!(path, "version").dir(workspace_root).read()?;
let nightly = version.contains("-nightly") || version.contains("-dev");
if !nightly {
path = OsStr::new("cargo");
}

#[derive(Debug, Deserialize)]
pub(crate) struct Manifest {
pub(crate) package: Option<Package>,
Ok(Self { path: path.into(), nightly })
}

pub(crate) fn process(&self) -> ProcessBuilder {
let mut cmd = cmd!(&self.path);
if !self.nightly {
cmd.arg("+nightly");
}
cmd
}
}

// https://doc.rust-lang.org/nightly/cargo/reference/manifest.html#the-package-section
#[derive(Debug, Deserialize)]
pub(crate) struct Package {
pub(crate) name: String,
pub(crate) fn config(cargo: &Cargo, workspace_root: &Utf8Path) -> Result<Config> {
let s = cargo
.process()
.args(&["-Z", "unstable-options", "config", "get", "--format", "json"])
.env("RUSTC_BOOTSTRAP", "1")
.dir(workspace_root)
.stderr_capture()
.read()?;
let mut config: Config = serde_json::from_str(&s)?;
config.apply_env()?;
Ok(config)
}

pub(crate) fn locate_project() -> Result<String> {
cmd!("cargo", "locate-project", "--message-format", "plain").read()
}

// =============================================================================
// Cargo configuration
// https://doc.rust-lang.org/nightly/cargo/reference/config.html
//
// Refs:
// - https://doc.rust-lang.org/nightly/cargo/reference/config.html
// - https://github.com/rust-lang/cargo/issues/9301

#[derive(Debug, Default, Deserialize)]
pub(crate) struct Config {
Expand All @@ -46,7 +74,7 @@ pub(crate) struct Config {

impl Config {
// Apply configuration environment variables
pub(crate) fn apply_env(&mut self) -> Result<()> {
fn apply_env(&mut self) -> Result<()> {
// Environment variables are prefer over config values.
// https://doc.rust-lang.org/nightly/cargo/reference/config.html#environment-variables
if let Some(rustc) = ver("CARGO_BUILD_RUSTC")? {
Expand All @@ -58,6 +86,9 @@ impl Config {
if let Some(rustdocflags) = ver("CARGO_BUILD_RUSTDOCFLAGS")? {
self.build.rustdocflags = Some(StringOrArray::String(rustdocflags));
}
if let Some(target) = ver("CARGO_BUILD_TARGET")? {
self.build.target = Some(target);
}
if let Some(verbose) = ver("CARGO_TERM_VERBOSE")? {
self.term.verbose = Some(verbose.parse()?);
}
Expand All @@ -68,7 +99,7 @@ impl Config {
Ok(())
}

pub(crate) fn merge_to(self, args: &mut Args, env: &mut EnvironmentVariables) {
pub(crate) fn merge_to(self, args: &mut Args, env: &mut Env) {
// RUSTFLAGS environment variable is prefer over build.rustflags config value.
// https://doc.rust-lang.org/nightly/cargo/reference/config.html#buildrustflags
if env.rustflags.is_none() {
Expand All @@ -89,6 +120,9 @@ impl Config {
env.rustc = Some(rustc.into());
}
}
if args.target.is_none() {
args.target = self.build.target;
}
if args.verbose == 0 && self.term.verbose.unwrap_or(false) {
args.verbose = 1;
}
Expand All @@ -107,6 +141,8 @@ struct Build {
rustflags: Option<StringOrArray>,
// https://doc.rust-lang.org/nightly/cargo/reference/config.html#buildrustdocflags
rustdocflags: Option<StringOrArray>,
// https://doc.rust-lang.org/nightly/cargo/reference/config.html#buildtarget
target: Option<String>,
}

// https://doc.rust-lang.org/nightly/cargo/reference/config.html#term
Expand All @@ -133,3 +169,12 @@ impl StringOrArray {
}
}
}

fn ver(key: &str) -> Result<Option<String>> {
match env::var(key) {
Ok(v) if v.is_empty() => Ok(None),
Ok(v) => Ok(Some(v)),
Err(env::VarError::NotPresent) => Ok(None),
Err(e) => Err(e.into()),
}
}
57 changes: 16 additions & 41 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,10 @@
use std::{env, ffi::OsString};

use anyhow::{format_err, Result};
use anyhow::Result;
use camino::Utf8PathBuf;
use clap::{AppSettings, Clap};
use serde::Deserialize;

// Clap panics if you pass a non-utf8 value to an argument that expects a utf8
// value.
//
// clap 3.0.0-beta.2:
// thread 'main' panicked at 'unexpected invalid UTF-8 code point', $CARGO/clap-3.0.0-beta.2/src/parse/matches/arg_matches.rs:220:28
//
// clap 2.33.3:
// thread 'main' panicked at 'unexpected invalid UTF-8 code point', $CARGO/clap-2.33.3/src/args/arg_matches.rs:217:28
//
// Even if you store a value as OsString and pass to cargo as is, you will get
// the same panic on cargo side. e.g., `cargo check --manifest-path $'fo\x80o'`
fn handle_args(args: impl IntoIterator<Item = impl Into<OsString>>) -> Result<Vec<String>> {
// Adapted from https://github.com/rust-lang/rust/blob/3bc9dd0dd293ab82945e35888ed6d7ab802761ef/compiler/rustc_driver/src/lib.rs#L1365-L1375.
args.into_iter()
.enumerate()
.map(|(i, arg)| {
arg.into()
.into_string()
.map_err(|arg| format_err!("argument {} is not valid Unicode: {:?}", i, arg))
})
.collect()
}

pub(crate) fn from_args() -> Result<Args> {
let Opts::LlvmCov(args) = Opts::parse_from(handle_args(env::args_os())?);
let Opts::LlvmCov(args) = Opts::parse();
Ok(args)
}

Expand All @@ -44,6 +19,7 @@ const MAX_TERM_WIDTH: usize = 100;
bin_name = "cargo",
max_term_width = MAX_TERM_WIDTH,
setting = AppSettings::DeriveDisplayOrder,
setting = AppSettings::StrictUtf8,
setting = AppSettings::UnifiedHelpMessage,
)]
enum Opts {
Expand All @@ -57,6 +33,7 @@ enum Opts {
about = ABOUT,
max_term_width = MAX_TERM_WIDTH,
setting = AppSettings::DeriveDisplayOrder,
setting = AppSettings::StrictUtf8,
setting = AppSettings::UnifiedHelpMessage,
)]
pub(crate) struct Args {
Expand Down Expand Up @@ -128,16 +105,22 @@ pub(crate) struct Args {
// For debugging (unstable)
#[clap(long, hidden = true)]
pub(crate) disable_default_ignore_filename_regex: bool,
// For debugging (unstable)
#[clap(long, hidden = true)]
pub(crate) hide_instantiations: bool,

// https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/instrument-coverage.html#including-doc-tests
/// Including doc tests (unstable)
#[clap(long)]
pub(crate) doctests: bool,
/// Run tests, but don't generate coverage reports
#[clap(long, conflicts_with_all = &["no-run"])]
pub(crate) no_report: bool,

// =========================================================================
// `cargo test` options
// https://doc.rust-lang.org/nightly/cargo/commands/cargo-test.html
/// Compile, but don't run tests (unstable)
/// Generate coverage reports without running tests
#[clap(long)]
pub(crate) no_run: bool,
/// Run all tests regardless of failure
Expand Down Expand Up @@ -217,7 +200,9 @@ impl Args {
pub(crate) enum Subcommand {
// internal (unstable)
#[clap(
max_term_width = MAX_TERM_WIDTH,
setting = AppSettings::DeriveDisplayOrder,
setting = AppSettings::StrictUtf8,
setting = AppSettings::UnifiedHelpMessage,
setting = AppSettings::Hidden,
)]
Expand Down Expand Up @@ -250,12 +235,12 @@ mod tests {

use anyhow::Result;
use clap::IntoApp;
use fs_err as fs;
use tempfile::Builder;

use super::{Args, MAX_TERM_WIDTH};
use crate::fs;

// See handle_args function for more.
// https://github.com/clap-rs/clap/issues/751
#[cfg(unix)]
#[test]
fn non_utf8_arg() {
Expand All @@ -266,17 +251,7 @@ mod tests {
use super::Opts;

// `cargo llvm-cov -- $'fo\x80o'`
let res = panic::catch_unwind(|| {
drop(Opts::try_parse_from(&[
"cargo".as_ref(),
"llvm-cov".as_ref(),
"--".as_ref(),
OsStr::from_bytes(&[b'f', b'o', 0x80, b'o']),
]));
});
assert!(res.is_err());

super::handle_args(&[
Opts::try_parse_from(&[
"cargo".as_ref(),
"llvm-cov".as_ref(),
"--".as_ref(),
Expand Down
Loading

0 comments on commit a405503

Please sign in to comment.