Skip to content

Commit 7423da0

Browse files
authored
Revert "chore: use php-discovery to find matching PHP build" (davidcole1340#206)
1 parent 3171206 commit 7423da0

File tree

4 files changed

+172
-90
lines changed

4 files changed

+172
-90
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ anyhow = "1"
2828
bindgen = "0.60"
2929
cc = "1.0"
3030
skeptic = "0.13"
31-
php-discovery = "0.1.2"
3231

3332
[target.'cfg(windows)'.build-dependencies]
3433
ureq = { version = "2.4", features = ["native-tls", "gzip"], default-features = false }

build.rs

Lines changed: 115 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,21 @@ use std::{
66
env,
77
fs::File,
88
io::{BufWriter, Write},
9-
path::PathBuf,
9+
path::{Path, PathBuf},
10+
process::Command,
11+
str::FromStr,
1012
};
1113

1214
use anyhow::{anyhow, bail, Context, Result};
1315
use bindgen::RustTarget;
1416
use impl_::Provider;
15-
use php_discovery::build::Build as PhpBuild;
1617

1718
const MIN_PHP_API_VER: u32 = 20200930;
1819
const MAX_PHP_API_VER: u32 = 20210902;
1920

2021
pub trait PHPProvider<'a>: Sized {
2122
/// Create a new PHP provider.
22-
fn new(info: &'a PhpBuild) -> Result<Self>;
23+
fn new(info: &'a PHPInfo) -> Result<Self>;
2324

2425
/// Retrieve a list of absolute include paths.
2526
fn get_includes(&self) -> Result<Vec<PathBuf>>;
@@ -32,7 +33,6 @@ pub trait PHPProvider<'a>: Sized {
3233
for line in bindings.lines() {
3334
writeln!(writer, "{}", line)?;
3435
}
35-
3636
Ok(())
3737
}
3838

@@ -42,49 +42,89 @@ pub trait PHPProvider<'a>: Sized {
4242
}
4343
}
4444

45+
/// Finds the location of an executable `name`.
46+
fn find_executable(name: &str) -> Option<PathBuf> {
47+
const WHICH: &str = if cfg!(windows) { "where" } else { "which" };
48+
let cmd = Command::new(WHICH).arg(name).output().ok()?;
49+
if cmd.status.success() {
50+
let stdout = String::from_utf8_lossy(&cmd.stdout);
51+
Some(stdout.trim().into())
52+
} else {
53+
None
54+
}
55+
}
56+
4557
/// Finds the location of the PHP executable.
46-
fn find_php() -> Result<PhpBuild> {
47-
php_discovery::discover()
48-
.map_err(|e| anyhow!("failed to discover available PHP builds: {:?}", e))
49-
.and_then(|builds| {
50-
if builds.is_empty() {
51-
bail!("Could not find any PHP builds in the system, please ensure that PHP is installed.")
52-
}
58+
fn find_php() -> Result<PathBuf> {
59+
// If PHP path is given via env, it takes priority.
60+
let env = std::env::var("PHP");
61+
if let Ok(env) = env {
62+
return Ok(env.into());
63+
}
5364

54-
Ok(builds)
55-
})
56-
.and_then(|builds| {
57-
let mut available = Vec::new();
58-
let mut matching = Vec::new();
59-
60-
for build in builds {
61-
available.push(build.php_api.to_string());
62-
if build.php_api >= MIN_PHP_API_VER && build.php_api <= MAX_PHP_API_VER {
63-
matching.push(build);
64-
}
65-
}
65+
find_executable("php").context("Could not find PHP path. Please ensure `php` is in your PATH or the `PHP` environment variable is set.")
66+
}
6667

67-
if matching.is_empty() {
68-
bail!(
69-
"Unable to find matching PHP binary, available PHP API version(s): '{}', requires a version between {} and {}",
70-
available.join(", "),
71-
MIN_PHP_API_VER,
72-
MAX_PHP_API_VER,
73-
)
74-
}
68+
pub struct PHPInfo(String);
7569

76-
let mut index = 0;
77-
if let Ok(version) = env::var("RUST_PHP_VERSION") {
78-
for (i, build) in matching.iter().enumerate() {
79-
if build.version.to_string() == version {
80-
index = i;
81-
break;
82-
}
83-
}
84-
}
70+
impl PHPInfo {
71+
pub fn get(php: &Path) -> Result<Self> {
72+
let cmd = Command::new(php)
73+
.arg("-i")
74+
.output()
75+
.context("Failed to call `php -i`")?;
76+
if !cmd.status.success() {
77+
bail!("Failed to call `php -i` status code {}", cmd.status);
78+
}
79+
let stdout = String::from_utf8_lossy(&cmd.stdout);
80+
Ok(Self(stdout.to_string()))
81+
}
82+
83+
// Only present on Windows.
84+
#[cfg(windows)]
85+
pub fn architecture(&self) -> Result<impl_::Arch> {
86+
use std::convert::TryInto;
87+
88+
self.get_key("Architecture")
89+
.context("Could not find architecture of PHP")?
90+
.try_into()
91+
}
92+
93+
pub fn thread_safety(&self) -> Result<bool> {
94+
Ok(self
95+
.get_key("Thread Safety")
96+
.context("Could not find thread safety of PHP")?
97+
== "enabled")
98+
}
99+
100+
pub fn debug(&self) -> Result<bool> {
101+
Ok(self
102+
.get_key("Debug Build")
103+
.context("Could not find debug build of PHP")?
104+
== "yes")
105+
}
85106

86-
Ok(matching.remove(index))
87-
})
107+
pub fn version(&self) -> Result<&str> {
108+
self.get_key("PHP Version")
109+
.context("Failed to get PHP version")
110+
}
111+
112+
pub fn zend_version(&self) -> Result<u32> {
113+
self.get_key("PHP API")
114+
.context("Failed to get Zend version")
115+
.and_then(|s| u32::from_str(s).context("Failed to convert Zend version to integer"))
116+
}
117+
118+
fn get_key(&self, key: &str) -> Option<&str> {
119+
let split = format!("{} => ", key);
120+
for line in self.0.lines() {
121+
let components: Vec<_> = line.split(&split).collect();
122+
if components.len() > 1 {
123+
return Some(components[1]);
124+
}
125+
}
126+
None
127+
}
88128
}
89129

90130
/// Builds the wrapper library.
@@ -138,6 +178,33 @@ fn generate_bindings(defines: &[(&str, &str)], includes: &[PathBuf]) -> Result<S
138178
Ok(bindings)
139179
}
140180

181+
/// Checks the PHP Zend API version for compatibility with ext-php-rs, setting
182+
/// any configuration flags required.
183+
fn check_php_version(info: &PHPInfo) -> Result<()> {
184+
let version = info.zend_version()?;
185+
186+
if !(MIN_PHP_API_VER..=MAX_PHP_API_VER).contains(&version) {
187+
bail!("The current version of PHP is not supported. Current PHP API version: {}, requires a version between {} and {}", version, MIN_PHP_API_VER, MAX_PHP_API_VER);
188+
}
189+
190+
// Infra cfg flags - use these for things that change in the Zend API that don't
191+
// rely on a feature and the crate user won't care about (e.g. struct field
192+
// changes). Use a feature flag for an actual feature (e.g. enums being
193+
// introduced in PHP 8.1).
194+
//
195+
// PHP 8.0 is the baseline - no feature flags will be introduced here.
196+
//
197+
// The PHP version cfg flags should also stack - if you compile on PHP 8.2 you
198+
// should get both the `php81` and `php82` flags.
199+
const PHP_81_API_VER: u32 = 20210902;
200+
201+
if version >= PHP_81_API_VER {
202+
println!("cargo:rustc-cfg=php81");
203+
}
204+
205+
Ok(())
206+
}
207+
141208
fn main() -> Result<()> {
142209
let out_dir = env::var_os("OUT_DIR").context("Failed to get OUT_DIR")?;
143210
let out_path = PathBuf::from(out_dir).join("bindings.rs");
@@ -162,12 +229,14 @@ fn main() -> Result<()> {
162229
return Ok(());
163230
}
164231

165-
let php_build = find_php()?;
166-
let provider = Provider::new(&php_build)?;
232+
let php = find_php()?;
233+
let info = PHPInfo::get(&php)?;
234+
let provider = Provider::new(&info)?;
167235

168236
let includes = provider.get_includes()?;
169237
let defines = provider.get_defines()?;
170238

239+
check_php_version(&info)?;
171240
build_wrapper(&defines, &includes)?;
172241
let bindings = generate_bindings(&defines, &includes)?;
173242

@@ -176,13 +245,10 @@ fn main() -> Result<()> {
176245
let mut out_writer = BufWriter::new(out_file);
177246
provider.write_bindings(bindings, &mut out_writer)?;
178247

179-
if php_build.version.major == 8 && php_build.version.minor == 1 {
180-
println!("cargo:rustc-cfg=php81");
181-
}
182-
if php_build.is_debug {
248+
if info.debug()? {
183249
println!("cargo:rustc-cfg=php_debug");
184250
}
185-
if php_build.is_thread_safety_enabled {
251+
if info.thread_safety()? {
186252
println!("cargo:rustc-cfg=php_zts");
187253
}
188254
provider.print_extra_link_args()?;

unix_build.rs

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
11
use std::{path::PathBuf, process::Command};
22

3-
use anyhow::{anyhow, bail, Context, Result};
4-
use php_discovery::build::Build;
3+
use anyhow::{bail, Context, Result};
54

6-
use crate::PHPProvider;
5+
use crate::{PHPInfo, PHPProvider};
76

8-
pub struct Provider<'a> {
9-
build: &'a Build,
10-
}
7+
pub struct Provider {}
118

12-
impl<'a> Provider<'a> {
9+
impl Provider {
1310
/// Runs `php-config` with one argument, returning the stdout.
1411
fn php_config(&self, arg: &str) -> Result<String> {
15-
let config = self.build.config().ok_or_else(|| {
16-
anyhow!(
17-
"unable to locate `php-config` binary for `{}`.",
18-
self.build.binary.to_string_lossy()
19-
)
20-
})?;
21-
22-
let cmd = Command::new(config)
12+
let cmd = Command::new("php-config")
2313
.arg(arg)
2414
.output()
2515
.context("Failed to run `php-config`")?;
@@ -32,9 +22,9 @@ impl<'a> Provider<'a> {
3222
}
3323
}
3424

35-
impl<'a> PHPProvider<'a> for Provider<'a> {
36-
fn new(build: &'a Build) -> Result<Self> {
37-
Ok(Self { build })
25+
impl<'a> PHPProvider<'a> for Provider {
26+
fn new(_: &'a PHPInfo) -> Result<Self> {
27+
Ok(Self {})
3828
}
3929

4030
fn get_includes(&self) -> Result<Vec<PathBuf>> {

windows_build.rs

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
use std::io::Write;
21
use std::{
3-
io::{Cursor, Read},
2+
convert::TryFrom,
3+
fmt::Display,
4+
io::{Cursor, Read, Write},
45
path::{Path, PathBuf},
56
process::Command,
67
sync::Arc,
78
};
89

9-
use anyhow::{Context, Result};
10-
use php_discovery::build::Build;
10+
use anyhow::{bail, Context, Result};
1111

12-
use crate::PHPProvider;
12+
use crate::{PHPInfo, PHPProvider};
1313

1414
const USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
1515

1616
pub struct Provider<'a> {
17-
build: &'a Build,
17+
info: &'a PHPInfo,
1818
devel: DevelPack,
1919
}
2020

@@ -32,25 +32,18 @@ impl<'a> Provider<'a> {
3232
}
3333

3434
impl<'a> PHPProvider<'a> for Provider<'a> {
35-
fn new(build: &'a Build) -> Result<Self> {
36-
// don't use `build.version.to_string()` as it includes extra part which is not
37-
// needed.
38-
let version = format!(
39-
"{}.{}.{}",
40-
build.version.major, build.version.minor, build.version.release
41-
);
42-
let devel = DevelPack::new(
43-
&version,
44-
build.is_thread_safety_enabled,
45-
build.architecture.to_string(),
46-
)?;
35+
fn new(info: &'a PHPInfo) -> Result<Self> {
36+
let version = info.version()?;
37+
let is_zts = info.thread_safety()?;
38+
let arch = info.architecture()?;
39+
let devel = DevelPack::new(version, is_zts, arch)?;
4740
if let Ok(linker) = get_rustc_linker() {
4841
if looks_like_msvc_linker(&linker) {
4942
println!("cargo:warning=It looks like you are using a MSVC linker. You may encounter issues when attempting to load your compiled extension into PHP if your MSVC linker version is not compatible with the linker used to compile your PHP. It is recommended to use `rust-lld` as your linker.");
5043
}
5144
}
5245

53-
Ok(Self { build, devel })
46+
Ok(Self { info, devel })
5447
}
5548

5649
fn get_includes(&self) -> Result<Vec<PathBuf>> {
@@ -63,9 +56,9 @@ impl<'a> PHPProvider<'a> for Provider<'a> {
6356
("PHP_WIN32", "1"),
6457
("WINDOWS", "1"),
6558
("WIN32", "1"),
66-
("ZEND_DEBUG", if self.build.is_debug { "1" } else { "0" }),
59+
("ZEND_DEBUG", if self.info.debug()? { "1" } else { "0" }),
6760
];
68-
if self.build.is_thread_safety_enabled {
61+
if self.info.thread_safety()? {
6962
defines.push(("ZTS", ""));
7063
}
7164
Ok(defines)
@@ -130,12 +123,46 @@ fn looks_like_msvc_linker(linker: &Path) -> bool {
130123
false
131124
}
132125

126+
#[derive(Debug, PartialEq, Eq)]
127+
pub enum Arch {
128+
X86,
129+
X64,
130+
AArch64,
131+
}
132+
133+
impl TryFrom<&str> for Arch {
134+
type Error = anyhow::Error;
135+
136+
fn try_from(value: &str) -> Result<Self> {
137+
Ok(match value {
138+
"x86" => Self::X86,
139+
"x64" => Self::X64,
140+
"arm64" => Self::AArch64,
141+
a => bail!("Unknown architecture {}", a),
142+
})
143+
}
144+
}
145+
146+
impl Display for Arch {
147+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148+
write!(
149+
f,
150+
"{}",
151+
match self {
152+
Arch::X86 => "x86",
153+
Arch::X64 => "x64",
154+
Arch::AArch64 => "arm64",
155+
}
156+
)
157+
}
158+
}
159+
133160
struct DevelPack(PathBuf);
134161

135162
impl DevelPack {
136163
/// Downloads a new PHP development pack, unzips it in the build script
137164
/// temporary directory.
138-
fn new(version: &str, is_zts: bool, arch: String) -> Result<DevelPack> {
165+
fn new(version: &str, is_zts: bool, arch: Arch) -> Result<DevelPack> {
139166
let zip_name = format!(
140167
"php-devel-pack-{}{}-Win32-{}-{}.zip",
141168
version,

0 commit comments

Comments
 (0)