Skip to content

Commit 8c08c1d

Browse files
committed
Use OsString and paths in cargo-marker
1 parent 60b49bc commit 8c08c1d

File tree

5 files changed

+147
-107
lines changed

5 files changed

+147
-107
lines changed

cargo-marker/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2021"
55
license = "MIT OR Apache-2.0"
66

77
[dependencies]
8-
clap = "4.0.26"
8+
clap = {version = "4.0.26", features = ["string"]}
99
once_cell = "1.16.0"
1010

1111
[features]

cargo-marker/src/cli.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clap::{Arg, ArgAction, Command};
1+
use clap::{builder::ValueParser, Arg, ArgAction, Command};
22

33
use crate::VERSION;
44

@@ -57,6 +57,7 @@ fn check_command_args() -> impl IntoIterator<Item = impl Into<Arg>> {
5757
.short('l')
5858
.long("lints")
5959
.num_args(1..)
60+
.value_parser(ValueParser::os_string())
6061
.help("Defines a set of lints crates that should be used"),
6162
]
6263
}

cargo-marker/src/driver.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//! run `cargo install` for the driver with a specific toolchain. The version and
1313
//! toolchain are hardcoded in this crate.
1414
15-
use std::{path::PathBuf, process::Command};
15+
use std::{ffi::OsString, path::PathBuf, process::Command};
1616

1717
use once_cell::sync::Lazy;
1818

@@ -33,6 +33,13 @@ struct RustcDriverInfo {
3333
api_version: String,
3434
}
3535

36+
pub fn print_driver_version() {
37+
println!(
38+
"rustc driver version: {} (toolchain: {}, api: {})",
39+
DEFAULT_DRIVER_INFO.version, DEFAULT_DRIVER_INFO.toolchain, DEFAULT_DRIVER_INFO.api_version
40+
);
41+
}
42+
3643
/// This tries to install the rustc driver specified in [`DEFAULT_DRIVER_INFO`].
3744
pub fn install_driver(verbose: bool) -> Result<(), ExitStatus> {
3845
// The toolchain, driver version and api version should ideally be configurable.
@@ -129,6 +136,34 @@ fn check_driver(verbose: bool, print_advice: bool) -> Result<(), ExitStatus> {
129136
}
130137
}
131138

139+
pub fn run_driver(
140+
env: Vec<(OsString, OsString)>,
141+
cargo_args: impl Iterator<Item = String>,
142+
verbose: bool,
143+
) -> Result<(), ExitStatus> {
144+
check_driver(verbose, true)?;
145+
println!();
146+
println!("Start linting:");
147+
148+
let mut cmd = Command::new("cargo");
149+
cmd.envs(env).arg("check").args(cargo_args);
150+
if verbose {
151+
cmd.arg("--verbose");
152+
}
153+
154+
let exit_status = cmd
155+
.spawn()
156+
.expect("could not run cargo")
157+
.wait()
158+
.expect("failed to wait for cargo?");
159+
160+
if exit_status.success() {
161+
Ok(())
162+
} else {
163+
Err(ExitStatus::MarkerCheckFailed)
164+
}
165+
}
166+
132167
pub fn get_driver_path() -> PathBuf {
133168
#[allow(unused_mut)]
134169
let mut path = std::env::current_exe()

cargo-marker/src/lints.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,63 @@
1+
use std::{
2+
ffi::OsStr,
3+
path::{Path, PathBuf},
4+
process::Command,
5+
};
16

7+
use crate::ExitStatus;
8+
9+
/// This creates a debug build for a local crate. The path of the build library
10+
/// will be returned, if the operation was successful.
11+
pub fn build_local_lint_crate(crate_dir: &Path, target_dir: &Path, verbose: bool) -> Result<PathBuf, ExitStatus> {
12+
if !crate_dir.exists() {
13+
eprintln!("The given lint can't be found, searched at: `{}`", crate_dir.display());
14+
return Err(ExitStatus::LintCrateNotFound);
15+
}
16+
17+
// Compile the lint crate
18+
let mut cmd = Command::new("cargo");
19+
if verbose {
20+
cmd.arg("--verbose");
21+
}
22+
let exit_status = cmd
23+
.current_dir(std::fs::canonicalize(crate_dir).unwrap())
24+
.args(["build", "--lib", "--target-dir"])
25+
.arg(target_dir.as_os_str())
26+
.env("RUSTFLAGS", "--cap-lints=allow")
27+
.spawn()
28+
.expect("could not run cargo")
29+
.wait()
30+
.expect("failed to wait for cargo?");
31+
32+
if !exit_status.success() {
33+
return Err(ExitStatus::LintCrateBuildFail);
34+
}
35+
36+
// Find the final binary and return the string
37+
#[cfg(any(target_os = "linux", target_os = "macos"))]
38+
let lib_file_prefix = "lib";
39+
#[cfg(target_os = "windows")]
40+
let lib_file_prefix = "";
41+
42+
// FIXME: currently this expect, that the lib name is the same as the crate dir.
43+
// See marker#60
44+
let file_name = format!(
45+
"{lib_file_prefix}{}",
46+
crate_dir.file_name().and_then(OsStr::to_str).unwrap_or_default()
47+
);
48+
// Here `debug` is attached as the crate is build with the `cargo build` command
49+
let mut krate_path = target_dir.join("debug").join(file_name);
50+
51+
#[cfg(target_os = "linux")]
52+
krate_path.set_extension("so");
53+
#[cfg(target_os = "macos")]
54+
krate_path.set_extension("dylib");
55+
#[cfg(target_os = "windows")]
56+
krate_path.set_extension("dll");
57+
58+
if !krate_path.exists() && !krate_path.is_file() {
59+
Err(ExitStatus::LintCrateLibNotFound)
60+
} else {
61+
Ok(krate_path)
62+
}
63+
}

cargo-marker/src/main.rs

Lines changed: 46 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,25 @@ mod driver;
77
mod lints;
88

99
use std::{
10-
ffi::OsStr,
10+
ffi::{OsStr, OsString},
1111
fs::create_dir_all,
12+
io::{self, Write},
13+
os::unix::prelude::OsStrExt,
1214
path::{Path, PathBuf},
13-
process::{exit, Command},
15+
process::exit,
1416
};
1517

1618
use cli::get_clap_config;
17-
use driver::get_driver_path;
19+
use driver::{get_driver_path, run_driver};
20+
use lints::build_local_lint_crate;
1821
use once_cell::sync::Lazy;
1922

23+
use crate::driver::print_driver_version;
24+
2025
const CARGO_ARGS_SEPARATOR: &str = "--";
2126
const VERSION: &str = concat!("cargo-marker ", env!("CARGO_PKG_VERSION"));
2227
const LINT_KRATES_BASE_DIR: &str = "./target/marker";
23-
static LINT_KRATES_TARGET_DIR: Lazy<String> = Lazy::new(|| prepare_lint_build_dir("build", "target"));
24-
static LINT_KRATES_OUT_DIR: Lazy<String> = Lazy::new(|| prepare_lint_build_dir("lints", "out"));
28+
static MARKER_LINT_DIR: Lazy<String> = Lazy::new(|| prepare_lint_build_dir("marker", "marker"));
2529

2630
#[derive(Debug)]
2731
pub enum ExitStatus {
@@ -35,6 +39,12 @@ pub enum ExitStatus {
3539
DriverInstallationFailed = 300,
3640
/// A general collection status, for failures originating from the driver
3741
DriverFailed = 400,
42+
/// The lint crate build failed for some reason
43+
LintCrateBuildFail = 500,
44+
/// Lint crate could not be found
45+
LintCrateNotFound = 501,
46+
/// The lint crate has been build, but the resulting binary could not be found.
47+
LintCrateLibNotFound = 502,
3848
/// Check failed
3949
MarkerCheckFailed = 1000,
4050
}
@@ -71,7 +81,7 @@ fn main() -> Result<(), ExitStatus> {
7181
let verbose = matches.get_flag("verbose");
7282

7383
if matches.get_flag("version") {
74-
print_version();
84+
print_version(verbose);
7585
return Ok(());
7686
}
7787

@@ -85,14 +95,15 @@ fn main() -> Result<(), ExitStatus> {
8595

8696
fn run_check(matches: &clap::ArgMatches, verbose: bool) -> Result<(), ExitStatus> {
8797
let mut lint_crates = vec![];
88-
if let Some(cmd_lint_crates) = matches.get_many::<String>("lints") {
98+
if let Some(cmd_lint_crates) = matches.get_many::<OsString>("lints") {
8999
println!();
90100
println!("Compiling Lints:");
91101
lint_crates.reserve(cmd_lint_crates.len());
102+
let target_dir = Path::new(&*MARKER_LINT_DIR);
92103
for krate in cmd_lint_crates {
93-
if let Ok(krate_dir) = prepare_lint_crate(krate, verbose) {
94-
lint_crates.push(krate_dir);
95-
}
104+
let src_dir = PathBuf::from(krate);
105+
let crate_file = build_local_lint_crate(src_dir.as_path(), target_dir, verbose)?;
106+
lint_crates.push(crate_file.as_os_str().to_os_string());
96107
}
97108
}
98109

@@ -101,115 +112,46 @@ fn run_check(matches: &clap::ArgMatches, verbose: bool) -> Result<(), ExitStatus
101112
exit(-1);
102113
}
103114

104-
if lint_crates.iter().any(|path| path.contains(';')) {
115+
if lint_crates.iter().any(|path| path.to_string_lossy().contains(';')) {
105116
eprintln!("The absolute paths of lint crates are not allowed to contain a `;`");
106117
exit(-1);
107118
}
108119

109-
let driver_path = get_driver_path();
110-
let marker_crates_env = lint_crates.join(";");
120+
#[rustfmt::skip]
121+
let env = vec![
122+
(OsString::from("RUSTC_WORKSPACE_WRAPPER"), get_driver_path().as_os_str().to_os_string()),
123+
(OsString::from("MARKER_LINT_CRATES"), lint_crates.join(OsStr::new(";")))
124+
];
111125
if matches.get_flag("test-setup") {
112-
println!("env:RUSTC_WORKSPACE_WRAPPER={}", driver_path.display());
113-
println!("env:MARKER_LINT_CRATES={marker_crates_env}");
126+
print_env(env).unwrap();
114127
Ok(())
115128
} else {
116-
run_driver(&driver_path, &marker_crates_env)
129+
let cargo_args = std::env::args().skip_while(|c| c != CARGO_ARGS_SEPARATOR).skip(1);
130+
run_driver(env, cargo_args, verbose)
117131
}
118132
}
119133

120-
fn print_version() {
134+
fn print_version(verbose: bool) {
121135
println!("cargo-marker version: {}", env!("CARGO_PKG_VERSION"));
122-
}
123-
124-
fn run_driver(driver_path: &PathBuf, lint_crates: &str) -> Result<(), ExitStatus> {
125-
println!();
126-
println!("Start linting:");
127-
128-
let mut cmd = Command::new("cargo");
129-
let cargo_args = std::env::args().skip_while(|c| c != CARGO_ARGS_SEPARATOR).skip(1);
130-
cmd.env("RUSTC_WORKSPACE_WRAPPER", driver_path)
131-
.env("MARKER_LINT_CRATES", lint_crates)
132-
.arg("check")
133-
.args(cargo_args);
134-
135-
let exit_status = cmd
136-
.spawn()
137-
.expect("could not run cargo")
138-
.wait()
139-
.expect("failed to wait for cargo?");
140136

141-
if exit_status.success() {
142-
Ok(())
143-
} else {
144-
Err(ExitStatus::MarkerCheckFailed)
137+
if verbose {
138+
print_driver_version();
145139
}
146140
}
147141

148-
/// This function ensures that the given crate is compiled as a library and
149-
/// returns the compiled library path if everything was successful. Otherwise
150-
/// it'll issue a warning and return `Err`
151-
fn prepare_lint_crate(krate: &str, verbose: bool) -> Result<String, ()> {
152-
let path = Path::new(krate);
153-
if !path.exists() {
154-
eprintln!("The given lint can't be found, searched at: `{}`", path.display());
155-
return Err(());
156-
}
157-
158-
let mut cmd = cargo_command(verbose);
159-
let exit_status = cmd
160-
.current_dir(std::fs::canonicalize(path).unwrap())
161-
.args([
162-
"build",
163-
"--lib",
164-
"--target-dir",
165-
&*LINT_KRATES_TARGET_DIR,
166-
"-Z",
167-
"unstable-options",
168-
"--out-dir",
169-
&*LINT_KRATES_OUT_DIR,
170-
])
171-
.env("RUSTFLAGS", "--cap-lints=allow")
172-
.spawn()
173-
.expect("could not run cargo")
174-
.wait()
175-
.expect("failed to wait for cargo?");
176-
177-
if !exit_status.success() {
178-
return Err(());
142+
fn print_env(env: Vec<(OsString, OsString)>) -> io::Result<()> {
143+
// This directly uses the lock, to print the os strings correctly, the
144+
// UTF-8 conversion could otherwise fail or be incomplete. This would be
145+
// so much easier if everyone would follow the "UTF-8 Everywhere Manifesto"
146+
let mut lock = io::stdout().lock();
147+
148+
for (name, value) in env {
149+
write!(lock, "env:")?;
150+
lock.write_all(name.as_bytes())?;
151+
write!(lock, "=")?;
152+
lock.write_all(value.as_bytes())?;
153+
writeln!(lock)?;
179154
}
180155

181-
#[cfg(any(target_os = "linux", target_os = "macos"))]
182-
let lib_file_prefix = "lib";
183-
#[cfg(target_os = "windows")]
184-
let lib_file_prefix = "";
185-
186-
// FIXME: currently this expect, that the lib name is the same as the crate dir.
187-
let file_name = format!(
188-
"{lib_file_prefix}{}",
189-
path.file_name().and_then(OsStr::to_str).unwrap_or_default()
190-
);
191-
let mut krate_path = Path::new(&*LINT_KRATES_OUT_DIR).join(file_name);
192-
193-
#[cfg(target_os = "linux")]
194-
krate_path.set_extension("so");
195-
#[cfg(target_os = "macos")]
196-
krate_path.set_extension("dylib");
197-
#[cfg(target_os = "windows")]
198-
krate_path.set_extension("dll");
199-
200-
Ok(krate_path.display().to_string())
201-
}
202-
203-
fn cargo_command(verbose: bool) -> Command {
204-
// Here we want to use the normal cargo command, to go through the rustup
205-
// cargo executable and with that, set the required toolchain version.
206-
// This will add a slight overhead to each cargo call. This feels a bit
207-
// unavoidable, until marker is delivered as part of the toolchain. Let's
208-
// hope that day will happen!
209-
let mut cmd = Command::new("cargo");
210-
211-
if verbose {
212-
cmd.arg("--verbose");
213-
}
214-
cmd
156+
Ok(())
215157
}

0 commit comments

Comments
 (0)