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 the object crate to generate .def files #402

Closed
wants to merge 2 commits into from
Closed
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ anyhow = "1.0"
cc = "1.0"
glob = "0.3"
itertools = "0.13"
object = "0.36.4"

# workaround cargo
[target.'cfg(windows)'.dependencies.windows-sys]
Expand Down
102 changes: 33 additions & 69 deletions src/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::io::{BufRead, BufReader, Read, Write};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
Expand All @@ -13,7 +13,7 @@ use cargo::util::interning::InternedString;
use cargo::{CliResult, GlobalContext};

use anyhow::Context as _;
use cargo_util::paths::{copy, create, create_dir_all, open, read, read_bytes, write};
use cargo_util::paths::{copy, create_dir_all, open, read, read_bytes, write};
use semver::Version;

use crate::build_targets::BuildTargets;
Expand Down Expand Up @@ -134,67 +134,35 @@ fn build_def_file(
target: &target::Target,
targetdir: &Path,
) -> anyhow::Result<()> {
let os = &target.os;
let env = &target.env;
if target.os == "windows" && target.env == "msvc" {
ws.gctx().shell().status("Building", ".def file")?;

if os == "windows" && env == "msvc" {
ws.gctx()
.shell()
.status("Building", ".def file using dumpbin")?;
// Parse the .dll as an object file
let dll_path = targetdir.join(format!("{}.dll", name.replace('-', "_")));
let dll_content = std::fs::read(&dll_path)?;
let dll_file = object::File::parse(&*dll_content)?;

let txt_path = targetdir.join(format!("{name}.txt"));
// Create the .def output file
let def_file = cargo_util::paths::create(targetdir.join(format!("{name}.def")))?;

let target_str = format!("{}-pc-windows-msvc", &target.arch);
let mut dumpbin = match cc::windows_registry::find(&target_str, "dumpbin.exe") {
Some(command) => command,
None => std::process::Command::new("dumpbin"),
};
write_def_file(dll_file, def_file)?;
}

dumpbin
.arg("/EXPORTS")
.arg(targetdir.join(format!("{}.dll", name.replace('-', "_"))));
dumpbin.arg(format!("/OUT:{}", txt_path.to_str().unwrap()));

let out = dumpbin.output()?;
if out.status.success() {
let txt_file = open(txt_path)?;
let buf_reader = BufReader::new(txt_file);
let mut def_file = create(targetdir.join(format!("{name}.def")))?;
writeln!(def_file, "EXPORTS")?;

// The Rust loop below is analogue to the following loop.
// for /f "skip=19 tokens=4" %A in (file.txt) do echo %A > file.def
// The most recent versions of dumpbin adds three lines of copyright
// information before the relevant content.
// If the "/OUT:file.txt" dumpbin's option is used, the three
// copyright lines are added to the shell, so the txt file
// contains three lines less.
// The Rust loop first skips 16 lines and then, for each line,
// deletes all the characters up to the fourth space included
// (skip=16 tokens=4)
for line in buf_reader
.lines()
.skip(16)
.take_while(|l| !l.as_ref().unwrap().is_empty())
.map(|l| {
l.unwrap()
.as_str()
.split_whitespace()
.nth(3)
.unwrap()
.to_string()
})
{
writeln!(def_file, "\t{line}")?;
}
Ok(())
}

Ok(())
} else {
Err(anyhow::anyhow!("Command failed {:?}", dumpbin))
}
} else {
Ok(())
fn write_def_file<W: std::io::Write>(dll_file: object::File, mut def_file: W) -> anyhow::Result<W> {
use object::read::Object;

writeln!(def_file, "EXPORTS")?;

for export in dll_file.exports()? {
def_file.write_all(b"\t")?;
def_file.write_all(export.name())?;
def_file.write_all(b"\n")?;
}

Ok(def_file)
}

/// Build import library for windows-gnu
Expand Down Expand Up @@ -932,7 +900,7 @@ fn compile_with_exec(
let mut leaf_args: Vec<String> = rustc_target
.shared_object_link_args(&capi_config, &install_paths.libdir, root_output)
.into_iter()
.flat_map(|l| vec!["-C".to_string(), format!("link-arg={l}")])
.flat_map(|l| ["-C".to_string(), format!("link-arg={l}")])
.collect();

leaf_args.extend(pkg_rustflags.clone());
Expand Down Expand Up @@ -1176,17 +1144,13 @@ pub fn cbuild(
let lib_name = name;
build_def_file(ws, lib_name, &rustc_target, &root_output)?;

let mut dlltool = std::env::var_os("DLLTOOL")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("dlltool"));

// dlltool argument overwrites environment var
if args.contains_id("dlltool") {
dlltool = args
.get_one::<PathBuf>("dlltool")
.map(PathBuf::from)
.unwrap();
}
let dlltool = if let Some(path) = args.get_one::<PathBuf>("dlltool") {
PathBuf::from(path)
} else if let Some(path) = std::env::var_os("DLLTOOL") {
PathBuf::from(path)
} else {
PathBuf::from("dlltool")
};

build_implib_file(ws, lib_name, &rustc_target, &root_output, &dlltool)?;
}
Expand Down