mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-14 04:15:35 +08:00
fix(windows): survive cmd.exe's 8191 character command line limit
- with big extension stacks (imagick) the link command lines exceed what cmd.exe allows and fail with 'The command line is too long' - pass the collected LIBS_CLI/LIBS_CGI/LIBS_MICRO lib lists to link.exe through @response files, like php-src already does for its object lists - compile the frankenphp -mthreads shim instead of generating a .bat: batch files run through cmd.exe and cap cgo link lines at 8191 chars, a real executable gets the full 32K and past that go writes a response file by itself; still probe-gated, still a stopgap for golang/go#80290
This commit is contained in:
@@ -26,6 +26,55 @@ use ZM\Logger\ConsoleColor;
|
||||
|
||||
trait frankenphp
|
||||
{
|
||||
/**
|
||||
* Source of the -mthreads stripping shim, see windowsCgoCompilers(). The %s
|
||||
* placeholder receives the wrapped compiler path as a C string literal.
|
||||
*/
|
||||
private const string MTHREADS_SHIM_C = <<<'C'
|
||||
// Generated by static-php-cli. Drops the MinGW-only -mthreads flag Go passes to
|
||||
// cgo (golang/go#80290) and forwards everything else to clang.
|
||||
#include <windows.h>
|
||||
#include <wchar.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static const wchar_t compiler[] = L"%s";
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const wchar_t *args = GetCommandLineW();
|
||||
// skip our own (possibly quoted) argv[0]
|
||||
if (*args == L'"') {
|
||||
args++;
|
||||
while (*args && *args != L'"') args++;
|
||||
if (*args) args++;
|
||||
} else {
|
||||
while (*args && *args != L' ' && *args != L'\t') args++;
|
||||
}
|
||||
|
||||
// "<compiler>" + args + trailing space so a flag at the very end still matches
|
||||
size_t len = wcslen(compiler) + wcslen(args) + 8;
|
||||
wchar_t *cmd = calloc(len, sizeof(wchar_t));
|
||||
if (cmd == NULL) return 1;
|
||||
swprintf(cmd, len, L"\"%%ls\"%%ls ", compiler, args);
|
||||
|
||||
// remove every space-delimited -mthreads token
|
||||
wchar_t *p;
|
||||
while ((p = wcsstr(cmd, L" -mthreads ")) != NULL) {
|
||||
wmemmove(p, p + 10, wcslen(p + 10) + 1);
|
||||
}
|
||||
|
||||
STARTUPINFOW si = {sizeof(si)};
|
||||
PROCESS_INFORMATION pi;
|
||||
if (!CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
|
||||
return 127;
|
||||
}
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
DWORD code = 1;
|
||||
GetExitCodeProcess(pi.hProcess, &code);
|
||||
return (int) code;
|
||||
}
|
||||
C;
|
||||
|
||||
#[Stage]
|
||||
public function buildFrankenphpForUnix(TargetPackage $package, PackageInstaller $installer, ToolchainInterface $toolchain, PackageBuilder $builder): void
|
||||
{
|
||||
@@ -382,8 +431,12 @@ trait frankenphp
|
||||
* Return the [CC, CXX] cgo should use to build FrankenPHP on Windows.
|
||||
*
|
||||
* Go passes the MinGW-only `-mthreads` flag to the C compiler for cgo builds;
|
||||
* Clang >= 20 rejects it for the MSVC target. When it does, wrap Clang with
|
||||
* a tiny .bat that strips the flag before forwarding.
|
||||
* Clang >= 20 rejects it for the MSVC target. When it does, wrap Clang with a
|
||||
* small compiled shim that strips the flag before forwarding. An executable and
|
||||
* not a .bat on purpose: batch files run through cmd.exe, which caps the command
|
||||
* line at 8191 characters, and cgo link steps with big extension stacks (imagick)
|
||||
* blow past that. A real process gets the full ~32K limit, and beyond that Go
|
||||
* switches to a response file by itself.
|
||||
*
|
||||
* Workaround for https://github.com/golang/go/issues/80290, remove once Go
|
||||
* stops passing the flag.
|
||||
@@ -403,14 +456,17 @@ trait frankenphp
|
||||
|
||||
logger()->info('Clang rejects -mthreads; wrapping it to strip the flag (golang/go#80290)');
|
||||
$dir = dirname($clang);
|
||||
$cc = $package->getSourceDir() . '\cc-nothreads.bat';
|
||||
$cxx = $package->getSourceDir() . '\cxx-nothreads.bat';
|
||||
// %*: the whole command line; the substring replace drops the -mthreads token,
|
||||
// preserving quoting of paths that a for-loop would mangle.
|
||||
file_put_contents($cc, "@echo off\r\nset \"a=%*\"\r\nset \"a=%a: -mthreads = %\"\r\n\"{$dir}\\clang.exe\" %a%\r\n");
|
||||
file_put_contents($cxx, "@echo off\r\nset \"a=%*\"\r\nset \"a=%a: -mthreads = %\"\r\n\"{$dir}\\clang++.exe\" %a%\r\n");
|
||||
$wrappers = [];
|
||||
foreach (['cc-nothreads' => 'clang.exe', 'cxx-nothreads' => 'clang++.exe'] as $name => $compiler) {
|
||||
$src = $package->getSourceDir() . "\\{$name}.c";
|
||||
$exe = $package->getSourceDir() . "\\{$name}.exe";
|
||||
file_put_contents($src, sprintf(self::MTHREADS_SHIM_C, str_replace('\\', '\\\\', "{$dir}\\{$compiler}")));
|
||||
cmd()->cd($package->getSourceDir())
|
||||
->exec('"' . $clang . '" -O2 ' . escapeshellarg($src) . ' -o ' . escapeshellarg($exe));
|
||||
$wrappers[] = $exe;
|
||||
}
|
||||
|
||||
return [$cc, $cxx];
|
||||
return $wrappers;
|
||||
}
|
||||
|
||||
protected function getFrankenPHPVersion(TargetPackage $package): string
|
||||
|
||||
Reference in New Issue
Block a user