mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-02 14:25:41 +08:00
[3.0] Add frankenphp single executable build for Windows (#1099)
This commit is contained in:
@@ -10,3 +10,4 @@ pthreads4w:
|
||||
license: Apache-2.0
|
||||
static-libs@windows:
|
||||
- libpthreadVC3.lib
|
||||
- pthreadVC3.lib
|
||||
|
||||
@@ -11,8 +11,16 @@ frankenphp:
|
||||
depends:
|
||||
- php-embed
|
||||
- go-xcaddy
|
||||
depends@windows:
|
||||
- php-embed
|
||||
- go-win
|
||||
- pthreads4w
|
||||
suggests@unix:
|
||||
- brotli
|
||||
- watcher
|
||||
suggests@windows:
|
||||
- brotli
|
||||
static-bins@unix:
|
||||
- frankenphp
|
||||
static-bins@windows:
|
||||
- frankenphp.exe
|
||||
|
||||
10
config/pkg/target/go-win.yml
Normal file
10
config/pkg/target/go-win.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
go-win:
|
||||
type: target
|
||||
artifact:
|
||||
binary: custom
|
||||
env:
|
||||
GOROOT: '{pkg_root_path}/go-win'
|
||||
GOBIN: '{pkg_root_path}/go-win/bin'
|
||||
GOPATH: '{pkg_root_path}/go-win/go'
|
||||
path@windows:
|
||||
- '{pkg_root_path}/go-win/bin'
|
||||
85
src/Package/Artifact/go_win.php
Normal file
85
src/Package/Artifact/go_win.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Package\Artifact;
|
||||
|
||||
use StaticPHP\Artifact\ArtifactDownloader;
|
||||
use StaticPHP\Artifact\Downloader\DownloadResult;
|
||||
use StaticPHP\Artifact\Downloader\Type\CheckUpdateResult;
|
||||
use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
|
||||
use StaticPHP\Attribute\Artifact\CustomBinary;
|
||||
use StaticPHP\Attribute\Artifact\CustomBinaryCheckUpdate;
|
||||
use StaticPHP\Exception\DownloaderException;
|
||||
use StaticPHP\Util\GlobalEnvManager;
|
||||
|
||||
class go_win
|
||||
{
|
||||
#[CustomBinary('go-win', [
|
||||
'windows-x86_64',
|
||||
])]
|
||||
public function downBinary(ArtifactDownloader $downloader): DownloadResult
|
||||
{
|
||||
$pkgroot = PKG_ROOT_PATH;
|
||||
|
||||
// get version
|
||||
[$version] = explode("\n", default_shell()->executeCurl('https://go.dev/VERSION?m=text') ?: '');
|
||||
if ($version === '') {
|
||||
throw new DownloaderException('Failed to get latest Go version from https://go.dev/VERSION?m=text');
|
||||
}
|
||||
|
||||
// find SHA256 hash from download page
|
||||
$page = default_shell()->executeCurl('https://go.dev/dl/');
|
||||
if ($page === '' || $page === false) {
|
||||
throw new DownloaderException('Failed to get Go download page from https://go.dev/dl/');
|
||||
}
|
||||
|
||||
$version_regex = str_replace('.', '\.', $version);
|
||||
$pattern = "/class=\"download\" href=\"\\/dl\\/{$version_regex}\\.windows-amd64\\.zip\">.*?<tt>([a-f0-9]{64})<\\/tt>/s";
|
||||
if (preg_match($pattern, $page, $matches)) {
|
||||
$hash = $matches[1];
|
||||
} else {
|
||||
throw new DownloaderException("Failed to find download hash for Go {$version} windows-amd64");
|
||||
}
|
||||
|
||||
$url = "https://go.dev/dl/{$version}.windows-amd64.zip";
|
||||
$path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . "{$version}.windows-amd64.zip";
|
||||
default_shell()->executeCurlDownload($url, $path, retries: $downloader->getRetry());
|
||||
|
||||
// verify hash
|
||||
$file_hash = hash_file('sha256', $path);
|
||||
if ($file_hash !== $hash) {
|
||||
throw new DownloaderException("Hash mismatch for downloaded go-win binary. Expected {$hash}, got {$file_hash}");
|
||||
}
|
||||
|
||||
return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $version], extract: "{$pkgroot}/go-win", verified: true, version: $version);
|
||||
}
|
||||
|
||||
#[CustomBinaryCheckUpdate('go-win', ['windows-x86_64'])]
|
||||
public function checkUpdateBinary(?string $old_version): CheckUpdateResult
|
||||
{
|
||||
[$version] = explode("\n", default_shell()->executeCurl('https://go.dev/VERSION?m=text') ?: '');
|
||||
if ($version === '') {
|
||||
throw new DownloaderException('Failed to get latest Go version from https://go.dev/VERSION?m=text');
|
||||
}
|
||||
return new CheckUpdateResult(
|
||||
old: $old_version,
|
||||
new: $version,
|
||||
needUpdate: $old_version === null || $version !== $old_version,
|
||||
);
|
||||
}
|
||||
|
||||
#[AfterBinaryExtract('go-win', ['windows-x86_64'])]
|
||||
public function afterExtract(string $target_path): void
|
||||
{
|
||||
if (!file_exists("{$target_path}\\bin\\go.exe")) {
|
||||
throw new DownloaderException("Go installation appears incomplete: go.exe not found at {$target_path}\\bin\\go.exe");
|
||||
}
|
||||
|
||||
GlobalEnvManager::putenv("GOROOT={$target_path}");
|
||||
GlobalEnvManager::putenv("GOPATH={$target_path}\\gopath");
|
||||
GlobalEnvManager::putenv("GOCACHE={$target_path}\\gocache");
|
||||
GlobalEnvManager::putenv("GOMODCACHE={$target_path}\\gopath\\pkg\\mod");
|
||||
GlobalEnvManager::addPathIfNotExists("{$target_path}\\bin");
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@ class pthreads4w
|
||||
FileSystem::createDir($lib->getLibDir());
|
||||
FileSystem::createDir($lib->getIncludeDir());
|
||||
FileSystem::copy("{$lib->getSourceDir()}\\libpthreadVC3.lib", "{$lib->getLibDir()}\\libpthreadVC3.lib");
|
||||
// FrankenPHP's cgo.go uses -lpthreadVC3, which lld-link maps to pthreadVC3.lib (no lib prefix)
|
||||
FileSystem::copy("{$lib->getSourceDir()}\\libpthreadVC3.lib", "{$lib->getLibDir()}\\pthreadVC3.lib");
|
||||
FileSystem::copy("{$lib->getSourceDir()}\\_ptw32.h", "{$lib->getIncludeDir()}\\_ptw32.h");
|
||||
FileSystem::copy("{$lib->getSourceDir()}\\pthread.h", "{$lib->getIncludeDir()}\\pthread.h");
|
||||
FileSystem::copy("{$lib->getSourceDir()}\\sched.h", "{$lib->getIncludeDir()}\\sched.h");
|
||||
|
||||
@@ -161,6 +161,7 @@ class php extends TargetPackage
|
||||
// embed build options
|
||||
if ($package->getName() === 'php' || $package->getName() === 'php-embed') {
|
||||
$package->addBuildOption('build-shared', 'D', InputOption::VALUE_REQUIRED, 'Shared extensions to build, comma separated', '');
|
||||
$package->addBuildOption('maintainer-skip-build', null, null, '(maintainer only) skip embed build if exists');
|
||||
}
|
||||
|
||||
// legacy php target build options
|
||||
@@ -265,10 +266,6 @@ class php extends TargetPackage
|
||||
if (!$package->getBuildOption('enable-zts')) {
|
||||
throw new WrongUsageException('FrankenPHP SAPI requires ZTS enabled PHP, build with `--enable-zts`!');
|
||||
}
|
||||
// frankenphp doesn't support windows, BSD is currently not supported by StaticPHP
|
||||
if (!in_array(PHP_OS_FAMILY, ['Linux', 'Darwin'])) {
|
||||
throw new WrongUsageException('FrankenPHP SAPI is only available on Linux and macOS!');
|
||||
}
|
||||
}
|
||||
// linux does not support loading shared libraries when target is pure static
|
||||
$embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static';
|
||||
@@ -345,7 +342,11 @@ class php extends TargetPackage
|
||||
public function postInstall(TargetPackage $package, PackageInstaller $installer): void
|
||||
{
|
||||
if ($package->getName() === 'frankenphp') {
|
||||
$package->runStage([$this, 'smokeTestFrankenphpForUnix']);
|
||||
if (SystemTarget::getTargetOS() === 'Windows') {
|
||||
$package->runStage([$this, 'smokeTestFrankenphpForWindows']);
|
||||
} else {
|
||||
$package->runStage([$this, 'smokeTestFrankenphpForUnix']);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($package->getName() !== 'php') {
|
||||
|
||||
@@ -6,9 +6,12 @@ namespace Package\Target\php;
|
||||
|
||||
use Package\Target\php;
|
||||
use StaticPHP\Attribute\Package\Stage;
|
||||
use StaticPHP\Config\PackageConfig;
|
||||
use StaticPHP\Exception\EnvironmentException;
|
||||
use StaticPHP\Exception\SPCInternalException;
|
||||
use StaticPHP\Exception\ValidationException;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Package\LibraryPackage;
|
||||
use StaticPHP\Package\PackageBuilder;
|
||||
use StaticPHP\Package\PackageInstaller;
|
||||
use StaticPHP\Package\TargetPackage;
|
||||
@@ -18,6 +21,7 @@ use StaticPHP\Util\FileSystem;
|
||||
use StaticPHP\Util\InteractiveTerm;
|
||||
use StaticPHP\Util\SPCConfigUtil;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
use StaticPHP\Util\System\WindowsUtil;
|
||||
use ZM\Logger\ConsoleColor;
|
||||
|
||||
trait frankenphp
|
||||
@@ -171,6 +175,197 @@ trait frankenphp
|
||||
}
|
||||
}
|
||||
|
||||
#[Stage]
|
||||
public function buildFrankenphpForWindows(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void
|
||||
{
|
||||
if (getenv('GOROOT') === false) {
|
||||
throw new EnvironmentException('go-win is not initialized properly. GOROOT is not set.');
|
||||
}
|
||||
|
||||
$clang_info = WindowsUtil::findClang();
|
||||
if ($clang_info === false) {
|
||||
throw new EnvironmentException(
|
||||
'Clang not found. FrankenPHP Windows build requires the LLVM toolchain component of Visual Studio. ' .
|
||||
'Install it in Visual Studio Installer under "C++ Clang tools for Windows", or set the CC environment variable.'
|
||||
);
|
||||
}
|
||||
|
||||
$frankenphp_version = $this->getFrankenPHPVersion($package);
|
||||
$libphp_version = php::getPHPVersion();
|
||||
$major = intdiv(PHP_VERSION_ID, 10000);
|
||||
$source_dir = $package->getSourceDir();
|
||||
|
||||
// collect PHP include paths in clang -I format (not MSVC /I).
|
||||
// Use forward slashes and NO quotes around paths: when Go passes CGO_CFLAGS tokens
|
||||
// directly to clang via exec(), any embedded quotes become literal characters in
|
||||
// the argument string and break include-path resolution.
|
||||
$include = str_replace('\\', '/', BUILD_INCLUDE_PATH);
|
||||
// The PHP source root is needed so that Windows-only headers installed only in
|
||||
// the source tree (e.g. win32/ioutil.h, win32/winutil.h) can be found via their
|
||||
// relative #include paths like `#include "win32/ioutil.h"`.
|
||||
$php_src = str_replace('\\', '/', SOURCE_PATH . '/php-src');
|
||||
$cgo_cflags = implode(' ', [
|
||||
"-I{$include}",
|
||||
"-I{$include}/php",
|
||||
"-I{$include}/php/main",
|
||||
"-I{$include}/php/Zend",
|
||||
"-I{$include}/php/TSRM",
|
||||
"-I{$include}/php/ext",
|
||||
"-I{$php_src}",
|
||||
"-I{$php_src}/main",
|
||||
"-I{$php_src}/ext",
|
||||
"-I{$php_src}/Zend",
|
||||
"-I{$php_src}/TSRM",
|
||||
"-DFRANKENPHP_VERSION={$frankenphp_version}",
|
||||
'-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1',
|
||||
]);
|
||||
|
||||
$dep_libs = [];
|
||||
foreach ($installer->getResolvedPackages(LibraryPackage::class) as $lib) {
|
||||
foreach (PackageConfig::get($lib->getName(), 'static-libs', []) as $lib_file) {
|
||||
if (file_exists("{$package->getLibDir()}\\{$lib_file}")) {
|
||||
$lib_name = preg_replace('/\.lib$/i', '', $lib_file);
|
||||
$dep_libs[] = "-l{$lib_name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$dep_libs = array_unique($dep_libs);
|
||||
$lib_dir = str_replace('\\', '/', BUILD_LIB_PATH);
|
||||
$php_embed_lib = "-lphp{$major}embed";
|
||||
$win_sys_libs = '-lkernel32 -lole32 -luser32 -ladvapi32 -lshell32 -lws2_32 -ldnsapi -lpsapi -lbcrypt';
|
||||
$cgo_ldflags = clean_spaces(implode(' ', array_filter([
|
||||
"-L{$lib_dir}",
|
||||
$php_embed_lib,
|
||||
implode(' ', $dep_libs),
|
||||
$win_sys_libs,
|
||||
'-llibcmt',
|
||||
'-Wl,/NODEFAULTLIB:msvcrt',
|
||||
'-Wl,/NODEFAULTLIB:msvcrtd',
|
||||
'-Wl,/FORCE:MULTIPLE',
|
||||
])));
|
||||
|
||||
// build tags: skip watcher (no inotify/kqueue on Windows)
|
||||
$go_build_tags = 'nobadger,nomysql,nopgx,nowatcher';
|
||||
if (!$installer->isPackageResolved('brotli')) {
|
||||
$go_build_tags .= ',nobrotli';
|
||||
}
|
||||
|
||||
$go_ldflags =
|
||||
'-extldflags=-fuse-ld=lld ' .
|
||||
"-X 'github.com/caddyserver/caddy/v2/modules/caddyhttp.ServerHeader=FrankenPHP Caddy' " .
|
||||
"-X 'github.com/caddyserver/caddy/v2.CustomBinaryName=frankenphp' " .
|
||||
"-X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP v{$frankenphp_version} PHP {$libphp_version} Caddy'";
|
||||
|
||||
// CGO on Windows tokenizes CC/CXX like a shell command line, splitting on spaces.
|
||||
// Paths like "C:\Program Files\..." break because only "C:\Program" is used.
|
||||
// Fix: prepend clang's directory to PATH and use plain executable names instead,
|
||||
// which matches FrankenPHP's official CI approach (CC=clang, CXX=clang++).
|
||||
$clang_dir = dirname($clang_info['clang']);
|
||||
$env = [
|
||||
'CGO_ENABLED' => '1',
|
||||
'CC' => 'clang.exe',
|
||||
'CXX' => 'clang++.exe',
|
||||
'PATH' => $clang_dir . ';' . getenv('PATH'),
|
||||
'CGO_CFLAGS' => clean_spaces($cgo_cflags),
|
||||
'CGO_LDFLAGS' => $cgo_ldflags,
|
||||
];
|
||||
|
||||
InteractiveTerm::setMessage('Building frankenphp: ' . ConsoleColor::yellow('embedding Windows metadata'));
|
||||
$package->runStage([$this, 'embedFrankenphpWindowsMetadata']);
|
||||
|
||||
InteractiveTerm::setMessage('Building frankenphp: ' . ConsoleColor::yellow('building with go build'));
|
||||
|
||||
cmd()->cd("{$source_dir}\\caddy\\frankenphp")
|
||||
->setEnv($env)
|
||||
->exec("go build -v -tags \"{$go_build_tags}\" -ldflags \"{$go_ldflags}\" -o frankenphp.exe .");
|
||||
|
||||
$builder->deployBinary("{$source_dir}\\caddy\\frankenphp\\frankenphp.exe", BUILD_BIN_PATH . '\frankenphp.exe');
|
||||
$package->setOutput('Binary path for FrankenPHP SAPI', BUILD_BIN_PATH . '\frankenphp.exe');
|
||||
}
|
||||
|
||||
/**
|
||||
* Embed Windows PE metadata (version info + icon) into resource.syso so that
|
||||
* go build picks it up automatically. Mirrors the official FrankenPHP Windows CI.
|
||||
*/
|
||||
#[Stage]
|
||||
public function embedFrankenphpWindowsMetadata(TargetPackage $package): void
|
||||
{
|
||||
$frankenphp_version = $this->getFrankenPHPVersion($package);
|
||||
$source_dir = $package->getSourceDir();
|
||||
$build_dir = "{$source_dir}\\caddy\\frankenphp";
|
||||
|
||||
[$p1, $p2, $p3] = explode('.', $frankenphp_version);
|
||||
$major = (int) $p1;
|
||||
$minor = (int) $p2;
|
||||
$patch = (int) $p3;
|
||||
|
||||
$version_info = [
|
||||
'FixedFileInfo' => [
|
||||
'FileVersion' => ['Major' => $major, 'Minor' => $minor, 'Patch' => $patch, 'Build' => 0],
|
||||
'ProductVersion' => ['Major' => $major, 'Minor' => $minor, 'Patch' => $patch, 'Build' => 0],
|
||||
],
|
||||
'StringFileInfo' => [
|
||||
'CompanyName' => 'FrankenPHP',
|
||||
'FileDescription' => 'The modern PHP app server',
|
||||
'FileVersion' => $frankenphp_version,
|
||||
'InternalName' => 'frankenphp',
|
||||
'OriginalFilename' => 'frankenphp.exe',
|
||||
'LegalCopyright' => '(c) 2022 Kévin Dunglas, MIT License',
|
||||
'ProductName' => 'FrankenPHP',
|
||||
'ProductVersion' => $frankenphp_version,
|
||||
'Comments' => 'https://frankenphp.dev/',
|
||||
],
|
||||
'VarFileInfo' => [
|
||||
'Translation' => ['LangID' => 9, 'CharsetID' => 1200],
|
||||
],
|
||||
];
|
||||
|
||||
file_put_contents("{$build_dir}\\versioninfo.json", json_encode($version_info, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// Install goversioninfo if not already installed.
|
||||
// GOPATH is set by the go-win artifact initializer via GlobalEnvManager::putenv().
|
||||
$goversioninfo = getenv('GOROOT') . '\bin\goversioninfo.exe';
|
||||
if (!file_exists($goversioninfo)) {
|
||||
cmd()->exec('go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest');
|
||||
}
|
||||
|
||||
// -64: embed as 64-bit resource; -icon: relative path from the build dir to the repo root icon.
|
||||
cmd()->cd($build_dir)
|
||||
->exec("\"{$goversioninfo}\" -64 -icon {$package->getSourceDir()}\\frankenphp.ico versioninfo.json -o resource.syso");
|
||||
}
|
||||
|
||||
#[Stage]
|
||||
public function smokeTestFrankenphpForWindows(PackageBuilder $builder): void
|
||||
{
|
||||
// analyse --no-smoke-test option
|
||||
$no_smoke_test = $builder->getOption('no-smoke-test', false);
|
||||
$option = match ($no_smoke_test) {
|
||||
false => false, // default value, run all smoke tests
|
||||
null => 'all', // --no-smoke-test without value, skip all smoke tests
|
||||
default => parse_comma_list($no_smoke_test), // --no-smoke-test=frankenphp,...
|
||||
};
|
||||
if ($option === 'all' || (is_array($option) && in_array('frankenphp', $option, true))) {
|
||||
return;
|
||||
}
|
||||
|
||||
InteractiveTerm::setMessage('Running FrankenPHP smoke test');
|
||||
$frankenphp = BUILD_BIN_PATH . '\frankenphp.exe';
|
||||
if (!file_exists($frankenphp)) {
|
||||
throw new ValidationException(
|
||||
"FrankenPHP binary not found: {$frankenphp}",
|
||||
validation_module: 'FrankenPHP smoke test'
|
||||
);
|
||||
}
|
||||
[$ret, $output] = cmd()->execWithResult("{$frankenphp} version");
|
||||
if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) {
|
||||
throw new ValidationException(
|
||||
'FrankenPHP failed smoke test: ret[' . $ret . ']. out[' . implode('', $output) . ']',
|
||||
validation_module: 'FrankenPHP smoke test'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFrankenPHPVersion(TargetPackage $package): string
|
||||
{
|
||||
if ($version = getenv('FRANKENPHP_VERSION')) {
|
||||
|
||||
@@ -447,12 +447,26 @@ trait windows
|
||||
}
|
||||
|
||||
#[BuildFor('Windows')]
|
||||
public function buildWin(TargetPackage $package): void
|
||||
public function buildWin(TargetPackage $package, PackageInstaller $installer): void
|
||||
{
|
||||
if ($package->getName() === 'frankenphp') {
|
||||
/* @var php $this */
|
||||
$package->runStage([$this, 'buildFrankenphpForWindows']);
|
||||
return;
|
||||
}
|
||||
if ($package->getName() !== 'php') {
|
||||
return;
|
||||
}
|
||||
|
||||
// maintainer can skip build though ...
|
||||
if (
|
||||
$installer->isPackageResolved('php-embed')
|
||||
&& $installer->getTargetPackage('php-embed')->getBuildOption('maintainer-skip-build')
|
||||
&& file_exists(BUILD_LIB_PATH . '\php8embed.lib')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$package->runStage([$this, 'buildconfForWindows']);
|
||||
$package->runStage([$this, 'configureForWindows']);
|
||||
$package->runStage([$this, 'makeForWindows']);
|
||||
@@ -467,6 +481,32 @@ trait windows
|
||||
// php-src patches from micro
|
||||
SourcePatcher::patchPhpSrc();
|
||||
|
||||
/* wsyslog.h is generated by mc.exe from win32/build/wsyslog.mc but is absent in some
|
||||
PHP tarballs (e.g. 8.4.x). wsyslog.c still #includes it for the PHP_SYSLOG_*_TYPE
|
||||
event-ID constants. Recreate the missing header with the correct mc.exe-encoded values:
|
||||
MessageId=N + Severity bits (Success=0x00, Info=0x40, Warning=0x80, Error=0xC0)
|
||||
combined into a 32-bit DWORD (Facility=0, Customer=0).
|
||||
*/
|
||||
$wsyslog_h = "{$package->getSourceDir()}\\win32\\wsyslog.h";
|
||||
if (!file_exists($wsyslog_h)) {
|
||||
$shim = <<<'HEADER'
|
||||
/* Auto-generated compatibility shim: wsyslog.h (from win32/build/wsyslog.mc) */
|
||||
#ifndef WSYSLOG_H
|
||||
#define WSYSLOG_H
|
||||
|
||||
#include "syslog.h"
|
||||
|
||||
/* Event IDs generated by mc.exe from wsyslog.mc (Facility=0, Customer=0) */
|
||||
#define PHP_SYSLOG_SUCCESS_TYPE ((DWORD)0x00000001L)
|
||||
#define PHP_SYSLOG_INFO_TYPE ((DWORD)0x40000002L)
|
||||
#define PHP_SYSLOG_WARNING_TYPE ((DWORD)0x80000003L)
|
||||
#define PHP_SYSLOG_ERROR_TYPE ((DWORD)0xC0000004L)
|
||||
|
||||
#endif /* WSYSLOG_H */
|
||||
HEADER;
|
||||
FileSystem::writeFile($wsyslog_h, $shim);
|
||||
}
|
||||
|
||||
// php 8.1 bug
|
||||
if ($this->getPHPVersionID() >= 80100 && $this->getPHPVersionID() < 80200) {
|
||||
logger()->info('Patching PHP 8.1 windows Fiber bug');
|
||||
@@ -656,22 +696,24 @@ C_CODE;
|
||||
$ts = $package->getBuildOption('enable-zts', false) ? '_TS' : '';
|
||||
$build_dir = "{$source_dir}\\x64\\{$rel_type}{$ts}";
|
||||
|
||||
// Build include flags pointing to source dirs (like PHP Windows build does)
|
||||
// Note: embed.c uses #include <sapi/embed/php_embed.h>, so we need $source_dir itself
|
||||
$zts_define = $ts ? ' /D ZTS=1' : '';
|
||||
$include_flags = sprintf(
|
||||
'/I"%s" /I"%s\main" /I"%s\Zend" /I"%s\TSRM" /I"%s" ' .
|
||||
'/D ZEND_WIN32=1 /D PHP_WIN32=1 /D WIN32 /D _WINDOWS /D WINDOWS=1 /D _MBCS /D _USE_MATH_DEFINES',
|
||||
'/D ZEND_WIN32=1 /D PHP_WIN32=1 /D WIN32 /D _WINDOWS /D WINDOWS=1 /D _MBCS /D _USE_MATH_DEFINES%s',
|
||||
$build_dir,
|
||||
$source_dir,
|
||||
$source_dir,
|
||||
$source_dir,
|
||||
$source_dir
|
||||
$source_dir,
|
||||
$zts_define
|
||||
);
|
||||
|
||||
// MSVC cl.exe format: compiler flags must come before /link, linker flags after
|
||||
// ldflags contains /LIBPATH which must be after /link
|
||||
// /FORCE:MULTIPLE: in ZTS mode both zend.obj and php_embed.obj (both packed into the fat php8embed.lib) define _tsrm_ls_cache as a __declspec(thread) variable.
|
||||
$compile_cmd = sprintf(
|
||||
'cl.exe /nologo /O2 /MT /Z7 %s embed.c /Fe:embed.exe /link /LIBPATH:"%s\lib" %s %s',
|
||||
'cl.exe /nologo /O2 /MT /Z7 %s embed.c /Fe:embed.exe /link /FORCE:MULTIPLE /LIBPATH:"%s\lib" %s %s',
|
||||
$include_flags,
|
||||
BUILD_ROOT_PATH,
|
||||
$config['libs'],
|
||||
|
||||
@@ -53,11 +53,12 @@ class LibraryPackage extends Package
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach (PackageConfig::get($this->getName(), 'static-bins', []) as $bin) {
|
||||
$path = FileSystem::isRelativePath($bin) ? "{$this->getBinDir()}/{$bin}" : $bin;
|
||||
if (!file_exists($path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PackageConfig::get($this->getName(), 'static-bins', []) as $bin) {
|
||||
$path = FileSystem::isRelativePath($bin) ? "{$this->getBinDir()}/{$bin}" : $bin;
|
||||
if (!file_exists($path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -133,7 +133,8 @@ class PackageInstaller
|
||||
|
||||
public function printBuildPackageOutputs(): void
|
||||
{
|
||||
foreach ($this->build_packages as $package) {
|
||||
/** @var Package $package */
|
||||
foreach ($this->getResolvedPackages() as $package) {
|
||||
if (($outputs = $package->getOutputs()) !== []) {
|
||||
InteractiveTerm::notice('Package ' . ConsoleColor::green($package->getName()) . ' outputs');
|
||||
$this->printArrayInfo(info: $outputs);
|
||||
@@ -685,6 +686,7 @@ class PackageInstaller
|
||||
if ($package->getBuildOption('build-all') || $package->getBuildOption('build-frankenphp')) {
|
||||
$frankenphp = PackageLoader::getPackage('frankenphp');
|
||||
$this->install_packages[$frankenphp->getName()] = $frankenphp;
|
||||
$this->build_packages[$package->getName()] = $package;
|
||||
$added = true;
|
||||
}
|
||||
$this->build_packages[$package->getName()] = $package;
|
||||
|
||||
@@ -27,7 +27,7 @@ class WindowsCmd extends Shell
|
||||
$this->last_cmd = $cmd = $this->getExecString($cmd);
|
||||
// echo $cmd . PHP_EOL;
|
||||
|
||||
$this->passthru($cmd, $this->console_putput, $original_command, cwd: $this->cd);
|
||||
$this->passthru($cmd, $this->console_putput, $original_command, cwd: $this->cd, env: $this->env);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,44 @@ class WindowsUtil
|
||||
return intval($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Clang compiler from the Visual Studio LLVM toolchain.
|
||||
*
|
||||
* Checks the CC environment variable first (user override), then searches
|
||||
* the VS2022/VS2019 installation via vswhere.
|
||||
*
|
||||
* @return array{clang: string, clangpp: string}|false False if not found
|
||||
*/
|
||||
public static function findClang(): array|false
|
||||
{
|
||||
// Allow user to override via CC environment variable
|
||||
if ($cc = getenv('CC')) {
|
||||
if (file_exists($cc)) {
|
||||
$clangpp = dirname($cc) . DIRECTORY_SEPARATOR . 'clang++.exe';
|
||||
return [
|
||||
'clang' => $cc,
|
||||
'clangpp' => file_exists($clangpp) ? $clangpp : $cc,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$vs = self::findVisualStudio();
|
||||
if ($vs === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$clang = $vs['dir'] . '\VC\Tools\Llvm\x64\bin\clang.exe';
|
||||
$clangpp = $vs['dir'] . '\VC\Tools\Llvm\x64\bin\clang++.exe';
|
||||
if (!file_exists($clang)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'clang' => $clang,
|
||||
'clangpp' => file_exists($clangpp) ? $clangpp : $clang,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create CMake toolchain file.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user