mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-08 17:35:36 +08:00
add windows support
This commit is contained in:
@@ -168,14 +168,15 @@ class Downloader
|
||||
public static function downloadFile(string $name, string $url, string $filename, ?string $move_path = null): void
|
||||
{
|
||||
logger()->debug("Downloading {$url}");
|
||||
pcntl_signal(SIGINT, function () use ($filename) {
|
||||
if (file_exists(DOWNLOAD_PATH . '/' . $filename)) {
|
||||
$cancel_func = function () use ($filename) {
|
||||
if (file_exists(FileSystem::convertPath(DOWNLOAD_PATH . '/' . $filename))) {
|
||||
logger()->warning('Deleting download file: ' . $filename);
|
||||
unlink(DOWNLOAD_PATH . '/' . $filename);
|
||||
unlink(FileSystem::convertPath(DOWNLOAD_PATH . '/' . $filename));
|
||||
}
|
||||
});
|
||||
self::curlDown(url: $url, path: DOWNLOAD_PATH . "/{$filename}");
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
};
|
||||
self::registerCancelEvent($cancel_func);
|
||||
self::curlDown(url: $url, path: FileSystem::convertPath(DOWNLOAD_PATH . "/{$filename}"));
|
||||
self::unregisterCancelEvent();
|
||||
logger()->debug("Locking {$filename}");
|
||||
self::lockSource($name, ['source_type' => 'archive', 'filename' => $filename, 'move_path' => $move_path]);
|
||||
}
|
||||
@@ -187,7 +188,7 @@ class Downloader
|
||||
*/
|
||||
public static function lockSource(string $name, array $data): void
|
||||
{
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
|
||||
if (!file_exists(FileSystem::convertPath(DOWNLOAD_PATH . '/.lock.json'))) {
|
||||
$lock = [];
|
||||
} else {
|
||||
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
|
||||
@@ -204,24 +205,25 @@ class Downloader
|
||||
*/
|
||||
public static function downloadGit(string $name, string $url, string $branch, ?string $move_path = null): void
|
||||
{
|
||||
$download_path = DOWNLOAD_PATH . "/{$name}";
|
||||
$download_path = FileSystem::convertPath(DOWNLOAD_PATH . "/{$name}");
|
||||
if (file_exists($download_path)) {
|
||||
FileSystem::removeDir($download_path);
|
||||
}
|
||||
logger()->debug("cloning {$name} source");
|
||||
$check = !defined('DEBUG_MODE') ? ' -q' : '';
|
||||
pcntl_signal(SIGINT, function () use ($download_path) {
|
||||
$cancel_func = function () use ($download_path) {
|
||||
if (is_dir($download_path)) {
|
||||
logger()->warning('Removing path ' . $download_path);
|
||||
FileSystem::removeDir($download_path);
|
||||
}
|
||||
});
|
||||
};
|
||||
self::registerCancelEvent($cancel_func);
|
||||
f_passthru(
|
||||
'git clone' . $check .
|
||||
SPC_GIT_EXEC . ' clone' . $check .
|
||||
' --config core.autocrlf=false ' .
|
||||
"--branch \"{$branch}\" " . (defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : '') . " --recursive \"{$url}\" \"{$download_path}\""
|
||||
);
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
self::unregisterCancelEvent();
|
||||
|
||||
// Lock
|
||||
logger()->debug("Locking git source {$name}");
|
||||
@@ -251,7 +253,6 @@ class Downloader
|
||||
* @param null|array $source source meta info: [type, path, rev, url, filename, regex, license]
|
||||
* @throws DownloaderException
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function downloadSource(string $name, ?array $source = null, bool $force = false): void
|
||||
{
|
||||
@@ -359,12 +360,12 @@ class Downloader
|
||||
};
|
||||
$headerArg = implode(' ', array_map(fn ($v) => '"-H' . $v . '"', $headers));
|
||||
|
||||
$cmd = "curl -sfSL {$methodArg} {$headerArg} \"{$url}\"";
|
||||
$cmd = SPC_CURL_EXEC . " -sfSL {$methodArg} {$headerArg} \"{$url}\"";
|
||||
if (getenv('CACHE_API_EXEC') === 'yes') {
|
||||
if (!file_exists(DOWNLOAD_PATH . '/.curl_exec_cache')) {
|
||||
if (!file_exists(FileSystem::convertPath(DOWNLOAD_PATH . '/.curl_exec_cache'))) {
|
||||
$cache = [];
|
||||
} else {
|
||||
$cache = json_decode(file_get_contents(DOWNLOAD_PATH . '/.curl_exec_cache'), true);
|
||||
$cache = json_decode(file_get_contents(FileSystem::convertPath(DOWNLOAD_PATH . '/.curl_exec_cache')), true);
|
||||
}
|
||||
if (isset($cache[$cmd]) && $cache[$cmd]['expire'] >= time()) {
|
||||
return $cache[$cmd]['cache'];
|
||||
@@ -375,7 +376,7 @@ class Downloader
|
||||
}
|
||||
$cache[$cmd]['cache'] = implode("\n", $output);
|
||||
$cache[$cmd]['expire'] = time() + 3600;
|
||||
file_put_contents(DOWNLOAD_PATH . '/.curl_exec_cache', json_encode($cache));
|
||||
file_put_contents(FileSystem::convertPath(DOWNLOAD_PATH . '/.curl_exec_cache'), json_encode($cache));
|
||||
return $cache[$cmd]['cache'];
|
||||
}
|
||||
f_exec($cmd, $output, $ret);
|
||||
@@ -404,7 +405,35 @@ class Downloader
|
||||
};
|
||||
$headerArg = implode(' ', array_map(fn ($v) => '"-H' . $v . '"', $headers));
|
||||
$check = !defined('DEBUG_MODE') ? 's' : '#';
|
||||
$cmd = "curl -{$check}fSL -o \"{$path}\" {$methodArg} {$headerArg} \"{$url}\"";
|
||||
$cmd = SPC_CURL_EXEC . " -{$check}fSL -o \"{$path}\" {$methodArg} {$headerArg} \"{$url}\"";
|
||||
f_passthru($cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register CTRL+C event for different OS.
|
||||
*
|
||||
* @param callable $callback callback function
|
||||
*/
|
||||
private static function registerCancelEvent(callable $callback): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
sapi_windows_set_ctrl_handler($callback);
|
||||
} elseif (extension_loaded('pcntl')) {
|
||||
pcntl_signal(SIGINT, $callback);
|
||||
} else {
|
||||
logger()->debug('You have not enabled `pcntl` extension, cannot prevent download file corruption when Ctrl+C');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unegister CTRL+C event for different OS.
|
||||
*/
|
||||
private static function unregisterCancelEvent(): void
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
sapi_windows_set_ctrl_handler(null);
|
||||
} elseif (extension_loaded('pcntl')) {
|
||||
pcntl_signal(SIGINT, SIG_IGN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,78 +160,35 @@ class FileSystem
|
||||
}
|
||||
logger()->info("extracting {$name} source to " . ($move_path ?? SOURCE_PATH . "/{$name}") . ' ...');
|
||||
try {
|
||||
$target = $move_path ?? (SOURCE_PATH . "/{$name}");
|
||||
$target = self::convertPath($move_path ?? (SOURCE_PATH . "/{$name}"));
|
||||
// Git source, just move
|
||||
if (is_dir($filename)) {
|
||||
self::copyDir($filename, $target);
|
||||
if (is_dir(self::convertPath($filename))) {
|
||||
self::copyDir(self::convertPath($filename), $target);
|
||||
self::emitSourceExtractHook($name);
|
||||
return;
|
||||
}
|
||||
if (f_mkdir(directory: $target, recursive: true) !== true) {
|
||||
throw new FileSystemException('create ' . $name . 'source dir failed');
|
||||
}
|
||||
|
||||
if (in_array(PHP_OS_FAMILY, ['Darwin', 'Linux', 'BSD'])) {
|
||||
if (f_mkdir(directory: $target, recursive: true) !== true) {
|
||||
throw new FileSystemException('create ' . $name . 'source dir failed');
|
||||
}
|
||||
switch (self::extname($filename)) {
|
||||
case 'xz':
|
||||
case 'txz':
|
||||
f_passthru("tar -xf {$filename} -C {$target} --strip-components 1");
|
||||
// f_passthru("cat {$filename} | xz -d | tar -x -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
break;
|
||||
case 'gz':
|
||||
case 'tgz':
|
||||
f_passthru("tar -xzf {$filename} -C {$target} --strip-components 1");
|
||||
break;
|
||||
case 'bz2':
|
||||
f_passthru("tar -xjf {$filename} -C {$target} --strip-components 1");
|
||||
break;
|
||||
case 'zip':
|
||||
f_passthru("unzip {$filename} -d {$target}");
|
||||
break;
|
||||
// case 'zstd':
|
||||
// case 'zst':
|
||||
// passthru('cat ' . $filename . ' | zstd -d | tar -x -C ".SOURCE_PATH . "/' . $name . ' --strip-components 1', $ret);
|
||||
// break;
|
||||
case 'tar':
|
||||
f_passthru("tar -xf {$filename} -C {$target} --strip-components 1");
|
||||
break;
|
||||
default:
|
||||
throw new FileSystemException('unknown archive format: ' . $filename);
|
||||
}
|
||||
match (self::extname($filename)) {
|
||||
'tar', 'xz', 'txz' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
|
||||
'tgz', 'gz' => f_passthru("tar -xzf {$filename} -C {$target} --strip-components 1"),
|
||||
'bz2' => f_passthru("tar -xjf {$filename} -C {$target} --strip-components 1"),
|
||||
'zip' => f_passthru("unzip {$filename} -d {$target}"),
|
||||
default => throw new FileSystemException('unknown archive format: ' . $filename),
|
||||
};
|
||||
} elseif (PHP_OS_FAMILY === 'Windows') {
|
||||
// find 7z
|
||||
$_7zExe = self::findCommandPath('7z', [
|
||||
'C:\Program Files\7-Zip-Zstandard',
|
||||
'C:\Program Files (x86)\7-Zip-Zstandard',
|
||||
'C:\Program Files\7-Zip',
|
||||
'C:\Program Files (x86)\7-Zip',
|
||||
]);
|
||||
if (!$_7zExe) {
|
||||
throw new FileSystemException('windows needs 7z to unpack');
|
||||
}
|
||||
// use php-sdk-binary-tools/bin/7za.exe
|
||||
$_7z = self::convertPath(PHP_SDK_PATH . '/bin/7za.exe');
|
||||
f_mkdir(SOURCE_PATH . "/{$name}", recursive: true);
|
||||
switch (self::extname($filename)) {
|
||||
case 'zstd':
|
||||
case 'zst':
|
||||
if (!str_contains($_7zExe, 'Zstandard')) {
|
||||
throw new FileSystemException("zstd is not supported: {$filename}");
|
||||
}
|
||||
// no break
|
||||
case 'xz':
|
||||
case 'txz':
|
||||
case 'gz':
|
||||
case 'tgz':
|
||||
case 'bz2':
|
||||
f_passthru("\"{$_7zExe}\" x -so {$filename} | tar -f - -x -C {$target} --strip-components 1");
|
||||
break;
|
||||
case 'tar':
|
||||
f_passthru("tar -xf {$filename} -C {$target} --strip-components 1");
|
||||
break;
|
||||
case 'zip':
|
||||
f_passthru("\"{$_7zExe}\" x {$filename} -o{$target}");
|
||||
break;
|
||||
default:
|
||||
throw new FileSystemException("unknown archive format: {$filename}");
|
||||
}
|
||||
match (self::extname($filename)) {
|
||||
'tar' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
|
||||
'xz', 'txz', 'gz', 'tgz', 'bz2' => f_passthru("\"{$_7z}\" x -so {$filename} | tar -f - -x -C {$target} --strip-components 1"),
|
||||
'zip' => f_passthru("\"{$_7z}\" x {$filename} -o{$target} -y"),
|
||||
default => throw new FileSystemException("unknown archive format: {$filename}"),
|
||||
};
|
||||
}
|
||||
self::emitSourceExtractHook($name);
|
||||
} catch (RuntimeException $e) {
|
||||
@@ -398,7 +355,7 @@ class FileSystem
|
||||
public static function createDir(string $path): void
|
||||
{
|
||||
if (!is_dir($path) && !f_mkdir($path, 0755, true) && !is_dir($path)) {
|
||||
throw new FileSystemException(sprintf('无法建立目录:%s', $path));
|
||||
throw new FileSystemException(sprintf('Unable to create dir: %s', $path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +365,7 @@ class FileSystem
|
||||
*/
|
||||
public static function writeFile(string $path, mixed $content, ...$args): bool|int|string
|
||||
{
|
||||
$dir = pathinfo($path, PATHINFO_DIRNAME);
|
||||
$dir = pathinfo(self::convertPath($path), PATHINFO_DIRNAME);
|
||||
if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
|
||||
throw new FileSystemException('Write file failed, cannot create parent directory: ' . $dir);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace SPC\store;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\linux\LinuxBuilder;
|
||||
use SPC\builder\unix\UnixBuilderBase;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
@@ -31,6 +32,20 @@ class SourcePatcher
|
||||
logger()->info('Extension [' . $ext->getName() . '] patched before buildconf');
|
||||
}
|
||||
}
|
||||
// patch windows php 8.1 bug
|
||||
if (PHP_OS_FAMILY === 'Windows' && $builder->getPHPVersionID() >= 80100 && $builder->getPHPVersionID() < 80200) {
|
||||
logger()->info('Patching PHP 8.1 windows Fiber bug');
|
||||
FileSystem::replaceFileStr(
|
||||
SOURCE_PATH . '\php-src\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(
|
||||
SOURCE_PATH . '\php-src\win32\build\config.w32',
|
||||
"ADD_FLAG('LDFLAGS', '$(BUILD_DIR)\\\\Zend\\\\make_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj');",
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,9 +196,10 @@ class SourcePatcher
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCPY 1$/m', '');
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCAT 1$/m', '');
|
||||
}
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', '');
|
||||
|
||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', '');
|
||||
if ($builder instanceof UnixBuilderBase) {
|
||||
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', '');
|
||||
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', '');
|
||||
}
|
||||
|
||||
// call extension patch before make
|
||||
foreach ($builder->getExts() as $ext) {
|
||||
@@ -252,4 +268,32 @@ class SourcePatcher
|
||||
@unlink($embed_c_bak);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch cli SAPI Makefile for Windows.
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchWindowsCLITarget(): void
|
||||
{
|
||||
// search Makefile code line contains "$(BUILD_DIR)\php.exe:"
|
||||
$content = FileSystem::readFile(SOURCE_PATH . '/php-src/Makefile');
|
||||
$lines = explode("\r\n", $content);
|
||||
$line_num = 0;
|
||||
$found = false;
|
||||
foreach ($lines as $v) {
|
||||
if (strpos($v, '$(BUILD_DIR)\php.exe:') !== false) {
|
||||
$found = $line_num;
|
||||
break;
|
||||
}
|
||||
++$line_num;
|
||||
}
|
||||
if ($found === false) {
|
||||
throw new RuntimeException('Cannot patch windows CLI Makefile!');
|
||||
}
|
||||
$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(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user