Skip to content

Commit 8197a4d

Browse files
authored
fix: properly link check on Windows (#1598)
1 parent 3098063 commit 8197a4d

File tree

4 files changed

+107
-12
lines changed

4 files changed

+107
-12
lines changed

examples/linking/recipe.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@ source:
1010

1111
build:
1212
script:
13-
- cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release ${CMAKE_ARGS}
13+
- if: win
14+
then:
15+
- cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release %CMAKE_ARGS%
16+
- if: unix
17+
then:
18+
- cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release ${CMAKE_ARGS}
1419
- cmake --build build --config Release
15-
- cmake --install build --prefix ${PREFIX}
20+
- cmake --install build
1621

1722
requirements:
1823
build:

src/post_process/checks.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ use std::{
44
path::{Path, PathBuf},
55
};
66

7-
use crate::post_process::{package_nature::PackageNature, relink};
87
use crate::{
98
metadata::Output,
109
post_process::{package_nature::PrefixInfo, relink::RelinkError},
1110
};
11+
use crate::{
12+
post_process::{package_nature::PackageNature, relink},
13+
windows::link::WIN_ALLOWLIST,
14+
};
1215

1316
use crate::render::resolved_dependencies::RunExportDependency;
1417
use globset::{Glob, GlobSet, GlobSetBuilder};
@@ -168,6 +171,13 @@ fn find_system_libs(output: &Output) -> Result<GlobSet, globset::Error> {
168171
return system_libs.build();
169172
}
170173

174+
if output.build_configuration.target_platform.is_windows() {
175+
for v in WIN_ALLOWLIST {
176+
system_libs.add(Glob::new(v)?);
177+
}
178+
return system_libs.build();
179+
}
180+
171181
if let Some(sysroot_package) = output
172182
.finalized_dependencies
173183
.clone()
@@ -240,7 +250,10 @@ pub fn perform_linking_checks(
240250

241251
let lib = resolved.as_ref().unwrap_or(lib);
242252
if let Ok(libpath) = lib.strip_prefix(host_prefix) {
243-
if let Some(package) = prefix_info.path_to_package.get(libpath) {
253+
if let Some(package) = prefix_info
254+
.path_to_package
255+
.get(&libpath.to_path_buf().into())
256+
{
244257
if let Some(nature) = prefix_info.package_to_nature.get(package) {
245258
// Only take shared libraries into account.
246259
if nature == &PackageNature::DSOLibrary {
@@ -288,7 +301,7 @@ pub fn perform_linking_checks(
288301
continue;
289302
}
290303

291-
// Check if the package has the library linked.
304+
// Check if the package has the library linked.
292305
if let Some(package) = package.linked_dsos.get(lib) {
293306
link_info.linked_packages.push(LinkedPackage {
294307
name: lib.to_path_buf(),

src/post_process/package_nature.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use rattler_conda_types::{PackageName, PrefixRecord};
1616
use std::{
1717
collections::{HashMap, HashSet},
18+
hash::Hash,
1819
ops::Sub,
1920
path::{Path, PathBuf},
2021
};
@@ -126,10 +127,43 @@ impl PackageNature {
126127
}
127128
}
128129

130+
pub struct CaseInsensitivePathBuf {
131+
path: PathBuf,
132+
}
133+
134+
impl CaseInsensitivePathBuf {
135+
fn normalize_path(&self) -> String {
136+
self.path
137+
.to_string_lossy()
138+
.to_lowercase()
139+
.replace('\\', "/")
140+
}
141+
}
142+
143+
impl Hash for CaseInsensitivePathBuf {
144+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
145+
self.normalize_path().hash(state);
146+
}
147+
}
148+
149+
impl From<PathBuf> for CaseInsensitivePathBuf {
150+
fn from(path: PathBuf) -> Self {
151+
CaseInsensitivePathBuf { path }
152+
}
153+
}
154+
155+
impl PartialEq for CaseInsensitivePathBuf {
156+
fn eq(&self, other: &Self) -> bool {
157+
self.normalize_path() == other.normalize_path()
158+
}
159+
}
160+
161+
impl Eq for CaseInsensitivePathBuf {}
162+
129163
#[derive(Default)]
130164
pub(crate) struct PrefixInfo {
131165
pub package_to_nature: HashMap<PackageName, PackageNature>,
132-
pub path_to_package: HashMap<PathBuf, PackageName>,
166+
pub path_to_package: HashMap<CaseInsensitivePathBuf, PackageName>,
133167
}
134168

135169
impl PrefixInfo {
@@ -149,10 +183,12 @@ impl PrefixInfo {
149183
record.repodata_record.package_record.name.clone(),
150184
package_nature,
151185
);
186+
152187
for file in record.files {
153-
prefix_info
154-
.path_to_package
155-
.insert(file, record.repodata_record.package_record.name.clone());
188+
prefix_info.path_to_package.insert(
189+
file.into(),
190+
record.repodata_record.package_record.name.clone(),
191+
);
156192
}
157193
}
158194
}

src/windows/link.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use std::{
88
use fs_err::File;
99

1010
use goblin::pe::{PE, header::DOS_MAGIC};
11+
use rattler_conda_types::Platform;
12+
use rattler_shell::activation::prefix_path_entries;
1113
use scroll::Pread;
1214

1315
use crate::{
@@ -84,8 +86,8 @@ impl Relinker for Dll {
8486

8587
fn resolve_libraries(
8688
&self,
87-
_prefix: &Path,
88-
_encoded_prefix: &Path,
89+
prefix: &Path,
90+
encoded_prefix: &Path,
8991
) -> HashMap<PathBuf, Option<PathBuf>> {
9092
let mut result = HashMap::new();
9193
for lib in &self.libraries {
@@ -97,7 +99,46 @@ impl Relinker for Dll {
9799
}) {
98100
continue;
99101
}
100-
result.insert(lib.clone(), Some(lib.clone()));
102+
103+
let dll_name = lib.file_name().unwrap_or_default();
104+
105+
// 1. Check in the same directory as the original DLL
106+
let path_in_prefix = self
107+
.path
108+
.strip_prefix(prefix)
109+
.expect("DLL path should be in prefix");
110+
let path = encoded_prefix.join(path_in_prefix);
111+
let same_dir = path.parent().map(|p| p.join(dll_name));
112+
113+
if let Some(path) = same_dir {
114+
if path.exists() {
115+
result.insert(lib.clone(), Some(path));
116+
continue;
117+
}
118+
}
119+
120+
// 2. Check all directories in the search path
121+
let mut found = false;
122+
let path_entries = prefix_path_entries(encoded_prefix, &Platform::Win64);
123+
let path = std::env::var("PATH").unwrap_or_default();
124+
let search_dirs = path_entries
125+
.into_iter()
126+
.chain(std::env::split_paths(&path))
127+
.collect::<Vec<_>>();
128+
129+
for search_dir in search_dirs {
130+
let potential_path = search_dir.join(dll_name);
131+
if potential_path.exists() {
132+
result.insert(lib.clone(), Some(potential_path));
133+
found = true;
134+
break;
135+
}
136+
}
137+
138+
if !found {
139+
// If not found anywhere, keep the original name but mark as None
140+
result.insert(lib.clone(), None);
141+
}
101142
}
102143
result
103144
}

0 commit comments

Comments
 (0)