mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-18 21:04:52 +08:00
229 lines
11 KiB
PHP
229 lines
11 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Package\Target\php;
|
||
|
|
|
||
|
|
use StaticPHP\Attribute\Package\BeforeStage;
|
||
|
|
use StaticPHP\Attribute\Package\BuildFor;
|
||
|
|
use StaticPHP\Attribute\Package\Stage;
|
||
|
|
use StaticPHP\Attribute\PatchDescription;
|
||
|
|
use StaticPHP\Exception\PatchException;
|
||
|
|
use StaticPHP\Exception\SPCInternalException;
|
||
|
|
use StaticPHP\Package\PackageBuilder;
|
||
|
|
use StaticPHP\Package\PackageInstaller;
|
||
|
|
use StaticPHP\Package\TargetPackage;
|
||
|
|
use StaticPHP\Util\FileSystem;
|
||
|
|
use StaticPHP\Util\InteractiveTerm;
|
||
|
|
use StaticPHP\Util\SourcePatcher;
|
||
|
|
use StaticPHP\Util\System\WindowsUtil;
|
||
|
|
use StaticPHP\Util\V2CompatLayer;
|
||
|
|
use ZM\Logger\ConsoleColor;
|
||
|
|
|
||
|
|
trait windows
|
||
|
|
{
|
||
|
|
#[Stage]
|
||
|
|
public function buildconfForWindows(TargetPackage $package): void
|
||
|
|
{
|
||
|
|
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('./buildconf.bat'));
|
||
|
|
V2CompatLayer::emitPatchPoint('before-php-buildconf');
|
||
|
|
cmd()->cd($package->getSourceDir())->exec('.\buildconf.bat');
|
||
|
|
}
|
||
|
|
|
||
|
|
#[Stage]
|
||
|
|
public function configureForWindows(TargetPackage $package, PackageInstaller $installer): void
|
||
|
|
{
|
||
|
|
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('./configure.bat'));
|
||
|
|
V2CompatLayer::emitPatchPoint('before-php-configure');
|
||
|
|
$args = [
|
||
|
|
'--disable-all',
|
||
|
|
"--with-php-build={$package->getBuildRootPath()}",
|
||
|
|
"--with-extra-includes={$package->getIncludeDir()}",
|
||
|
|
"--with-extra-libs={$package->getLibDir()}",
|
||
|
|
];
|
||
|
|
// sapis
|
||
|
|
$cli = $installer->isPackageResolved('php-cli');
|
||
|
|
$cgi = $installer->isPackageResolved('php-cgi');
|
||
|
|
$micro = $installer->isPackageResolved('php-micro');
|
||
|
|
$args[] = $cli ? '--enable-cli=yes' : '--enable-cli=no';
|
||
|
|
$args[] = $cgi ? '--enable-cgi=yes' : '--enable-cgi=no';
|
||
|
|
$args[] = $micro ? '--enable-micro=yes' : '--enable-micro=no';
|
||
|
|
|
||
|
|
// zts
|
||
|
|
$args[] = $package->getBuildOption('enable-zts', false) ? '--enable-zts=yes' : '--enable-zts=no';
|
||
|
|
// opcache-jit
|
||
|
|
$args[] = !$package->getBuildOption('disable-opcache-jit', false) ? '--enable-opcache-jit=yes' : '--enable-opcache-jit=no';
|
||
|
|
// micro win32
|
||
|
|
if ($micro && $package->getBuildOption('enable-micro-win32', false)) {
|
||
|
|
$args[] = '--enable-micro-win32=yes';
|
||
|
|
}
|
||
|
|
// config-file-scan-dir
|
||
|
|
if ($option = $package->getBuildOption('with-config-file-scan-dir', false)) {
|
||
|
|
$args[] = "--with-config-file-scan-dir={$option}";
|
||
|
|
}
|
||
|
|
// micro logo
|
||
|
|
if ($micro && ($logo = $this->getBuildOption('with-micro-logo')) !== null) {
|
||
|
|
$args[] = "--enable-micro-logo={$logo}";
|
||
|
|
copy($logo, SOURCE_PATH . '\php-src\\' . $logo);
|
||
|
|
}
|
||
|
|
$args = implode(' ', $args);
|
||
|
|
$static_extension_str = $this->makeStaticExtensionString($installer);
|
||
|
|
cmd()->cd($package->getSourceDir())->exec(".\\configure.bat {$args} {$static_extension_str}");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[BeforeStage('php', [self::class, 'makeCliForWindows'])]
|
||
|
|
#[PatchDescription('Patch Windows Makefile for CLI target')]
|
||
|
|
public function patchCLITarget(TargetPackage $package): void
|
||
|
|
{
|
||
|
|
// search Makefile code line contains "$(BUILD_DIR)\php.exe:"
|
||
|
|
$content = FileSystem::readFile("{$package->getSourceDir()}\\Makefile");
|
||
|
|
$lines = explode("\r\n", $content);
|
||
|
|
$line_num = 0;
|
||
|
|
$found = false;
|
||
|
|
foreach ($lines as $v) {
|
||
|
|
if (str_contains($v, '$(BUILD_DIR)\php.exe:')) {
|
||
|
|
$found = $line_num;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
++$line_num;
|
||
|
|
}
|
||
|
|
if ($found === false) {
|
||
|
|
throw new PatchException('Windows Makefile patching for php.exe target', 'Cannot patch windows CLI Makefile, Makefile does not contain "$(BUILD_DIR)\php.exe:" line');
|
||
|
|
}
|
||
|
|
$lines[$line_num] = '$(BUILD_DIR)\php.exe: generated_files $(DEPS_CLI) $(PHP_GLOBAL_OBJS) $(CLI_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest';
|
||
|
|
$lines[$line_num + 1] = "\t" . '"$(LINK)" /nologo $(PHP_GLOBAL_OBJS_RESP) $(CLI_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(ASM_OBJS) $(LIBS) $(LIBS_CLI) $(BUILD_DIR)\php.exe.res /out:$(BUILD_DIR)\php.exe $(LDFLAGS) $(LDFLAGS_CLI) /ltcg /nodefaultlib:msvcrt /nodefaultlib:msvcrtd /ignore:4286';
|
||
|
|
FileSystem::writeFile("{$package->getSourceDir()}\\Makefile", implode("\r\n", $lines));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[Stage]
|
||
|
|
public function makeCliForWindows(TargetPackage $package, PackageBuilder $builder): void
|
||
|
|
{
|
||
|
|
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('nmake php-cli'));
|
||
|
|
|
||
|
|
// extra lib
|
||
|
|
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
|
||
|
|
|
||
|
|
// Add debug symbols for release build if --no-strip is specified
|
||
|
|
// We need to modify CFLAGS to replace /Ox with /Zi and add /DEBUG to LDFLAGS
|
||
|
|
$debug_overrides = '';
|
||
|
|
if ($package->getBuildOption('no-strip', false)) {
|
||
|
|
// Read current CFLAGS from Makefile and replace optimization flags
|
||
|
|
$makefile_content = file_get_contents("{$package->getSourceDir()}\\Makefile");
|
||
|
|
if (preg_match('/^CFLAGS=(.+?)$/m', $makefile_content, $matches)) {
|
||
|
|
$cflags = $matches[1];
|
||
|
|
// Replace /Ox (full optimization) with /Zi (debug info) and /Od (disable optimization)
|
||
|
|
// Keep optimization for speed: /O2 /Zi instead of /Od /Zi
|
||
|
|
$cflags = str_replace('/Ox ', '/O2 /Zi ', $cflags);
|
||
|
|
$debug_overrides = '"CFLAGS=' . $cflags . '" "LDFLAGS=/DEBUG /LTCG /INCREMENTAL:NO" "LDFLAGS_CLI=/DEBUG" ';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
cmd()->cd($package->getSourceDir())
|
||
|
|
->exec("nmake /nologo {$debug_overrides}LIBS_CLI=\"ws2_32.lib shell32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= php.exe");
|
||
|
|
|
||
|
|
$this->deployWindowsBinary($builder, $package, 'php-cli');
|
||
|
|
}
|
||
|
|
|
||
|
|
#[Stage]
|
||
|
|
public function makeForWindows(TargetPackage $package, PackageInstaller $installer): void
|
||
|
|
{
|
||
|
|
V2CompatLayer::emitPatchPoint('before-php-make');
|
||
|
|
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('nmake clean'));
|
||
|
|
cmd()->cd($package->getSourceDir())->exec('nmake clean');
|
||
|
|
|
||
|
|
if ($installer->isPackageResolved('php-cli')) {
|
||
|
|
$package->runStage([$this, 'makeCliForWindows']);
|
||
|
|
}
|
||
|
|
if ($installer->isPackageResolved('php-cgi')) {
|
||
|
|
$package->runStage([$this, 'makeCgiForWindows']);
|
||
|
|
}
|
||
|
|
if ($installer->isPackageResolved('php-micro')) {
|
||
|
|
$package->runStage([$this, 'makeMicroForWindows']);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[BuildFor('Windows')]
|
||
|
|
public function buildWin(TargetPackage $package): void
|
||
|
|
{
|
||
|
|
if ($package->getName() !== 'php') {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$package->runStage([$this, 'buildconfForWindows']);
|
||
|
|
$package->runStage([$this, 'configureForWindows']);
|
||
|
|
$package->runStage([$this, 'makeForWindows']);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[BeforeStage('php', [self::class, 'buildconfForWindows'])]
|
||
|
|
#[PatchDescription('Patch SPC_MICRO_PATCHES defined patches')]
|
||
|
|
#[PatchDescription('Fix PHP 8.1 static build bug on Windows')]
|
||
|
|
#[PatchDescription('Fix PHP Visual Studio version detection')]
|
||
|
|
public function patchBeforeBuildconfForWindows(TargetPackage $package): void
|
||
|
|
{
|
||
|
|
// php-src patches from micro
|
||
|
|
SourcePatcher::patchPhpSrc();
|
||
|
|
|
||
|
|
// php 8.1 bug
|
||
|
|
if ($this->getPHPVersionID() >= 80100 && $this->getPHPVersionID() < 80200) {
|
||
|
|
logger()->info('Patching PHP 8.1 windows Fiber bug');
|
||
|
|
FileSystem::replaceFileStr(
|
||
|
|
"{$package->getSourceDir()}\\win32\\build\\config.w32",
|
||
|
|
"ADD_FLAG('LDFLAGS', '$(BUILD_DIR)\\\\Zend\\\\jump_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj');",
|
||
|
|
"ADD_FLAG('ASM_OBJS', '$(BUILD_DIR)\\\\Zend\\\\jump_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj $(BUILD_DIR)\\\\Zend\\\\make_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj');"
|
||
|
|
);
|
||
|
|
FileSystem::replaceFileStr(
|
||
|
|
"{$package->getSourceDir()}\\win32\\build\\config.w32",
|
||
|
|
"ADD_FLAG('LDFLAGS', '$(BUILD_DIR)\\\\Zend\\\\make_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj');",
|
||
|
|
''
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fix PHP VS version
|
||
|
|
// get vs version
|
||
|
|
$vc = WindowsUtil::findVisualStudio();
|
||
|
|
$vc_matches = match ($vc['major_version']) {
|
||
|
|
'17' => ['VS17', 'Visual C++ 2022'],
|
||
|
|
'16' => ['VS16', 'Visual C++ 2019'],
|
||
|
|
default => ['unknown', 'unknown'],
|
||
|
|
};
|
||
|
|
// patch php-src/win32/build/confutils.js
|
||
|
|
FileSystem::replaceFileStr(
|
||
|
|
"{$package->getSourceDir()}\\win32\\build\\confutils.js",
|
||
|
|
'var name = "unknown";',
|
||
|
|
"var name = short ? \"{$vc_matches[0]}\" : \"{$vc_matches[1]}\";return name;"
|
||
|
|
);
|
||
|
|
|
||
|
|
// patch micro win32
|
||
|
|
if ($package->getBuildOption('enable-micro-win32') && !file_exists("{$package->getSourceDir()}\\sapi\\micro\\php_micro.c.win32bak")) {
|
||
|
|
copy("{$package->getSourceDir()}\\sapi\\micro\\php_micro.c", "{$package->getSourceDir()}\\php-src\\sapi\\micro\\php_micro.c.win32bak");
|
||
|
|
FileSystem::replaceFileStr("{$package->getSourceDir()}\\sapi\\micro\\php_micro.c", '#include "php_variables.h"', '#include "php_variables.h"' . "\n#define PHP_MICRO_WIN32_NO_CONSOLE 1");
|
||
|
|
} else {
|
||
|
|
if (file_exists("{$package->getSourceDir()}\\sapi\\micro\\php_micro.c.win32bak")) {
|
||
|
|
rename("{$package->getSourceDir()}\\sapi\\micro\\php_micro.c.win32bak", "{$package->getSourceDir()}\\sapi\\micro\\php_micro.c");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function deployWindowsBinary(PackageBuilder $builder, TargetPackage $package, string $sapi): void
|
||
|
|
{
|
||
|
|
$rel_type = 'Release'; // TODO: Debug build support
|
||
|
|
$ts = $builder->getOption('enable-zts') ? '_TS' : '';
|
||
|
|
$debug_dir = BUILD_ROOT_PATH . '\debug';
|
||
|
|
$src = match ($sapi) {
|
||
|
|
'php-cli' => ["{$package->getSourceDir()}\\x64\\{$rel_type}{$ts}", 'php.exe', 'php.pdb'],
|
||
|
|
'php-micro' => ["{$package->getSourceDir()}\\x64\\{$rel_type}{$ts}", 'micro.sfx', 'micro.pdb'],
|
||
|
|
'php-cgi' => ["{$package->getSourceDir()}\\x64\\{$rel_type}{$ts}", 'php-cgi.exe', 'php-cgi.pdb'],
|
||
|
|
default => throw new SPCInternalException("Deployment does not accept type {$sapi}"),
|
||
|
|
};
|
||
|
|
$src = "{$src[0]}\\{$src[1]}";
|
||
|
|
$dst = BUILD_BIN_PATH . '\\' . basename($src);
|
||
|
|
|
||
|
|
$builder->deployBinary($src, $dst);
|
||
|
|
|
||
|
|
// make debug info file path
|
||
|
|
if ($builder->getOption('no-strip', false) && file_exists("{$src[0]}\\{$src[2]}")) {
|
||
|
|
cmd()->exec('copy ' . escapeshellarg("{$src[0]}\\{$src[2]}") . ' ' . escapeshellarg($debug_dir));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|