diff --git a/bin/spc-debug.ps1 b/bin/spc-debug.ps1 new file mode 100644 index 000000000..015dae9c9 --- /dev/null +++ b/bin/spc-debug.ps1 @@ -0,0 +1,12 @@ +$PHP_Exec = ".\runtime\php.exe" + +if (-not(Test-Path $PHP_Exec)) { + $PHP_Exec = Get-Command php.exe -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Definition + if (-not $PHP_Exec) { + Write-Host "Error: PHP not found, you need to install PHP on your system or use 'bin/setup-runtime'." -ForegroundColor Red + exit 1 + } +} + +& "$PHP_Exec" -d xdebug.mode=debug -d xdebug.client_host=127.0.0.1 -d xdebug.client_port=9003 -d xdebug.start_with_request=yes ("bin/spc") @args +exit $LASTEXITCODE diff --git a/config/pkg/lib/brotli.yml b/config/pkg/lib/brotli.yml index 524f9ddc8..c88b93b5a 100644 --- a/config/pkg/lib/brotli.yml +++ b/config/pkg/lib/brotli.yml @@ -15,3 +15,7 @@ brotli: - libbrotlicommon - libbrotlidec - libbrotlienc + static-libs@windows: + - brotlicommon.lib + - brotlidec.lib + - brotlienc.lib diff --git a/config/pkg/lib/bzip2.yml b/config/pkg/lib/bzip2.yml index 1cd36bd7e..f9e1870d8 100644 --- a/config/pkg/lib/bzip2.yml +++ b/config/pkg/lib/bzip2.yml @@ -16,3 +16,6 @@ bzip2: - bzlib.h static-libs@unix: - libbz2.a + static-libs@windows: + - libbz2.lib + - libbz2_a.lib diff --git a/config/pkg/lib/freetype.yml b/config/pkg/lib/freetype.yml index c101a174b..df7dc22a8 100644 --- a/config/pkg/lib/freetype.yml +++ b/config/pkg/lib/freetype.yml @@ -11,6 +11,7 @@ freetype: depends: - zlib suggests: + - libpng - bzip2 - brotli headers@unix: diff --git a/config/pkg/lib/icu.yml b/config/pkg/lib/icu.yml index 43328228d..e42dcb6c3 100644 --- a/config/pkg/lib/icu.yml +++ b/config/pkg/lib/icu.yml @@ -6,11 +6,20 @@ icu: repo: unicode-org/icu match: icu4c.+-src\.tgz prefer-stable: true + binary: + windows-x86_64: { type: url, url: 'https://dl.static-php.dev/static-php-cli/deps/icu-static-windows-x64/icu-static-windows-x64.zip', extract: hosted } metadata: - license-files: [LICENSE] + license-files: ['@/icu.txt'] license: ICU + headers@windows: + - unicode lang: cpp pkg-configs: - icu-uc - icu-i18n - icu-io + static-libs@windows: + - icudt.lib + - icuin.lib + - icuio.lib + - icuuc.lib diff --git a/config/pkg/lib/libiconv-win.yml b/config/pkg/lib/libiconv-win.yml new file mode 100644 index 000000000..103acf2e9 --- /dev/null +++ b/config/pkg/lib/libiconv-win.yml @@ -0,0 +1,13 @@ +libiconv-win: + type: library + artifact: + source: + type: git + rev: master + url: 'https://github.com/static-php/libiconv-win.git' + metadata: + license-files: [source/COPYING] + license: GPL-3.0-or-later + static-libs@windows: + - libiconv.lib + - libiconv_a.lib diff --git a/config/pkg/lib/libpng.yml b/config/pkg/lib/libpng.yml index e8831d60c..083cf430a 100644 --- a/config/pkg/lib/libpng.yml +++ b/config/pkg/lib/libpng.yml @@ -14,3 +14,6 @@ libpng: - zlib static-libs@unix: - libpng16.a + static-libs@windows: + - libpng16_static.lib + - libpng_a.lib diff --git a/config/pkg/lib/libssh2.yml b/config/pkg/lib/libssh2.yml index 8e1d82754..2916e3a9c 100644 --- a/config/pkg/lib/libssh2.yml +++ b/config/pkg/lib/libssh2.yml @@ -20,3 +20,5 @@ libssh2: - libssh2 static-libs@unix: - libssh2.a + static-libs@windows: + - libssh2.lib diff --git a/config/pkg/lib/libxml2.yml b/config/pkg/lib/libxml2.yml index 7e86b5af5..5ffdc8b27 100644 --- a/config/pkg/lib/libxml2.yml +++ b/config/pkg/lib/libxml2.yml @@ -12,7 +12,13 @@ libxml2: - libiconv - zlib - xz + depends@windows: + - zlib + - libiconv-win headers: - libxml2 pkg-configs: - libxml-2.0 + static-libs@windows: + - libxml2s.lib + - libxml2_a.lib diff --git a/config/pkg/lib/nghttp2.yml b/config/pkg/lib/nghttp2.yml index 166c33ac5..11521d5a3 100644 --- a/config/pkg/lib/nghttp2.yml +++ b/config/pkg/lib/nghttp2.yml @@ -22,3 +22,5 @@ nghttp2: - libnghttp2 static-libs@unix: - libnghttp2.a + static-libs@windows: + - nghttp2.lib diff --git a/config/pkg/lib/nghttp3.yml b/config/pkg/lib/nghttp3.yml index f9adc05b5..272172b99 100644 --- a/config/pkg/lib/nghttp3.yml +++ b/config/pkg/lib/nghttp3.yml @@ -17,3 +17,5 @@ nghttp3: - libnghttp3 static-libs@unix: - libnghttp3.a + static-libs@windows: + - nghttp3.lib diff --git a/config/pkg/lib/ngtcp2.yml b/config/pkg/lib/ngtcp2.yml index c864739a7..8984ca729 100644 --- a/config/pkg/lib/ngtcp2.yml +++ b/config/pkg/lib/ngtcp2.yml @@ -11,9 +11,6 @@ ngtcp2: license: MIT depends: - openssl - suggests: - - nghttp3 - - brotli headers: - ngtcp2 pkg-configs: @@ -22,3 +19,5 @@ ngtcp2: static-libs@unix: - libngtcp2.a - libngtcp2_crypto_ossl.a + static-libs@windows: + - ngtcp2.lib diff --git a/config/pkg/lib/onig.yml b/config/pkg/lib/onig.yml index fa06524dc..c2ef658af 100644 --- a/config/pkg/lib/onig.yml +++ b/config/pkg/lib/onig.yml @@ -13,3 +13,6 @@ onig: - oniguruma.h static-libs@unix: - libonig.a + static-libs@windows: + - onig.lib + - onig_a.lib diff --git a/config/pkg/lib/openssl.yml b/config/pkg/lib/openssl.yml index 22d065088..161cdcebc 100644 --- a/config/pkg/lib/openssl.yml +++ b/config/pkg/lib/openssl.yml @@ -21,3 +21,6 @@ openssl: static-libs@unix: - libssl.a - libcrypto.a + static-libs@windows: + - libssl.lib + - libcrypto.lib diff --git a/config/pkg/lib/zlib.yml b/config/pkg/lib/zlib.yml index cf7f11ba0..b4e71364e 100644 --- a/config/pkg/lib/zlib.yml +++ b/config/pkg/lib/zlib.yml @@ -14,3 +14,6 @@ zlib: - zconf.h static-libs@unix: - libz.a + static-libs@windows: + - zlibstatic.lib + - zlib_a.lib diff --git a/config/pkg/lib/zstd.yml b/config/pkg/lib/zstd.yml index 3d76e270c..875380d1f 100644 --- a/config/pkg/lib/zstd.yml +++ b/config/pkg/lib/zstd.yml @@ -17,3 +17,5 @@ zstd: - libzstd static-libs@unix: - libzstd.a + static-libs@windows: + - zstd_static.lib diff --git a/config/pkg/target/curl.yml b/config/pkg/target/curl.yml index 4daba8c14..78064510c 100644 --- a/config/pkg/target/curl.yml +++ b/config/pkg/target/curl.yml @@ -12,6 +12,10 @@ curl: depends@unix: - openssl - zlib + depends@windows: + - zlib + - libssh2 + - nghttp2 suggests@unix: - libssh2 - brotli @@ -23,6 +27,9 @@ curl: - ldap - idn2 - krb5 + suggests@windows: + - brotli + - zstd frameworks: - CoreFoundation - CoreServices @@ -33,3 +40,5 @@ curl: - curl static-libs@unix: - libcurl.a + static-libs@windows: + - libcurl_a.lib diff --git a/src/Package/Library/brotli.php b/src/Package/Library/brotli.php index f22b9ef29..cab05112b 100644 --- a/src/Package/Library/brotli.php +++ b/src/Package/Library/brotli.php @@ -8,11 +8,19 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixCMakeExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; use StaticPHP\Util\FileSystem; #[Library('brotli')] class brotli { + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $package): void + { + WindowsCMakeExecutor::create($package)->build(); + // FileSystem::copy("{$package->getLibDir()}\\onig.lib", "{$package->getLibDir()}\\onig_a.lib"); + } + #[BuildFor('Linux')] #[BuildFor('Darwin')] public function build(LibraryPackage $lib): void diff --git a/src/Package/Library/bzip2.php b/src/Package/Library/bzip2.php index 403773dab..90fcce7c6 100644 --- a/src/Package/Library/bzip2.php +++ b/src/Package/Library/bzip2.php @@ -20,6 +20,17 @@ public function patchBeforeBuild(LibraryPackage $lib): void FileSystem::replaceFileStr($lib->getSourceDir() . '/Makefile', 'CFLAGS=-Wall', 'CFLAGS=-fPIC -Wall'); } + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $package): void + { + cmd()->cd($package->getSourceDir()) + ->exec('nmake /nologo /f Makefile.msc CFLAGS="-DWIN32 -MT -Ox -D_FILE_OFFSET_BITS=64 -nologo" clean') + ->exec('nmake /nologo /f Makefile.msc CFLAGS="-DWIN32 -MT -Ox -D_FILE_OFFSET_BITS=64 -nologo" lib'); + FileSystem::copy("{$package->getSourceDir()}\\libbz2.lib", "{$package->getLibDir()}\\libbz2.lib"); + FileSystem::copy("{$package->getSourceDir()}\\libbz2.lib", "{$package->getLibDir()}\\libbz2_a.lib"); + FileSystem::copy("{$package->getSourceDir()}\\bzlib.h", "{$package->getIncludeDir()}\\bzlib.h"); + } + #[BuildFor('Linux')] #[BuildFor('Darwin')] public function build(LibraryPackage $lib, PackageBuilder $builder): void diff --git a/src/Package/Library/freetype.php b/src/Package/Library/freetype.php index 6cb05a90a..8a83eb5e3 100644 --- a/src/Package/Library/freetype.php +++ b/src/Package/Library/freetype.php @@ -8,6 +8,7 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixCMakeExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; use StaticPHP\Util\FileSystem; #[Library('freetype')] @@ -33,4 +34,18 @@ public function buildUnix(LibraryPackage $lib): void $lib->patchPkgconfPrefix(['freetype2.pc']); FileSystem::replaceFileStr("{$lib->getBuildRootPath()}/lib/pkgconfig/freetype2.pc", ' -L/lib ', " -L{$lib->getBuildRootPath()}/lib "); } + + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + WindowsCMakeExecutor::create($lib) + ->optionalPackage('libpng', ...cmake_boolean_args('FT_DISABLE_PNG', true)) + ->optionalPackage('bzip2', ...cmake_boolean_args('FT_DISABLE_BZIP2', true)) + ->optionalPackage('brotli', ...cmake_boolean_args('FT_DISABLE_BROTLI', true)) + ->addConfigureArgs('-DFT_DISABLE_HARFBUZZ=ON') + ->build(); + + // freetype.lib to libfreetype_a.lib + FileSystem::copy("{$lib->getLibDir()}\\freetype.lib", "{$lib->getLibDir()}\\libfreetype_a.lib"); + } } diff --git a/src/Package/Library/gmssl.php b/src/Package/Library/gmssl.php index 21b4ea668..7785e55a4 100644 --- a/src/Package/Library/gmssl.php +++ b/src/Package/Library/gmssl.php @@ -8,6 +8,8 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixCMakeExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; +use StaticPHP\Util\FileSystem; #[Library('gmssl')] class gmssl @@ -18,4 +20,35 @@ public function build(LibraryPackage $lib): void { UnixCMakeExecutor::create($lib)->build(); } + + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + $buildDir = "{$lib->getSourceDir()}\\builddir"; + + // GmSSL requires NMake Makefiles generator on Windows + WindowsCMakeExecutor::create($lib) + ->setBuildDir($buildDir) + ->setCustomDefaultArgs( + '-G "NMake Makefiles"', + '-DWIN32=ON', + '-DBUILD_SHARED_LIBS=OFF', + '-DCMAKE_BUILD_TYPE=Release', + '-DCMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG"', + '-DCMAKE_CXX_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG"', + '-DCMAKE_INSTALL_PREFIX=' . escapeshellarg($lib->getBuildRootPath()), + '-B ' . escapeshellarg($buildDir), + ) + ->toStep(1) + ->build(); + + // fix cmake_install.cmake install prefix (GmSSL overrides it internally) + $installCmake = "{$buildDir}\\cmake_install.cmake"; + FileSystem::writeFile( + $installCmake, + 'set(CMAKE_INSTALL_PREFIX "' . str_replace('\\', '/', $lib->getBuildRootPath()) . '")' . PHP_EOL . FileSystem::readFile($installCmake) + ); + + cmd()->cd($buildDir)->exec('nmake install XCFLAGS=/MT'); + } } diff --git a/src/Package/Library/libiconv_win.php b/src/Package/Library/libiconv_win.php new file mode 100644 index 000000000..b6b0531df --- /dev/null +++ b/src/Package/Library/libiconv_win.php @@ -0,0 +1,43 @@ + '\MSVC17', + '16' => '\MSVC16', + default => throw new EnvironmentException("Current VS version {$ver['major_version']} is not supported yet!"), + }; + ApplicationContext::set('vs_ver_dir', $vs_ver_dir); + } + + #[BuildFor('Windows')] + public function build(LibraryPackage $lib): void + { + $vs_ver_dir = ApplicationContext::get('vs_ver_dir'); + cmd()->cd("{$lib->getSourceDir()}{$vs_ver_dir}") + ->exec('msbuild libiconv.sln /t:Rebuild /p:Configuration=Release /p:Platform=x64'); + FileSystem::createDir($lib->getLibDir()); + FileSystem::createDir($lib->getIncludeDir()); + FileSystem::copy("{$lib->getSourceDir()}{$vs_ver_dir}\\x64\\lib\\libiconv.lib", "{$lib->getLibDir()}\\libiconv.lib"); + FileSystem::copy("{$lib->getSourceDir()}{$vs_ver_dir}\\x64\\lib\\libiconv_a.lib", "{$lib->getLibDir()}\\libiconv_a.lib"); + FileSystem::copy("{$lib->getSourceDir()}\\source\\include\\iconv.h", "{$lib->getIncludeDir()}\\iconv.h"); + } +} diff --git a/src/Package/Library/libpng.php b/src/Package/Library/libpng.php index 1d02fdd69..6a1690105 100644 --- a/src/Package/Library/libpng.php +++ b/src/Package/Library/libpng.php @@ -8,6 +8,8 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixAutoconfExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; +use StaticPHP\Util\FileSystem; #[Library('libpng')] class libpng @@ -44,4 +46,21 @@ public function buildUnix(LibraryPackage $lib): void $lib->patchPkgconfPrefix(['libpng16.pc']); $lib->patchLaDependencyPrefix(); } + + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + WindowsCMakeExecutor::create($lib) + ->addConfigureArgs( + '-DSKIP_INSTALL_PROGRAM=ON', + '-DSKIP_INSTALL_FILES=ON', + '-DPNG_STATIC=ON', + '-DPNG_SHARED=OFF', + '-DPNG_TESTS=OFF', + ) + ->build(); + + // libpng16_static.lib to libpng_a.lib + FileSystem::copy("{$lib->getLibDir()}\\libpng16_static.lib", "{$lib->getLibDir()}\\libpng_a.lib"); + } } diff --git a/src/Package/Library/libssh2.php b/src/Package/Library/libssh2.php index f71d508ac..b41434e09 100644 --- a/src/Package/Library/libssh2.php +++ b/src/Package/Library/libssh2.php @@ -8,10 +8,22 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixCMakeExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; #[Library('libssh2')] class libssh2 { + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + WindowsCMakeExecutor::create($lib) + ->addConfigureArgs( + '-DENABLE_ZLIB_COMPRESSION=ON', + '-DBUILD_TESTING=OFF' + ) + ->build(); + } + #[BuildFor('Linux')] #[BuildFor('Darwin')] public function build(LibraryPackage $lib): void diff --git a/src/Package/Library/libxml2.php b/src/Package/Library/libxml2.php index 3f8b3e71f..108b9962f 100644 --- a/src/Package/Library/libxml2.php +++ b/src/Package/Library/libxml2.php @@ -7,12 +7,33 @@ use StaticPHP\Attribute\Package\BuildFor; use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; +use StaticPHP\Package\PackageInstaller; use StaticPHP\Runtime\Executor\UnixCMakeExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; use StaticPHP\Util\FileSystem; #[Library('libxml2')] class libxml2 { + #[BuildFor('Windows')] + public function buildForWindows(LibraryPackage $lib, PackageInstaller $installer): void + { + $iconv_win = $installer->getLibraryPackage('libiconv-win'); + WindowsCMakeExecutor::create($lib) + ->addConfigureArgs( + '-DLIBXML2_WITH_ICONV=ON', + "-DIconv_LIBRARY={$iconv_win->getLibDir()}", + "-DIconv_INCLUDE_DIR={$iconv_win->getIncludeDir()}", + '-DLIBXML2_WITH_ZLIB=ON', + '-DLIBXML2_WITH_PYTHON=OFF', + '-DLIBXML2_WITH_LZMA=OFF', + '-DLIBXML2_WITH_PROGRAMS=OFF', + '-DLIBXML2_WITH_TESTS=OFF', + ) + ->build(); + FileSystem::copy("{$lib->getLibDir()}\\libxml2s.lib", "{$lib->getLibDir()}\\libxml2_a.lib"); + } + #[BuildFor('Linux')] public function buildForLinux(LibraryPackage $lib): void { diff --git a/src/Package/Library/nghttp2.php b/src/Package/Library/nghttp2.php index 3a85ada4a..f09659470 100644 --- a/src/Package/Library/nghttp2.php +++ b/src/Package/Library/nghttp2.php @@ -8,10 +8,26 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixAutoconfExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; #[Library('nghttp2')] class nghttp2 { + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + WindowsCMakeExecutor::create($lib) + ->addConfigureArgs( + '-DENABLE_SHARED_LIB=OFF', + '-DENABLE_STATIC_LIB=ON', + '-DENABLE_STATIC_CRT=ON', + '-DENABLE_LIB_ONLY=ON', + '-DENABLE_DOC=OFF', + '-DBUILD_TESTING=OFF', + ) + ->build(); + } + #[BuildFor('Linux')] #[BuildFor('Darwin')] public function build(LibraryPackage $lib): void diff --git a/src/Package/Library/nghttp3.php b/src/Package/Library/nghttp3.php index 1f686b7b5..4659c5711 100644 --- a/src/Package/Library/nghttp3.php +++ b/src/Package/Library/nghttp3.php @@ -8,10 +8,26 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixAutoconfExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; #[Library('nghttp3')] class nghttp3 { + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + WindowsCMakeExecutor::create($lib) + ->addConfigureArgs( + '-DENABLE_SHARED_LIB=OFF', + '-DENABLE_STATIC_LIB=ON', + '-DBUILD_STATIC_LIBS=ON', + '-DBUILD_SHARED_LIBS=OFF', + '-DENABLE_STATIC_CRT=ON', + '-DENABLE_LIB_ONLY=ON', + ) + ->build(); + } + #[BuildFor('Linux')] #[BuildFor('Darwin')] public function build(LibraryPackage $lib): void diff --git a/src/Package/Library/ngtcp2.php b/src/Package/Library/ngtcp2.php index 15821225b..c88b643bf 100644 --- a/src/Package/Library/ngtcp2.php +++ b/src/Package/Library/ngtcp2.php @@ -8,10 +8,27 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixAutoconfExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; #[Library('ngtcp2')] class ngtcp2 { + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + WindowsCMakeExecutor::create($lib) + ->addConfigureArgs( + '-DENABLE_SHARED_LIB=OFF', + '-DENABLE_STATIC_LIB=ON', + '-DBUILD_STATIC_LIBS=ON', + '-DBUILD_SHARED_LIBS=OFF', + '-DENABLE_STATIC_CRT=ON', + '-DENABLE_LIB_ONLY=ON', + '-DENABLE_OPENSSL=ON', + ) + ->build(); + } + #[BuildFor('Linux')] #[BuildFor('Darwin')] public function build(LibraryPackage $lib): void @@ -26,18 +43,6 @@ public function build(LibraryPackage $lib): void ]), '--with-openssl=no' ) - ->optionalPackage('nghttp3', ...ac_with_args('libnghttp3', true)) - ->optionalPackage( - 'brotli', - fn (LibraryPackage $brotli) => implode(' ', [ - '--with-brotlidec=yes', - "LIBBROTLIDEC_CFLAGS=\"-I{$brotli->getIncludeDir()}\"", - "LIBBROTLIDEC_LIBS=\"{$brotli->getStaticLibFiles()}\"", - '--with-libbrotlienc=yes', - "LIBBROTLIENC_CFLAGS=\"-I{$brotli->getIncludeDir()}\"", - "LIBBROTLIENC_LIBS=\"{$brotli->getStaticLibFiles()}\"", - ]) - ) ->appendEnv(['PKG_CONFIG' => '$PKG_CONFIG --static']) ->configure('--enable-lib-only') ->make(); diff --git a/src/Package/Library/openssl.php b/src/Package/Library/openssl.php index 541b6145f..a01d01b0b 100644 --- a/src/Package/Library/openssl.php +++ b/src/Package/Library/openssl.php @@ -6,13 +6,69 @@ use StaticPHP\Attribute\Package\BuildFor; use StaticPHP\Attribute\Package\Library; +use StaticPHP\Attribute\Package\Validate; +use StaticPHP\DI\ApplicationContext; +use StaticPHP\Exception\EnvironmentException; use StaticPHP\Package\LibraryPackage; +use StaticPHP\Runtime\SystemTarget; use StaticPHP\Util\FileSystem; use StaticPHP\Util\System\LinuxUtil; +use StaticPHP\Util\System\WindowsUtil; #[Library('openssl')] class openssl { + #[Validate] + public function validate(): void + { + if (SystemTarget::getTargetOS() === 'Windows') { + global $argv; + $perl_path_native = PKG_ROOT_PATH . '\strawberry-perl-' . arch2gnu(php_uname('m')) . '-win\perl\bin\perl.exe'; + $perl = file_exists($perl_path_native) ? ($perl_path_native) : WindowsUtil::findCommand('perl.exe'); + if ($perl === null) { + throw new EnvironmentException( + 'You need to install perl first!', + "Please run \"{$argv[0]} doctor\" to fix the environment.", + ); + } + ApplicationContext::set('perl', $perl); + } + } + + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + $perl = ApplicationContext::get('perl'); + $cmd = cmd()->cd($lib->getSourceDir()) + ->exec( + "{$perl} Configure zlib VC-WIN64A " . + 'no-shared ' . + '--prefix=' . quote($lib->getBuildRootPath()) . ' ' . + '--with-zlib-lib=' . quote($lib->getLibDir()) . ' ' . + '--with-zlib-include=' . quote($lib->getIncludeDir()) . ' ' . + '--release ' . + 'no-legacy ' + ); + + // patch zlib + FileSystem::replaceFileStr("{$lib->getSourceDir()}\\Makefile", 'ZLIB1', 'zlibstatic.lib'); + // patch debug: https://stackoverflow.com/questions/18486243/how-do-i-build-openssl-statically-linked-against-windows-runtime + FileSystem::replaceFileStr("{$lib->getSourceDir()}\\Makefile", '/debug', '/incremental:no /opt:icf /dynamicbase /nxcompat /ltcg /nodefaultlib:msvcrt'); + + // build + $cmd->exec("nmake install_dev CNF_LDFLAGS=\"/NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:msvcrt /NODEFAULTLIB:msvcrtd /DEFAULTLIB:libcmt /LIBPATH:{$lib->getLibDir()} zlibstatic.lib\""); + + // copy necessary c files + FileSystem::copy("{$lib->getSourceDir()}\\ms\\applink.c", "{$lib->getIncludeDir()}\\openssl\\applink.c"); + + // patch cmake outputs + FileSystem::replaceFileRegex( + "{$lib->getLibDir()}\\cmake\\OpenSSL\\OpenSSLConfig.cmake", + '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', + 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}" ws2_32.lib gdi32.lib advapi32.lib crypt32.lib user32.lib)' + ); + } + #[BuildFor('Darwin')] public function buildForDarwin(LibraryPackage $pkg): void { diff --git a/src/Package/Library/zlib.php b/src/Package/Library/zlib.php index 8706dfe9b..f45b942c9 100644 --- a/src/Package/Library/zlib.php +++ b/src/Package/Library/zlib.php @@ -8,6 +8,8 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixAutoconfExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; +use StaticPHP\Util\FileSystem; #[Library('zlib')] class zlib @@ -21,4 +23,28 @@ public function build(LibraryPackage $lib): void // Patch pkg-config file $lib->patchPkgconfPrefix(['zlib.pc'], PKGCONF_PATCH_PREFIX); } + + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + WindowsCMakeExecutor::create($lib)->build(); + $detect_list = [ + 'zlibstatic.lib', + 'zs.lib', + 'libzs.lib', + ]; + foreach ($detect_list as $item) { + if (file_exists("{$lib->getLibDir()}\\{$item}")) { + FileSystem::copy("{$lib->getLibDir()}\\{$item}", "{$lib->getLibDir()}\\zlib_a.lib"); + FileSystem::copy("{$lib->getLibDir()}\\{$item}", "{$lib->getLibDir()}\\zlibstatic.lib"); + break; + } + } + FileSystem::removeFileIfExists("{$lib->getBinDir()}\\zlib.dll"); + FileSystem::removeFileIfExists("{$lib->getLibDir()}\\zlib.lib"); + FileSystem::removeFileIfExists("{$lib->getLibDir()}\\libz.dll"); + FileSystem::removeFileIfExists("{$lib->getLibDir()}\\libz.lib"); + FileSystem::removeFileIfExists("{$lib->getLibDir()}\\z.lib"); + FileSystem::removeFileIfExists("{$lib->getLibDir()}\\z.dll"); + } } diff --git a/src/Package/Library/zstd.php b/src/Package/Library/zstd.php index ab538358e..f12bf3e02 100644 --- a/src/Package/Library/zstd.php +++ b/src/Package/Library/zstd.php @@ -8,10 +8,24 @@ use StaticPHP\Attribute\Package\Library; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixCMakeExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; #[Library('zstd')] class zstd { + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $package): void + { + WindowsCMakeExecutor::create($package) + ->setWorkingDir("{$package->getSourceDir()}/build/cmake") + ->setBuildDir("{$package->getSourceDir()}/build/cmake/build") + ->addConfigureArgs( + '-DZSTD_BUILD_STATIC=ON', + '-DZSTD_BUILD_SHARED=OFF', + ) + ->build(); + } + #[BuildFor('Linux')] #[BuildFor('Darwin')] public function build(LibraryPackage $lib): void diff --git a/src/Package/Target/curl.php b/src/Package/Target/curl.php index dbfa8f7a1..43a2904b7 100644 --- a/src/Package/Target/curl.php +++ b/src/Package/Target/curl.php @@ -10,6 +10,7 @@ use StaticPHP\Attribute\PatchDescription; use StaticPHP\Package\LibraryPackage; use StaticPHP\Runtime\Executor\UnixCMakeExecutor; +use StaticPHP\Runtime\Executor\WindowsCMakeExecutor; use StaticPHP\Runtime\SystemTarget; use StaticPHP\Util\FileSystem; @@ -20,7 +21,9 @@ class curl #[PatchDescription('Remove CMAKE_C_IMPLICIT_LINK_LIBRARIES and fix macOS framework detection')] public function patchBeforeBuild(LibraryPackage $lib): bool { - shell()->cd($lib->getSourceDir())->exec('sed -i.save s@\${CMAKE_C_IMPLICIT_LINK_LIBRARIES}@@ ./CMakeLists.txt'); + if (SystemTarget::getTargetOS() !== 'Windows') { + shell()->cd($lib->getSourceDir())->exec('sed -i.save s@\${CMAKE_C_IMPLICIT_LINK_LIBRARIES}@@ ./CMakeLists.txt'); + } if (SystemTarget::getTargetOS() === 'Darwin') { FileSystem::replaceFileRegex("{$lib->getSourceDir()}/CMakeLists.txt", '/NOT COREFOUNDATION_FRAMEWORK/m', 'FALSE'); FileSystem::replaceFileRegex("{$lib->getSourceDir()}/CMakeLists.txt", '/NOT SYSTEMCONFIGURATION_FRAMEWORK/m', 'FALSE'); @@ -29,6 +32,34 @@ public function patchBeforeBuild(LibraryPackage $lib): bool return true; } + #[BuildFor('Windows')] + public function buildWin(LibraryPackage $lib): void + { + WindowsCMakeExecutor::create($lib) + ->optionalPackage('zstd', ...cmake_boolean_args('CURL_ZSTD')) + ->optionalPackage('brotli', ...cmake_boolean_args('CURL_BROTLI')) + ->addConfigureArgs( + '-DBUILD_CURL_EXE=OFF', + '-DZSTD_LIBRARY=zstd_static.lib', + '-DBUILD_TESTING=OFF', + '-DBUILD_EXAMPLES=OFF', + '-DUSE_LIBIDN2=OFF', + '-DCURL_USE_LIBPSL=OFF', + '-DUSE_WINDOWS_SSPI=ON', + '-DCURL_USE_SCHANNEL=ON', + '-DCURL_USE_OPENSSL=OFF', + '-DCURL_ENABLE_SSL=ON', + '-DUSE_NGHTTP2=ON', + '-DSHARE_LIB_OBJECT=OFF', + '-DCURL_USE_LIBSSH2=ON', + '-DENABLE_IPV6=ON', + ) + ->build(); + // move libcurl.lib to libcurl_a.lib + rename("{$lib->getLibDir()}\\libcurl.lib", "{$lib->getLibDir()}\\libcurl_a.lib"); + FileSystem::replaceFileStr("{$lib->getIncludeDir()}\\curl\\curl.h", '#ifdef CURL_STATICLIB', '#if 1'); + } + #[BuildFor('Linux')] #[BuildFor('Darwin')] public function build(LibraryPackage $lib): void diff --git a/src/StaticPHP/Artifact/ArtifactExtractor.php b/src/StaticPHP/Artifact/ArtifactExtractor.php index 4d38a84bd..8b73243a4 100644 --- a/src/StaticPHP/Artifact/ArtifactExtractor.php +++ b/src/StaticPHP/Artifact/ArtifactExtractor.php @@ -468,6 +468,9 @@ protected function extractArchive(string $filename, string $target): void if ($extname !== 'exe' && !is_dir($target)) { FileSystem::createDir($target); + if (!is_dir($target)) { + throw new FileSystemException("Failed to create target directory: {$target}"); + } } match (SystemTarget::getTargetOS()) { 'Windows' => match ($extname) { diff --git a/src/StaticPHP/Doctor/Doctor.php b/src/StaticPHP/Doctor/Doctor.php index 1239a30c8..05f3c4a1d 100644 --- a/src/StaticPHP/Doctor/Doctor.php +++ b/src/StaticPHP/Doctor/Doctor.php @@ -147,13 +147,17 @@ private static function getLockPath(): string { if (SystemTarget::getTargetOS() === 'Windows') { $trial_ls = [ - getenv('LOCALAPPDATA') ?: ((getenv('USERPROFILE') ?: 'C:\Users\Default') . '\AppData\Local') . '\.spc-doctor.lock', + getenv('LOCALAPPDATA') ? + (getenv('LOCALAPPDATA') . '\.spc-doctor.lock') : + (((getenv('USERPROFILE') ?: 'C:\Users\Default') . '\AppData\Local') . '\.spc-doctor.lock'), sys_get_temp_dir() . '\.spc-doctor.lock', WORKING_DIR . '\.spc-doctor.lock', ]; } else { $trial_ls = [ - getenv('XDG_CACHE_HOME') ?: ((getenv('HOME') ?: '/tmp') . '/.cache') . '/.spc-doctor.lock', + getenv('XDG_CACHE_HOME') ? + (getenv('XDG_CACHE_HOME') . '/.spc-doctor.lock') + : (((getenv('HOME') ?: '/tmp') . '/.cache') . '/.spc-doctor.lock'), sys_get_temp_dir() . '/.spc-doctor.lock', WORKING_DIR . '/.spc-doctor.lock', ]; diff --git a/src/StaticPHP/Package/LibraryPackage.php b/src/StaticPHP/Package/LibraryPackage.php index aa24f057c..769612b9f 100644 --- a/src/StaticPHP/Package/LibraryPackage.php +++ b/src/StaticPHP/Package/LibraryPackage.php @@ -44,18 +44,20 @@ public function isInstalled(): bool return false; } } - foreach (PackageConfig::get($this->getName(), 'pkg-configs', []) as $pc) { - if (!str_ends_with($pc, '.pc')) { - $pc .= '.pc'; - } - if (!file_exists("{$this->getLibDir()}/pkgconfig/{$pc}")) { - return false; + if (SystemTarget::getTargetOS() !== 'Windows') { + foreach (PackageConfig::get($this->getName(), 'pkg-configs', []) as $pc) { + if (!str_ends_with($pc, '.pc')) { + $pc .= '.pc'; + } + if (!file_exists("{$this->getLibDir()}/pkgconfig/{$pc}")) { + 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; diff --git a/src/StaticPHP/Package/PackageInstaller.php b/src/StaticPHP/Package/PackageInstaller.php index 417c4e1b6..f5c03a7de 100644 --- a/src/StaticPHP/Package/PackageInstaller.php +++ b/src/StaticPHP/Package/PackageInstaller.php @@ -168,10 +168,23 @@ public function run(bool $disable_delay_msg = false): void // check download if ($this->download) { $downloaderOptions = DownloaderOptions::extractFromConsoleOptions($this->options, 'dl'); - $downloader = new ArtifactDownloader( - [...$downloaderOptions, 'source-only' => implode(',', array_map(fn ($x) => $x->getName(), $this->build_packages))], - $this->interactive + // Collect packages that have no build stage for current OS but do have a platform binary. + // These must always download binary (not source), regardless of global prefer-source setting. + $binary_only_packages = array_filter( + $this->packages, + fn ($p) => $p instanceof LibraryPackage + && !$this->isBuildPackage($p) + && !$p->hasStage('build') + && ($p->getArtifact()?->hasPlatformBinary() ?? false) ); + $dl_opts = [ + ...$downloaderOptions, + 'source-only' => implode(',', array_map(fn ($x) => $x->getName(), $this->build_packages)), + ]; + if ($binary_only_packages !== []) { + $dl_opts['binary-only'] = implode(',', array_map(fn ($x) => $x->getName(), $binary_only_packages)); + } + $downloader = new ArtifactDownloader($dl_opts, $this->interactive); $downloader->addArtifacts($this->getArtifacts())->download(); } else { logger()->notice('Skipping download (--no-download option enabled)'); @@ -716,10 +729,13 @@ private function validatePackagesBeforeBuild(): void } $is_to_build = $this->isBuildPackage($package); $has_build_stage = $package instanceof LibraryPackage && $package->hasStage('build'); - $should_use_binary = $package instanceof LibraryPackage && ($package->getArtifact()?->shouldUseBinary() ?? false); + // Use hasPlatformBinary() here (not shouldUseBinary()) because this runs before download, + // so the binary is not yet on disk. We only need to know if a binary is declared for + // the current platform in the artifact config. + $has_platform_binary = $package instanceof LibraryPackage && ($package->getArtifact()?->hasPlatformBinary() ?? false); // Check if package can neither be built nor installed - if (!$is_to_build && !$should_use_binary && !$has_build_stage) { + if (!$is_to_build && !$has_platform_binary && !$has_build_stage) { throw new WrongUsageException("Package '{$package->getName()}' cannot be installed: no build stage defined and no binary artifact available for current OS: " . SystemTarget::getCurrentPlatformString()); } } diff --git a/src/StaticPHP/Runtime/Executor/WindowsCMakeExecutor.php b/src/StaticPHP/Runtime/Executor/WindowsCMakeExecutor.php index 9e0978196..1f057f126 100644 --- a/src/StaticPHP/Runtime/Executor/WindowsCMakeExecutor.php +++ b/src/StaticPHP/Runtime/Executor/WindowsCMakeExecutor.php @@ -176,6 +176,12 @@ public function getConfigureArgsString(): string return implode(' ', array_merge($this->configure_args, $this->getDefaultCMakeArgs())); } + public function setWorkingDir(string $dir): static + { + $this->cmd = $this->cmd->cd($dir); + return $this; + } + /** * Returns the default CMake args. */ @@ -207,12 +213,12 @@ private function makeCmakeToolchainFile(): string private function initBuildDir(): void { if ($this->build_dir === null) { - $this->build_dir = "{$this->package->getSourceDir()}\\build"; + $this->build_dir = "{$this->package->getSourceRoot()}\\build"; } } private function initCmd(): void { - $this->cmd = cmd()->cd($this->package->getSourceDir()); + $this->cmd = cmd()->cd($this->package->getSourceRoot()); } } diff --git a/src/StaticPHP/Runtime/Shell/DefaultShell.php b/src/StaticPHP/Runtime/Shell/DefaultShell.php index 66dfb7ab0..77dbf94a0 100644 --- a/src/StaticPHP/Runtime/Shell/DefaultShell.php +++ b/src/StaticPHP/Runtime/Shell/DefaultShell.php @@ -132,7 +132,7 @@ public function executeTarExtract(string $archive_path, string $target_path, str }; $mute = $this->console_putput ? '' : ' 2>/dev/null'; - $cmd = "tar {$compression_flag}xf {$archive_arg} --strip-components {$strip} -C {$target_arg}{$mute}"; + $cmd = "\"C:\\Windows\\system32\\tar.exe\" {$compression_flag}xf {$archive_arg} --strip-components {$strip} -C {$target_arg}{$mute}"; $this->logCommandInfo($cmd); logger()->debug("[TAR EXTRACT] {$cmd}"); @@ -187,7 +187,7 @@ public function execute7zExtract(string $archive_path, string $target_path): boo $extname = FileSystem::extname($archive_path); match ($extname) { 'tar' => $this->executeTarExtract($archive_path, $target_path, 'none'), - 'gz', 'tgz', 'xz', 'txz', 'bz2' => $run("{$_7z} x -so {$archive_arg} | tar -f - -x -C {$target_arg} --strip-components 1"), + 'gz', 'tgz', 'xz', 'txz', 'bz2' => $run("{$_7z} x -so {$archive_arg} | \"C:\\Windows\\system32\\tar.exe\" -f - -x -C {$target_arg} --strip-components 1"), default => $run("{$_7z} x {$archive_arg} -o{$target_arg} -y{$mute}"), }; diff --git a/src/StaticPHP/Util/FileSystem.php b/src/StaticPHP/Util/FileSystem.php index 3015b4891..144f81eb3 100644 --- a/src/StaticPHP/Util/FileSystem.php +++ b/src/StaticPHP/Util/FileSystem.php @@ -142,6 +142,9 @@ public static function copy(string $from, string $to): bool logger()->debug("Copying file from {$from} to {$to}"); $dst_path = FileSystem::convertPath($to); $src_path = FileSystem::convertPath($from); + if ($src_path === $dst_path) { + return true; + } if (!copy($src_path, $dst_path)) { throw new FileSystemException('Cannot copy file from ' . $src_path . ' to ' . $dst_path); } diff --git a/src/StaticPHP/Util/System/WindowsUtil.php b/src/StaticPHP/Util/System/WindowsUtil.php index a6df41564..b6d943be4 100644 --- a/src/StaticPHP/Util/System/WindowsUtil.php +++ b/src/StaticPHP/Util/System/WindowsUtil.php @@ -4,12 +4,15 @@ namespace StaticPHP\Util\System; +use StaticPHP\Exception\EnvironmentException; use StaticPHP\Util\FileSystem; class WindowsUtil { + private static array|false|null $vsCache = null; + /** - * Find windows program using executable name. + * Find Windows program using executable name. * * @param string $name command name (xxx.exe) * @param array $paths search path (default use env path) @@ -39,8 +42,16 @@ public static function findCommand(string $name, array $paths = []): ?string */ public static function findVisualStudio(): array|false { + if (self::$vsCache !== null) { + return self::$vsCache; + } + // call vswhere (need VS and C++ tools installed), output is json $vswhere_exec = PKG_ROOT_PATH . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'vswhere.exe'; + // detect vswhere exists, if not throw error + if (!file_exists($vswhere_exec)) { + throw new EnvironmentException('vswhere.exe not found, please run `doctor` command first'); + } $args = [ '-latest', '-format', 'json', @@ -49,13 +60,13 @@ public static function findVisualStudio(): array|false $cmd = escapeshellarg($vswhere_exec) . ' ' . implode(' ', $args); $result = f_exec($cmd, $out, $code); if ($code !== 0 || !$result) { - return false; + return self::$vsCache = false; } $json = json_decode(implode("\n", $out), true); if (!is_array($json) || count($json) === 0) { - return false; + return self::$vsCache = false; } - return [ + return self::$vsCache = [ 'version' => $json[0]['installationVersion'], 'major_version' => explode('.', $json[0]['installationVersion'])[0], 'dir' => $json[0]['installationPath'], @@ -89,6 +100,7 @@ public static function makeCmakeToolchainFile(?string $cflags = null, ?string $l $ldflags = '/nodefaultlib:msvcrt /nodefaultlib:msvcrtd /defaultlib:libcmt'; } $buildroot = str_replace('\\', '\\\\', BUILD_ROOT_PATH); + $source = str_replace('\\', '/', SOURCE_PATH); $toolchain = <<