From 0e5e39e63d9d4764ef8ee28901c8bd22cd002583 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Thu, 22 Sep 2022 11:12:59 +0200 Subject: [PATCH] remove need to build xtask from CI (#2634) * xtask: move coverage implementation to nox * move pytests from xtask to nox in ci * use --package instead of --manifest-path for tests * write github actions groups to sys.stderr --- .github/workflows/ci.yml | 4 +- noxfile.py | 139 ++++++++++++++++++++++++++++----------- xtask/src/cli.rs | 19 ------ xtask/src/llvm_cov.rs | 95 -------------------------- xtask/src/main.rs | 1 - 5 files changed, 103 insertions(+), 155 deletions(-) delete mode 100644 xtask/src/llvm_cov.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78e5f558..f59ea6d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -249,7 +249,7 @@ jobs: shell: bash run: | python -m pip install -U pip nox - cargo xtask test-py + nox -s test-py env: CARGO_TARGET_DIR: ${{ github.workspace }}/target @@ -344,7 +344,7 @@ jobs: - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - run: python -m pip install -U pip nox - - run: cargo xtask coverage --output-lcov coverage.lcov + - run: nox -s coverage - uses: codecov/codecov-action@v2 with: file: coverage.lcov diff --git a/noxfile.py b/noxfile.py index 7f7e9f8c..49c11c9f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,9 +1,11 @@ +import os import re import subprocess import sys import time from glob import glob from pathlib import Path +from typing import Any, Dict, List, Optional import nox @@ -11,24 +13,51 @@ nox.options.sessions = ["test", "clippy", "fmt"] @nox.session(venv_backend="none") -def test(session: nox.Session): +def test(session: nox.Session) -> None: test_rust(session) test_py(session) @nox.session(name="test-rust", venv_backend="none") def test_rust(session: nox.Session): - session.run("cargo", "test", external=True) - session.run("cargo", "test", "--features=abi3", external=True) - session.run("cargo", "test", "--features=full", external=True) - session.run("cargo", "test", "--features=abi3 full", external=True) + _run_cargo_test(session, package="pyo3-build-config") + _run_cargo_test(session, package="pyo3-macros-backend") + _run_cargo_test(session, package="pyo3-macros") + _run_cargo_test(session, package="pyo3-ffi") + + _run_cargo_test(session) + _run_cargo_test(session, features="abi3") + _run_cargo_test(session, features="full") + _run_cargo_test(session, features="abi3 full") @nox.session(name="test-py", venv_backend="none") -def test_py(session): - session.run("nox", "-f", "pytests/noxfile.py", external=True) +def test_py(session: nox.Session) -> None: + _run(session, "nox", "-f", "pytests/noxfile.py", external=True) for example in glob("examples/*/noxfile.py"): - session.run("nox", "-f", example, external=True) + _run(session, "nox", "-f", example, external=True) + + +@nox.session(venv_backend="none") +def coverage(session: nox.Session) -> None: + session.env.update(_get_coverage_env()) + _run(session, "cargo", "llvm-cov", "clean", "--workspace", external=True) + test(session) + _run( + session, + "cargo", + "llvm-cov", + "--package=pyo3", + "--package=pyo3-build-config", + "--package=pyo3-macros-backend", + "--package=pyo3-macros", + "--package=pyo3-ffi", + "report", + "--lcov", + "--output-path", + "coverage.lcov", + external=True, + ) @nox.session @@ -39,19 +68,20 @@ def fmt(session: nox.Session): @nox.session(name="fmt-rust", venv_backend="none") def fmt_rust(session: nox.Session): - session.run("cargo", "fmt", "--all", "--check", external=True) + _run(session, "cargo", "fmt", "--all", "--check", external=True) @nox.session(name="fmt-py") def fmt_py(session: nox.Session): session.install("black==22.3.0") - session.run("black", ".", "--check") + _run(session, "black", ".", "--check") @nox.session(venv_backend="none") def clippy(session: nox.Session) -> None: for feature_set in ["full", "abi3 full"]: - session.run( + _run( + session, "cargo", "clippy", f"--features={feature_set}", @@ -65,31 +95,15 @@ def clippy(session: nox.Session) -> None: @nox.session(venv_backend="none") def publish(session: nox.Session) -> None: - session.run( - "cargo", - "publish", - "--manifest-path", - "pyo3-build-config/Cargo.toml", - external=True, - ) + _run_cargo_publish(session, package="pyo3-build-config") time.sleep(10) - session.run( - "cargo", - "publish", - "--manifest-path", - "pyo3-macros-backend/Cargo.toml", - external=True, - ) + _run_cargo_publish(session, package="pyo3-macros-backend") time.sleep(10) - session.run( - "cargo", "publish", "--manifest-path", "pyo3-macros/Cargo.toml", external=True - ) + _run_cargo_publish(session, package="pyo3-macros") time.sleep(10) - session.run( - "cargo", "publish", "--manifest-path", "pyo3-ffi/Cargo.toml", external=True - ) + _run_cargo_publish(session, package="pyo3-ffi") time.sleep(10) - session.run("cargo", "publish", external=True) + _run_cargo_publish(session, package="pyo3") @nox.session(venv_backend="none") @@ -156,7 +170,8 @@ class EmscriptenInfo: @nox.session(name="build-emscripten", venv_backend="none") def build_emscripten(session: nox.Session): info = EmscriptenInfo() - session.run( + _run( + session, "make", "-C", str(info.emscripten_dir), @@ -192,20 +207,24 @@ def test_emscripten(session: nox.Session): ) session.env["CARGO_BUILD_TARGET"] = target session.env["PYO3_CROSS_LIB_DIR"] = pythonlibdir - session.run("rustup", "target", "add", target, "--toolchain", "stable") - session.run( - "bash", "-c", f"source {info.builddir/'emsdk/emsdk_env.sh'} && cargo test" + _run(session, "rustup", "target", "add", target, "--toolchain", "stable") + _run( + session, + "bash", + "-c", + f"source {info.builddir/'emsdk/emsdk_env.sh'} && cargo test", ) @nox.session(name="build-guide", venv_backend="none") def build_guide(session: nox.Session): - session.run("mdbook", "build", "-d", "../target/guide", "guide", *session.posargs) + _run(session, "mdbook", "build", "-d", "../target/guide", "guide", *session.posargs) @nox.session(name="address-sanitizer", venv_backend="none") def address_sanitizer(session: nox.Session): - session.run( + _run( + session, "cargo", "+nightly", "test", @@ -230,3 +249,47 @@ def _get_rust_target() -> str: _HOST_LINE_START = "host: " + + +def _get_coverage_env() -> Dict[str, str]: + env = {} + output = subprocess.check_output(["cargo", "llvm-cov", "show-env"], text=True) + + for line in output.strip().splitlines(): + (key, value) = line.split("=", maxsplit=1) + env[key] = value.strip('"') + + # Ensure that examples/ and pytests/ all build to the correct target directory to collect + # coverage artifacts. + env["CARGO_TARGET_DIR"] = env["CARGO_LLVM_COV_TARGET_DIR"] + + return env + + +def _run(session: nox.Session, *args: str, **kwargs: Any) -> None: + """Wrapper for _run(session, which creates nice groups on GitHub Actions.""" + if "GITHUB_ACTIONS" in os.environ: + # Insert ::group:: at the start of nox's command line output + print("::group::", end="", flush=True, file=sys.stderr) + session.run(*args, **kwargs) + if "GITHUB_ACTIONS" in os.environ: + print("::endgroup::", file=sys.stderr) + + +def _run_cargo_test( + session: nox.Session, + *, + package: Optional[str] = None, + features: Optional[str] = None, +) -> None: + command = ["cargo", "test"] + if package: + command.append(f"--package={package}") + if features: + command.append(f"--features={features}") + + _run(session, *command, external=True) + + +def _run_cargo_publish(session: nox.Session, *, package: str) -> None: + _run(session, "cargo", "publish", f"--package={package}", external=True) diff --git a/xtask/src/cli.rs b/xtask/src/cli.rs index 61d2a6e6..f873816a 100644 --- a/xtask/src/cli.rs +++ b/xtask/src/cli.rs @@ -17,8 +17,6 @@ pub enum Subcommand { Fmt, /// Runs `clippy`, denying all warnings. Clippy, - /// Runs `cargo llvm-cov` for the PyO3 codebase. - Coverage(CoverageOpts), /// Attempts to render the documentation. Doc(DocOpts), /// Runs various variations on `cargo test` @@ -33,21 +31,6 @@ impl Default for Subcommand { } } -#[derive(StructOpt)] -pub struct CoverageOpts { - /// Creates an lcov output file. - #[structopt(long, default_value = "lcov.info")] - pub output_lcov: String, -} - -impl Default for CoverageOpts { - fn default() -> Self { - Self { - output_lcov: String::from("lcov.info"), - } - } -} - #[derive(StructOpt)] pub struct DocOpts { /// Whether to run the docs using nightly rustdoc @@ -100,7 +83,6 @@ impl Subcommand { } else { Installed::warn_nox() }; - crate::llvm_cov::run(CoverageOpts::default())?; installed.assert()? } @@ -110,7 +92,6 @@ impl Subcommand { crate::fmt::python::run()?; } Subcommand::Clippy => crate::clippy::run()?, - Subcommand::Coverage(opts) => crate::llvm_cov::run(opts)?, Subcommand::TestPy => crate::pytests::run(None)?, Subcommand::Test => crate::test::run()?, }; diff --git a/xtask/src/llvm_cov.rs b/xtask/src/llvm_cov.rs deleted file mode 100644 index a7ff1f92..00000000 --- a/xtask/src/llvm_cov.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::cli; -use crate::cli::CoverageOpts; -use crate::utils::*; -use anyhow::{Context, Result}; -use std::{collections::HashMap, process::Command}; - -/// Runs `cargo llvm-cov` for the PyO3 codebase. -pub fn run(opts: CoverageOpts) -> Result<()> { - let env = get_coverage_env()?; - - cli::run( - Command::new("cargo") - .args(&["llvm-cov", "clean", "--workspace"]) - .envs(&env), - )?; - - cli::run( - Command::new("cargo") - .args(&["test", "--manifest-path", "pyo3-build-config/Cargo.toml"]) - .envs(&env), - )?; - cli::run( - Command::new("cargo") - .args(&["test", "--manifest-path", "pyo3-macros-backend/Cargo.toml"]) - .envs(&env), - )?; - cli::run( - Command::new("cargo") - .args(&["test", "--manifest-path", "pyo3-macros/Cargo.toml"]) - .envs(&env), - )?; - - cli::run(Command::new("cargo").arg("test").envs(&env))?; - cli::run( - Command::new("cargo") - .args(&["test", "--features", "abi3"]) - .envs(&env), - )?; - cli::run( - Command::new("cargo") - .args(&["test", "--features", "full"]) - .envs(&env), - )?; - cli::run( - Command::new("cargo") - .args(&["test", "--features", "abi3 full"]) - .envs(&env), - )?; - - crate::pytests::run(&env)?; - - cli::run( - llvm_cov_command(&["--no-run", "--lcov", "--output-path", &opts.output_lcov]).envs(&env), - )?; - - Ok(()) -} - -fn llvm_cov_command(args: &[&str]) -> Command { - let mut command = Command::new("cargo"); - command - .args(&[ - "llvm-cov", - "--package=pyo3", - "--package=pyo3-build-config", - "--package=pyo3-macros-backend", - "--package=pyo3-macros", - "--package=pyo3-ffi", - ]) - .args(args); - command -} - -fn get_coverage_env() -> Result> { - let mut env = HashMap::new(); - - let output = cli::run_with_output(&mut llvm_cov_command(&["show-env"])).context("Unable to run llvm-cov. If it is not installed, you can install it with `cargo install cargo-llvm-cov`.")?; - - let output = std::str::from_utf8(&output.stdout)?; - - for line in output.trim().split('\n') { - let (key, value) = split_once(line, '=') - .context("expected '=' in each line of output from llvm-cov show-env")?; - env.insert(key.to_owned(), value.trim_matches('"').to_owned()); - } - - // Ensure that examples/ and pytests/ all build to the correct target directory to collect - // coverage artifacts. - env.insert( - "CARGO_TARGET_DIR".to_owned(), - env.get("CARGO_LLVM_COV_TARGET_DIR").unwrap().to_owned(), - ); - - Ok(env) -} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index ab7afafa..42174268 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -5,7 +5,6 @@ pub mod cli; pub mod clippy; pub mod doc; pub mod fmt; -pub mod llvm_cov; pub mod pytests; pub mod test; pub mod utils;