From cfd54fe3a01379ac14aa2a6e8a5e946055541a63 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 6 Apr 2024 17:07:44 +0530 Subject: [PATCH] Never pick up Strawberry Perl's pkg-config as a valid implementation Strawberry Perl places a `pkg-config.bat` into PATH that is written in Perl and is not intended to be used by third parties as a MinGW distribution. This wouldn't matter, except that Strawberry Perl is also included in Github CI images out of the box, in `PATH`, and it breaks everyone's CI jobs. This is already done by Meson and CMake: https://github.com/mesonbuild/meson/pull/9384 https://gitlab.kitware.com/cmake/cmake/-/merge_requests/9375 Fixes https://github.com/rust-lang/pkg-config-rs/issues/164 --- src/lib.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3b9a359..cbc2f0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -652,6 +652,77 @@ impl Config { self.statik.unwrap_or_else(|| self.infer_static(name)) } + #[cfg(windows)] + fn find_pkg_config_in(&self, mut path: PathBuf, pathexts: &[OsString]) -> Option { + path.push("pkg-config"); + for pathext in pathexts { + path.set_extension(pathext); + if !path.is_file() { + return None; + } + // Strawberry Perl's pkg-config is pkg-config.bat + if pathext.eq_ignore_ascii_case("bat") { + let mut cmd = Command::new(path.as_os_str()); + let out = match cmd.arg("--help").output() { + Ok(o) => o, + Err(e) => { + eprintln!("Ignoring unusable pkg-config ({:?}): {:?}", path, e); + return None; + } + }; + if let Ok(out) = str::from_utf8(&out.stdout) { + if out.contains("Pure-Perl") { + eprintln!("Ignoring Strawberry Perl pkg-config: {:?}", path); + return None; + } + } + } + } + Some(path.into_os_string()) + } + + fn pkg_config_from_path(&self) -> OsString { + #[cfg(windows)] + { + // Resolve pkg-config in PATH to find the absolute path to pkg-config that we should + // use, so that we can skip Strawberry Perl's pure-perl implementation of pkg-config. + // Despite its presence in PATH, the implementation is for internal use and not meant + // to be used as a MinGW distribution. + use std::os::windows::prelude::*; + let pathexts = env::var_os("PATHEXT").map_or_else( + || { + ["COM", "EXE", "BAT"] + .iter() + .map(|v| OsStr::new(v).to_owned()) + .collect::>() + }, + |v| { + env::split_paths(&v) + // PATHEXT is a list of .EXT but we want EXT + .map(|v| { + OsString::from_wide( + &v.as_os_str().encode_wide().skip(1).collect::>(), + ) + }) + .collect::>() + }, + ); + // Windows first searches for the specified command in the current directory, + // regardless of the value of PATH. + if let Some(ret) = self.find_pkg_config_in(".".into(), &pathexts) { + return ret; + } + if let Some(paths) = env::var_os("PATH") { + for path in env::split_paths(&paths) { + if let Some(ret) = self.find_pkg_config_in(path, &pathexts) { + return ret; + } + } + } + } + OsString::from("pkg-config") + } + fn run(&self, name: &str, args: &[&str]) -> Result, Error> { let pkg_config_exe = self.targeted_env_var("PKG_CONFIG"); let fallback_exe = if pkg_config_exe.is_none() { @@ -659,7 +730,7 @@ impl Config { } else { None }; - let exe = pkg_config_exe.unwrap_or_else(|| OsString::from("pkg-config")); + let exe = pkg_config_exe.unwrap_or_else(|| self.pkg_config_from_path()); let mut cmd = self.command(exe, name, args);