Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use reponse file on windows and when using gcc #15

Merged
merged 1 commit into from
Sep 8, 2021
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
81 changes: 64 additions & 17 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use std::{env, vec};

use anyhow::*;

use crate::cargo::{add_link_arg, print_warning, set_metadata, track_file};
use crate::cli::{Arg, ArgDef};
use crate::cargo::{self, add_link_arg, print_warning, set_metadata, track_file};
use crate::cli::{self, Arg, ArgDef};
use crate::utils::OsStrExt;

const VAR_C_INCLUDE_ARGS: &str = "C_INCLUDE_ARGS";
const VAR_LINK_ARGS: &str = "LINK_ARGS";
const LINK_ARGS_FILE_NAME: &str = "linker_args.txt";

pub const LDPROXY_NAME: &str = "ldproxy";

Expand Down Expand Up @@ -141,7 +142,7 @@ impl LinkArgsBuilder {
self.force_ldproxy = value;
self
}

pub fn linker(mut self, path: impl Into<PathBuf>) -> Self {
self.linker = Some(path.into());
self
Expand All @@ -157,8 +158,13 @@ impl LinkArgsBuilder {
self
}

pub fn build(self) -> LinkArgs {
let mut result = Vec::new();
pub fn build(self) -> Result<LinkArgs> {
let args: Vec<_> = self
.libdirflags
.into_iter()
.chain(self.libflags)
.chain(self.linkflags)
.collect();

let detected_ldproxy = env::var("RUSTC_LINKER")
.ok()
Expand All @@ -174,29 +180,65 @@ impl LinkArgsBuilder {
print_warning(
"The linker arguments force the usage of `ldproxy` but the linker used \
by cargo is different. Please set the linker to `ldproxy` in your cargo config \
or set `force_ldproxy` to `false`."
or set `force_ldproxy` to `false`.",
);
}

if self.force_ldproxy || detected_ldproxy {
let result = if self.force_ldproxy || detected_ldproxy {
let mut result = Vec::new();

if let Some(linker) = &self.linker {
result.extend(LDPROXY_LINKER_ARG.format(Some(linker.try_to_str().unwrap())));
result.extend(LDPROXY_LINKER_ARG.format(Some(linker.try_to_str()?)));
}

if self.dedup_libs {
result.extend(LDPROXY_DEDUP_LIBS_ARG.format(None));
}

if let Some(cwd) = &self.working_directory {
result.extend(LDPROXY_WORKING_DIRECTORY_ARG.format(Some(cwd.try_to_str().unwrap())))
result.extend(LDPROXY_WORKING_DIRECTORY_ARG.format(Some(cwd.try_to_str()?)))
}
}

result.extend(self.libdirflags);
result.extend(self.libflags);
result.extend(self.linkflags);
// If `windows && gcc` we always use reponse files to circumvent the command-line
// length limitation.
// TODO: implement other linkers
if cfg!(windows) {
// TODO: add way to detect linker flavor
let is_gcc = self
.linker
.and_then(|l| {
l.file_stem()
.and_then(OsStr::to_str)
.map(|s| s.ends_with("gcc"))
})
.unwrap_or(false);

if is_gcc {
let link_args_file = cargo::out_dir().join(LINK_ARGS_FILE_NAME);
let args = cli::join_unix_args(args.iter().map(|s| s.as_str()));

std::fs::write(&link_args_file, args).with_context(|| {
anyhow!(
"could not write link args to file '{}'",
link_args_file.display()
)
})?;

result.push(format!("@{}", link_args_file.try_to_str()?));
} else {
result.extend(args);
}

result
} else {
result.extend(args);
result
}
} else {
args
};

LinkArgs { args: result }
Ok(LinkArgs { args: result })
}
}

Expand All @@ -221,8 +263,11 @@ impl LinkArgs {
/// [`LinkArgs::output_propagated`] in their build script with the value of this
/// crate's `links` property (specified in `Cargo.toml`).
pub fn propagate(&self) {
// FIXME: escape spaces correctly
set_metadata(VAR_LINK_ARGS, self.args.join(" "));
// TODO: maybe more efficient escape machanism
set_metadata(
VAR_LINK_ARGS,
cli::join_unix_args(self.args.iter().map(|s| s.as_str())),
);
}

/// Add all linker arguments from `lib_name` which have been propagated using [`propagate`](LinkArgs::propagate).
Expand All @@ -231,7 +276,9 @@ impl LinkArgs {
/// dependency's `links` property value, which is specified in its package manifest
/// (`Cargo.toml`).
pub fn output_propagated(lib_name: impl Display) -> Result<()> {
for arg in env::var(format!("DEP_{}_{}", lib_name, VAR_LINK_ARGS))?.split(' ') {
let args = env::var(format!("DEP_{}_{}", lib_name, VAR_LINK_ARGS))?;

for arg in cli::UnixCommandArgs::new(&args) {
add_link_arg(arg);
}

Expand Down
12 changes: 11 additions & 1 deletion src/cargo.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::ffi::OsStr;
use std::fmt::{Display, Write};
use std::fs;
use std::path::{Path, PathBuf};
use std::{env, fs};

use anyhow::*;
use cargo_toml::{Manifest, Product};
Expand Down Expand Up @@ -332,6 +332,16 @@ pub fn print_warning(warning: impl Display) {
println!("cargo:warning={}", warning);
}

/// Get the out directory of a crate.
///
/// Panics if environment variable `OUT_DIR` is not set
/// (ie. when called outside of a build script).
pub fn out_dir() -> PathBuf {
env::var_os("OUT_DIR")
.expect("`OUT_DIR` env variable not set (maybe called outside of build script)")
.into()
}

pub trait IntoWarning {
type T;

Expand Down
2 changes: 2 additions & 0 deletions src/cli/separate_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ impl<'a> Iterator for WindowsCommandArgs<'a> {
}

pub use shlex::Shlex as UnixCommandArgs;
pub use shlex::join as join_unix_args;
pub use shlex::quote as quote_unix_arg;

#[cfg(windows)]
pub type NativeCommandArgs<'a> = WindowsCommandArgs<'a>;
Expand Down