Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# smoelius: Force `LF` line endings in `.stderr` files.
# See https://github.com/Manishearth/compiletest-rs/issues/154
*.stderr eol=lf
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
test:
strategy:
matrix:
environment: [ubuntu-latest, macos-latest]
environment: [ubuntu-latest, macos-latest, windows-latest]

runs-on: ${{ matrix.environment }}

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 56 additions & 50 deletions cargo-dylint/tests/custom_toolchain.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,69 @@
use anyhow::{anyhow, Result};
use dylint_internal::{
find_and_replace,
rustup::{toolchain_path, SanitizeEnvironment},
Command,
};
use std::path::Path;
use tempfile::{tempdir, NamedTempFile};
use test_env_log::test;
// smoelius: As per `dylint-link/src/main.rs`:
// "Only the MSVC toolchain is supported on Windows"
#[cfg(not(target_os = "windows"))]
mod custom_toolchain {
use anyhow::{anyhow, Result};
use dylint_internal::{
find_and_replace,
rustup::{toolchain_path, SanitizeEnvironment},
Command,
};
use std::path::Path;
use tempfile::{tempdir, NamedTempFile};
use test_env_log::test;

#[test]
fn custom_toolchain() {
let tempdir = tempdir().unwrap();
#[test]
fn custom_toolchain() {
let tempdir = tempdir().unwrap();

dylint_internal::checkout_dylint_template(tempdir.path()).unwrap();
dylint_internal::checkout_dylint_template(tempdir.path()).unwrap();

let toolchain_path = toolchain_path(tempdir.path()).unwrap();
let toolchain_path = toolchain_path(tempdir.path()).unwrap();

let custom_toolchain = random_string().unwrap();
let custom_toolchain = random_string().unwrap();

link_toolchain(&custom_toolchain, &toolchain_path).unwrap();
link_toolchain(&custom_toolchain, &toolchain_path).unwrap();

patch_dylint_template(tempdir.path(), &custom_toolchain).unwrap();
patch_dylint_template(tempdir.path(), &custom_toolchain).unwrap();

dylint_internal::test()
.sanitize_environment()
.current_dir(tempdir.path())
.success()
.unwrap();
dylint_internal::test()
.sanitize_environment()
.current_dir(tempdir.path())
.success()
.unwrap();

uninstall_toolchain(&custom_toolchain).unwrap();
}
uninstall_toolchain(&custom_toolchain).unwrap();
}

fn random_string() -> Result<String> {
let tempfile = NamedTempFile::new()?;
tempfile
.path()
.file_name()
.map(|s| s.to_string_lossy().trim_start_matches('.').to_string())
.ok_or_else(|| anyhow!("Could not get file name"))
}
fn random_string() -> Result<String> {
let tempfile = NamedTempFile::new()?;
tempfile
.path()
.file_name()
.map(|s| s.to_string_lossy().trim_start_matches('.').to_string())
.ok_or_else(|| anyhow!("Could not get file name"))
}

fn link_toolchain(toolchain: &str, path: &Path) -> Result<()> {
Command::new("rustup")
.args(&["toolchain", "link", toolchain, &path.to_string_lossy()])
.success()
}
fn link_toolchain(toolchain: &str, path: &Path) -> Result<()> {
Command::new("rustup")
.args(&["toolchain", "link", toolchain, &path.to_string_lossy()])
.success()
}

fn patch_dylint_template(path: &Path, channel: &str) -> Result<()> {
find_and_replace(
&path.join("rust-toolchain"),
&[&format!(
r#"s/(?m)^channel = "[^"]*"$/channel = "{}"/"#,
channel,
)],
)
}
fn patch_dylint_template(path: &Path, channel: &str) -> Result<()> {
// smoelius: See https://github.com/rust-lang/regex/issues/244
find_and_replace(
&path.join("rust-toolchain"),
&[&format!(
r#"s/(?m)^channel = "[^"]*"/channel = "{}"/"#,
channel,
)],
)
}

fn uninstall_toolchain(toolchain: &str) -> Result<()> {
Command::new("rustup")
.args(&["toolchain", "uninstall", toolchain])
.success()
fn uninstall_toolchain(toolchain: &str) -> Result<()> {
Command::new("rustup")
.args(&["toolchain", "uninstall", toolchain])
.success()
}
}
10 changes: 6 additions & 4 deletions cargo-dylint/tests/dylint_driver_path.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use dylint_internal::{
env,
rustup::{toolchain_path, SanitizeEnvironment},
Command,
};
use std::fs::create_dir_all;
use tempfile::tempdir_in;
Expand Down Expand Up @@ -31,7 +30,10 @@ fn dylint_driver_path() {
// https://github.com/trailofbits/dylint/issues/54
let toolchain_path = toolchain_path(tempdir.path()).unwrap();
let toolchain = toolchain_path.iter().last().unwrap();
Command::new(dylint_driver_path.join(toolchain).join("dylint-driver"))
.success()
.unwrap();
let mut command = dylint_internal::driver(
&toolchain.to_string_lossy().to_string(),
&dylint_driver_path.join(toolchain).join("dylint-driver"),
)
.unwrap();
command.success().unwrap();
}
3 changes: 2 additions & 1 deletion cargo-dylint/tests/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ fn one_name_multiple_toolchains() {
}

fn patch_dylint_template(path: &Path, channel: &str, clippy_utils_tag: &str) -> Result<()> {
// smoelius: See https://github.com/rust-lang/regex/issues/244
find_and_replace(
&path.join("rust-toolchain"),
&[&format!(
r#"s/(?m)^channel = "[^"]*"$/channel = "{}"/"#,
r#"s/(?m)^channel = "[^"]*"/channel = "{}"/"#,
channel,
)],
)?;
Expand Down
3 changes: 3 additions & 0 deletions dylint-link/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ env_logger = "0.9.0"
if_chain = "1.0.1"

dylint_internal = { version = "=1.0.1", path = "../internal" }

[target.'cfg(target_os = "windows")'.dependencies]
cc = "1.0.69"
143 changes: 119 additions & 24 deletions dylint-link/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#![deny(clippy::unwrap_used)]
#![deny(clippy::panic)]

#[cfg(target_os = "windows")]
use anyhow::ensure;
use anyhow::{anyhow, Result};
use dylint_internal::{
env::{self, var},
Expand All @@ -14,41 +16,134 @@ use std::{
fs::copy,
path::{Path, PathBuf},
};
#[cfg(target_os = "windows")]
use std::{fs::File, io::Read};

fn main() -> Result<()> {
env_logger::init();

let linker = linker()?;
let args: Vec<String> = std::env::args().collect();
Command::new(linker).args(&args[1..]).success()?;

Command::new("cc").args(&args[1..]).success()?;
if let Some(path) = output_path(args.iter())? {
copy_library(&path)?;
}

Ok(())
}

let cargo_pkg_name = var(env::CARGO_PKG_NAME)?;
#[cfg(target_os = "windows")]
fn linker() -> Result<PathBuf> {
let rustup_toolchain = var(env::RUSTUP_TOOLCHAIN)?;
let split_toolchain: Vec<_> = rustup_toolchain.split('-').collect();
if_chain! {
if split_toolchain.last() == Some(&"msvc");
let len = split_toolchain.len();
if len >= 4;
then {
// MinerSebas: Removes the Release Information: "nightly-2021-04-08-x86_64-pc-windows-msvc" -> "x86_64-pc-windows-msvc"
let trimmed_toolchain: String = split_toolchain[len - 4..].join("-");
if let Some(tool) = cc::windows_registry::find_tool(&trimmed_toolchain, "link.exe") {
Ok(tool.path().into())
} else {
Err(anyhow!("Could not find the MSVC Linker"))
}
} else {
Err(anyhow!("Only the MSVC toolchain is supported on Windows"))
}
}
}

#[cfg(not(target_os = "windows"))]
#[allow(clippy::unnecessary_wraps)]
fn linker() -> Result<PathBuf> {
Ok(PathBuf::from("cc"))
}

#[cfg(target_os = "windows")]
fn output_path<'a, I>(iter: I) -> Result<Option<PathBuf>>
where
I: Iterator<Item = &'a String>,
{
for arg in iter {
if let Some(path) = arg.strip_prefix("/OUT:") {
return Ok(Some(path.into()));
}
if let Some(path) = arg.strip_prefix('@') {
return extract_out_path_from_linker_response_file(path);
}
}

let mut args = std::env::args();
while let Some(arg) = args.next() {
Ok(None)
}

#[cfg(not(target_os = "windows"))]
fn output_path<'a, I>(mut iter: I) -> Result<Option<PathBuf>>
where
I: Iterator<Item = &'a String>,
{
while let Some(arg) = iter.next() {
if arg == "-o" {
if_chain! {
if let Some(path) = args.next();
let path = Path::new(&path);
if let Some(lib_name) = parse_path(path);
if lib_name == cargo_pkg_name.replace("-", "_");
then {
let filename_with_toolchain = format!(
"{}{}@{}{}",
consts::DLL_PREFIX,
lib_name,
rustup_toolchain,
consts::DLL_SUFFIX
);
let parent = path
.parent()
.ok_or_else(|| anyhow!("Could not get parent directory"))?;
let path_with_toolchain = strip_deps(parent).join(filename_with_toolchain);
copy(path, path_with_toolchain)?;
}
if let Some(path) = iter.next() {
return Ok(Some(path.into()));
}
break;
}
}

Ok(None)
}

#[cfg(target_os = "windows")]
fn extract_out_path_from_linker_response_file(path: impl AsRef<Path>) -> Result<Option<PathBuf>> {
// MinerSebas: On Windows the cmd line has a Limit of 8191 Characters.
// If your command would exceed this you can instead use a Linker Response File to set arguments.
// (https://docs.microsoft.com/en-us/cpp/build/reference/at-specify-a-linker-response-file?view=msvc-160)

// MinerSebas: Read the Linker Response File
let mut buf: Vec<u8> = Vec::new();
File::open(path)?.read_to_end(&mut buf)?;

// MinerSebas: Convert the File from UTF-16 to a Rust UTF-8 String
// (Only necessary for MSVC, the GNU Linker uses UTF-8 isntead.)
// Based on: https://stackoverflow.com/a/57172592
let file: Vec<u16> = buf
.chunks_exact(2)
.into_iter()
.map(|a| u16::from_ne_bytes([a[0], a[1]]))
.collect();
let file = String::from_utf16_lossy(file.as_slice());

let paths: Vec<_> = file
.lines()
.flat_map(|line| line.trim().trim_matches('"').strip_prefix("/OUT:"))
.collect();

ensure!(paths.len() <= 1, "Found multiple output paths");

// smoelius: Do not raise an error if no output path is found.
Ok(paths.last().map(Into::into))
}

fn copy_library(path: &Path) -> Result<()> {
if_chain! {
if let Some(lib_name) = parse_path(path);
let cargo_pkg_name = var(env::CARGO_PKG_NAME)?;
if lib_name == cargo_pkg_name.replace("-", "_");
then {
let rustup_toolchain = var(env::RUSTUP_TOOLCHAIN)?;
let filename_with_toolchain = format!(
"{}{}@{}{}",
consts::DLL_PREFIX,
lib_name,
rustup_toolchain,
consts::DLL_SUFFIX
);
let parent = path
.parent()
.ok_or_else(|| anyhow!("Could not get parent directory"))?;
let path_with_toolchain = strip_deps(parent).join(filename_with_toolchain);
copy(path, path_with_toolchain)?;
}
}

Expand Down
Loading