diff --git a/bin/spc b/bin/spc new file mode 100755 index 00000000..1c012bf9 --- /dev/null +++ b/bin/spc @@ -0,0 +1,16 @@ +#!php +run(); +} catch (Exception $e) { + \SPC\exception\ExceptionHandler::getInstance()->handle($e); +} diff --git a/config/ext.json b/config/ext.json new file mode 100644 index 00000000..a91c323d --- /dev/null +++ b/config/ext.json @@ -0,0 +1,387 @@ +{ + "bcmath": { + "type": "builtin" + }, + "bz2": { + "type": "builtin", + "arg-type": "custom", + "lib-depends": [ + "bzip2" + ] + }, + "calendar": { + "type": "builtin" + }, + "ctype": { + "type": "builtin" + }, + "curl": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "curl" + ] + }, + "dba": { + "type": "builtin", + "arg-type-windows": "with" + }, + "dom": { + "type": "builtin", + "arg-type-windows": "with", + "lib-depends": [ + "libxml2" + ] + }, + "enchant": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "enchant2" + ] + }, + "exif": { + "type": "builtin" + }, + "ffi": { + "arg-type": "with", + "type": "builtin", + "lib-depends": [ + "libffi" + ] + }, + "fileinfo": { + "type": "builtin" + }, + "filter": { + "type": "builtin" + }, + "ftp": { + "type": "builtin", + "lib-suggests": [ + "openssl" + ] + }, + "gd": { + "type": "builtin", + "arg-type-windows": "with", + "lib-depends": [ + "zlib", + "libpng" + ], + "lib-suggests": [ + "gd", + "libavif", + "libwebp", + "libjpeg", + "xpm", + "libfreetype" + ], + "lib-depends-windows": [ + "libiconv", + "libfreetype", + "libjpeg", + "zlib", + "libpng", + "xpm" + ], + "lib-suggests-windows": [ + "gd", + "libavif", + "libwebp" + ] + }, + "gettext": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "gettext" + ] + }, + "gmp": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "gmp" + ] + }, + "iconv": { + "type": "builtin", + "arg-type": "with", + "lib-depends-windows": [ + "libiconv" + ] + }, + "imap": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "imap" + ], + "lib-suggests": [ + "kerberos" + ] + }, + "intl": { + "type": "builtin", + "lib-depends": [ + "icu" + ] + }, + "ldap": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "ldap" + ] + }, + "mbregex": { + "type": "builtin", + "lib-depends": [ + "onig" + ] + }, + "mbstring": { + "type": "builtin", + "lib-depends": [ + "onig" + ] + }, + "mysqli": { + "type": "builtin", + "arg-type": "with", + "ext-depends": [ + "mysqlnd" + ] + }, + "mysqlnd": { + "type": "builtin", + "arg-type-windows": "with" + }, + "opcache": { + "type": "builtin" + }, + "openssl": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "openssl" + ] + }, + "pcntl": { + "type": "builtin", + "unix-only": true + }, + "pdo": { + "type": "builtin" + }, + "pdo_mysql": { + "type": "builtin", + "arg-type": "with", + "ext-depends": [ + "pdo", + "mysqlnd" + ] + }, + "pdo_pgsql": { + "type": "builtin", + "arg-type": "with", + "ext-depends": [ + "pdo" + ], + "lib-depends": [ + "pq" + ] + }, + "pdo_sqlite": { + "type": "builtin", + "arg-type": "with", + "ext-depends": [ + "pdo", + "sqlite3" + ], + "lib-depends": [ + "sqlite" + ] + }, + "phar": { + "type": "builtin", + "lib-suggests": [ + "zlib" + ] + }, + "posix": { + "type": "builtin", + "unix-only": true + }, + "pspell": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "aspell" + ] + }, + "readline": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "readline" + ], + "lib-suggests": [ + "libedit", + "ncurses" + ] + }, + "redis": { + "type": "external", + "source": "redis" + }, + "session": { + "type": "builtin" + }, + "shmop": { + "type": "builtin" + }, + "simplexml": { + "type": "builtin", + "arg-type-windows": "with", + "lib-depends": [ + "libxml2" + ] + }, + "snmp": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "net-snmp" + ] + }, + "soap": { + "type": "builtin", + "lib-depends": [ + "libxml2" + ] + }, + "sockets": { + "type": "builtin" + }, + "sodium": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "sodium" + ] + }, + "sqlite3": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "sqlite" + ] + }, + "swoole": { + "type": "external", + "source": "swoole", + "lib-depends": [ + "openssl", + "curl" + ], + "ext-depends": [ + "openssl" + ], + "ext-suggests": [ + "curl" + ], + "unix-only": true + }, + "swow": { + "type": "external", + "source": "swow", + "lib-suggests": [ + "openssl", + "curl" + ], + "ext-suggests": [ + "curl" + ] + }, + "sysvmsg": { + "type": "builtin", + "unix-only": true + }, + "sysvsem": { + "type": "builtin", + "unix-only": true + }, + "sysvshm": { + "type": "builtin", + "unix-only": true + }, + "tidy": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "tidy" + ] + }, + "tokenizer": { + "type": "builtin" + }, + "xml": { + "type": "builtin", + "arg-type-windows": "with", + "lib-depends": [ + "libxml2" + ] + }, + "xmlreader": { + "type": "builtin", + "lib-depends": [ + "libxml2" + ] + }, + "xmlwriter": { + "type": "builtin", + "lib-depends": [ + "libxml2" + ] + }, + "xsl": { + "type": "builtin", + "arg-type": "with", + "lib-depends": [ + "libxslt" + ] + }, + "yaml": { + "type": "external", + "source": "yaml", + "arg-type": "with", + "lib-depends": [ + "libyaml" + ] + }, + "zip": { + "type": "builtin", + "arg-type": "with", + "arg-type-windows": "enable", + "lib-depends": [ + "libzip" + ] + }, + "zlib": { + "type": "builtin", + "arg-type": "with", + "arg-type-windows": "enable", + "lib-depends": [ + "zlib" + ] + }, + "zstd": { + "type": "external", + "source": "ext-zstd", + "lib-depends": [ + "zstd" + ] + } +} \ No newline at end of file diff --git a/config/lib.json b/config/lib.json new file mode 100644 index 00000000..967c31f9 --- /dev/null +++ b/config/lib.json @@ -0,0 +1,330 @@ +{ + "brotli": { + "source": "brotli", + "static-libs-unix": [ + "libbrotlidec-static.a", + "libbrotlienc-static.a", + "libbrotlicommon-static.a" + ], + "static-libs-windows": [ + "brotlicommon-static.lib", + "brotlienc-static.lib", + "brotlidec-static.lib" + ], + "headers": [ + "brotli" + ] + }, + "bzip2": { + "source": "bzip2", + "static-libs-unix": [ + "libbz2.a" + ], + "static-libs-windows": [ + [ + "libbz2.lib", + "libbz2_a.lib" + ] + ], + "headers": [ + "bzlib.h" + ] + }, + "curl": { + "source": "curl", + "static-libs-unix": [ + "libcurl.a" + ], + "static-libs-windows": [ + "libcurl.lib" + ], + "headers": [ + "curl" + ], + "lib-depends-unix": [ + "zlib" + ], + "lib-suggests": [ + "libssh2", + "brotli", + "nghttp2", + "zstd", + "openssl", + "idn2", + "psl" + ], + "lib-suggests-windows": [ + "zlib", + "libssh2", + "brotli", + "nghttp2", + "zstd", + "openssl", + "idn2", + "psl" + ], + "frameworks": [ + "CoreFoundation", + "SystemConfiguration" + ] + }, + "libffi": { + "source": "libffi", + "static-libs-unix": [ + "libffi.a" + ], + "static-libs-windows": [ + "libffi.lib" + ], + "headers-unix": [ + "ffi.h", + "ffitarget.h" + ], + "headers-windows": [ + "ffi.h", + "fficonfig.h", + "ffitarget.h" + ] + }, + "libpng": { + "source": "libpng", + "static-libs-unix": [ + "libpng.a" + ], + "static-libs-windows": [ + "libpng16_static.lib" + ], + "headers-unix": [ + "png.h", + "pngconf.h", + "pnglibconf.h" + ], + "headers-windows": [ + "png.h", + "pngconf.h" + ], + "lib-depends": [ + "zlib" + ] + }, + "libssh2": { + "source": "libssh2", + "static-libs-unix": [ + "libssh2.a" + ], + "static-libs-windows": [ + "libssh2.lib" + ], + "headers": [ + "libssh2.h", + "libssh2_publickey.h", + "libssh2_sftp.h" + ], + "lib-depends": [ + "openssl" + ], + "lib-suggests": [ + "zlib" + ] + }, + "libxml2": { + "source": "libxml2", + "static-libs-unix": [ + "libxml2.a" + ], + "static-libs-windows": [ + [ + "libxml2s.lib", + "libxml2_a.lib" + ] + ], + "headers": [ + "libxml2" + ], + "lib-suggests": [ + "icu", + "xz", + "zlib" + ], + "lib-suggests-windows": [ + "icu", + "xz", + "zlib", + "pthreads4w" + ] + }, + "libyaml": { + "source": "libyaml", + "static-libs-unix": [ + "libyaml.a" + ], + "static-libs-windows": [ + "yaml.lib" + ], + "headers": [ + "yaml.h" + ] + }, + "libzip": { + "source": "libzip", + "static-libs-unix": [ + "libzip.a" + ], + "static-libs-windows": [ + [ + "zip.lib", + "libzip_a.lib" + ] + ], + "headers": [ + "zip.h", + "zipconf.h" + ], + "lib-depends": [ + "zlib" + ], + "lib-suggests": [ + "bzip2", + "xz", + "zstd", + "openssl" + ] + }, + "nghttp2": { + "source": "nghttp2", + "static-libs-unix": [ + "libnghttp2.a" + ], + "static-libs-windows": [ + "nghttp2.lib" + ], + "headers": [ + "nghttp2" + ], + "lib-depends": [ + "zlib", + "openssl" + ], + "lib-suggests": [ + "libxml2", + "libev", + "libcares", + "libngtcp2", + "libnghttp3", + "libbpf", + "libevent-openssl", + "jansson", + "jemalloc", + "systemd", + "cunit" + ] + }, + "onig": { + "source": "onig", + "static-libs-unix": [ + "libonig.a" + ], + "static-libs-windows": [ + [ + "onig.lib", + "onig_a.lib" + ] + ], + "headers": [ + "oniggnu.h", + "oniguruma.h" + ] + }, + "openssl": { + "source": "openssl", + "static-libs-unix": [ + "libssl.a", + "libcrypto.a" + ], + "static-libs-windows": [ + "libssl.lib", + "libcrypto.lib" + ], + "headers": [ + "openssl" + ], + "lib-suggests": [ + "zlib" + ] + }, + "pthreads4w": { + "source": "pthreads4w", + "static-libs-windows": [ + "libpthreadVC3.lib" + ], + "headers-windows": [ + "_ptw32.h", + "pthread.h", + "sched.h", + "semaphore.h" + ] + }, + "sqlite": { + "source": "sqlite", + "static-libs-unix": [ + "libsqlite3.a" + ], + "headers-unix": [ + "sqlite3.h", + "sqlite3ext.h" + ] + }, + "xz": { + "source": "xz", + "static-libs-unix": [ + "liblzma.a" + ], + "static-libs-windows": [ + [ + "liblzma.lib", + "liblzma_a.lib" + ] + ], + "headers-unix": [ + "lzma" + ], + "headers-windows": [ + "lzma", + "lzma.h" + ] + }, + "zlib": { + "source": "zlib", + "static-libs-unix": [ + "libz.a" + ], + "static-libs-windows": [ + "zlib_a.lib" + ], + "headers": [ + "zlib.h", + "zconf.h" + ] + }, + "zstd": { + "source": "zstd", + "static-libs-unix": [ + "libzstd.a" + ], + "static-libs-windows": [ + [ + "zstd.lib", + "zstd_static.lib" + ] + ], + "headers-unix": [ + "zdict.h", + "zstd.h", + "zstd_errors.h" + ], + "headers-windows": [ + "zstd.h", + "zstd_errors.h" + ] + } +} \ No newline at end of file diff --git a/config/source.json b/config/source.json new file mode 100644 index 00000000..0a31cd5f --- /dev/null +++ b/config/source.json @@ -0,0 +1,220 @@ +{ + "brotli": { + "type": "ghtar", + "repo": "google/brotli", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "bzip2": { + "type": "filelist", + "url": "https://sourceware.org/pub/bzip2/", + "regex": "/href=\"(?bzip2-(?[^\"]+)\\.tar\\.gz)\"/", + "license": { + "type": "text", + "text": "This program, \"bzip2\", the associated library \"libbzip2\", and all documentation, are copyright (C) 1996-2010 Julian R Seward. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nJulian Seward, jseward@bzip.org bzip2/libbzip2 version 1.0.6 of 6 September 2010\n\nPATENTS: To the best of my knowledge, bzip2 and libbzip2 do not use any patented algorithms. However, I do not have the resources to carry out a patent search. Therefore I cannot give any guarantee of the above statement." + } + }, + "curl": { + "type": "ghrel", + "repo": "curl/curl", + "match": "curl.+\\.tar\\.xz", + "license": { + "type": "file", + "path": "COPYING" + } + }, + "ext-zstd": { + "type": "git", + "path": "php-src/ext/zstd", + "rev": "master", + "url": "https://github.com/kjdev/php-ext-zstd", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "libffi": { + "type": "ghrel", + "repo": "libffi/libffi", + "match": "libffi.+\\.tar\\.gz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "libpng": { + "type": "git", + "url": "https://git.code.sf.net/p/libpng/code", + "rev": "libpng16", + "license": { + "type": "text", + "text": "COPYRIGHT NOTICE, DISCLAIMER, and LICENSE\n=========================================\n\nPNG Reference Library License version 2\n---------------------------------------\n\n * Copyright (c) 1995-2019 The PNG Reference Library Authors.\n * Copyright (c) 2018-2019 Cosmin Truta.\n * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.\n * Copyright (c) 1996-1997 Andreas Dilger.\n * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n\nThe software is supplied \"as is\", without warranty of any kind,\nexpress or implied, including, without limitation, the warranties\nof merchantability, fitness for a particular purpose, title, and\nnon-infringement. In no event shall the Copyright owners, or\nanyone distributing the software, be liable for any damages or\nother liability, whether in contract, tort or otherwise, arising\nfrom, out of, or in connection with the software, or the use or\nother dealings in the software, even if advised of the possibility\nof such damage.\n\nPermission is hereby granted to use, copy, modify, and distribute\nthis software, or portions hereof, for any purpose, without fee,\nsubject to the following restrictions:\n\n 1. The origin of this software must not be misrepresented; you\n must not claim that you wrote the original software. If you\n use this software in a product, an acknowledgment in the product\n documentation would be appreciated, but is not required.\n\n 2. Altered source versions must be plainly marked as such, and must\n not be misrepresented as being the original software.\n\n 3. This Copyright notice may not be removed or altered from any\n source or altered source distribution.\n\n\nPNG Reference Library License version 1 (for libpng 0.5 through 1.6.35)\n-----------------------------------------------------------------------\n\nlibpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are\nCopyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are\nderived from libpng-1.0.6, and are distributed according to the same\ndisclaimer and license as libpng-1.0.6 with the following individuals\nadded to the list of Contributing Authors:\n\n Simon-Pierre Cadieux\n Eric S. Raymond\n Mans Rullgard\n Cosmin Truta\n Gilles Vollant\n James Yu\n Mandar Sahastrabuddhe\n Google Inc.\n Vadim Barkov\n\nand with the following additions to the disclaimer:\n\n There is no warranty against interference with your enjoyment of\n the library or against infringement. There is no warranty that our\n efforts or the library will fulfill any of your particular purposes\n or needs. This library is provided with all faults, and the entire\n risk of satisfactory quality, performance, accuracy, and effort is\n with the user.\n\nSome files in the \"contrib\" directory and some configure-generated\nfiles that are distributed with libpng have other copyright owners, and\nare released under other open source licenses.\n\nlibpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are\nCopyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from\nlibpng-0.96, and are distributed according to the same disclaimer and\nlicense as libpng-0.96, with the following individuals added to the\nlist of Contributing Authors:\n\n Tom Lane\n Glenn Randers-Pehrson\n Willem van Schaik\n\nlibpng versions 0.89, June 1996, through 0.96, May 1997, are\nCopyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,\nand are distributed according to the same disclaimer and license as\nlibpng-0.88, with the following individuals added to the list of\nContributing Authors:\n\n John Bowler\n Kevin Bracey\n Sam Bushell\n Magnus Holmgren\n Greg Roelofs\n Tom Tanner\n\nSome files in the \"scripts\" directory have other copyright owners,\nbut are released under this license.\n\nlibpng versions 0.5, May 1995, through 0.88, January 1996, are\nCopyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n\nFor the purposes of this copyright and license, \"Contributing Authors\"\nis defined as the following set of individuals:\n\n Andreas Dilger\n Dave Martindale\n Guy Eric Schalnat\n Paul Schmidt\n Tim Wegner\n\nThe PNG Reference Library is supplied \"AS IS\". The Contributing\nAuthors and Group 42, Inc. disclaim all warranties, expressed or\nimplied, including, without limitation, the warranties of\nmerchantability and of fitness for any purpose. The Contributing\nAuthors and Group 42, Inc. assume no liability for direct, indirect,\nincidental, special, exemplary, or consequential damages, which may\nresult from the use of the PNG Reference Library, even if advised of\nthe possibility of such damage.\n\nPermission is hereby granted to use, copy, modify, and distribute this\nsource code, or portions hereof, for any purpose, without fee, subject\nto the following restrictions:\n\n 1. The origin of this source code must not be misrepresented.\n\n 2. Altered versions must be plainly marked as such and must not\n be misrepresented as being the original source.\n\n 3. This Copyright notice may not be removed or altered from any\n source or altered source distribution.\n\nThe Contributing Authors and Group 42, Inc. specifically permit,\nwithout fee, and encourage the use of this source code as a component\nto supporting the PNG file format in commercial products. If you use\nthis source code in a product, acknowledgment is not required but would\nbe appreciated.\n" + } + }, + "libressl": { + "type": "filelist", + "url": "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/", + "regex": "/href=\"(?libressl-(?[^\"]+)\\.tar\\.gz)\"/", + "license": { + "type": "file", + "path": "COPYING" + } + }, + "libssh2": { + "type": "ghrel", + "repo": "libssh2/libssh2", + "match": "libssh2.+\\.tar\\.gz", + "license": { + "type": "file", + "path": "COPYING" + } + }, + "libxml2": { + "type": "url", + "url": "https://gitlab.gnome.org/GNOME/libxml2/-/archive/v2.9.14/libxml2-v2.9.14.tar.gz", + "license": { + "type": "file", + "path": "Copyright" + } + }, + "libyaml": { + "type": "ghrel", + "repo": "yaml/libyaml", + "match": "yaml-.+\\.tar\\.gz", + "license": { + "type": "file", + "path": "License" + } + }, + "libzip": { + "type": "ghrel", + "repo": "nih-at/libzip", + "match": "libzip.+\\.tar\\.xz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "micro": { + "type": "git", + "path": "php-src/sapi/micro", + "rev": "master", + "url": "https://github.com/dixyes/phpmicro", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "nghttp2": { + "type": "ghrel", + "repo": "nghttp2/nghttp2", + "match": "nghttp2.+\\.tar\\.xz", + "license": { + "type": "file", + "path": "COPYING" + } + }, + "onig": { + "type": "ghrel", + "repo": "kkos/oniguruma", + "match": "onig-.+\\.tar\\.gz", + "license": { + "type": "file", + "path": "COPYING" + } + }, + "openssl": { + "type": "filelist", + "url": "https://www.openssl.org/source/", + "regex": "/href=\"(?openssl-(?[^\"]+)\\.tar\\.gz)\"/", + "license": { + "type": "file", + "path": "LICENSE.txt" + } + }, + "pthreads4w": { + "type": "git", + "rev": "master", + "url": "https://git.code.sf.net/p/pthreads4w/code", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "redis": { + "type": "git", + "path": "php-src/ext/redis", + "rev": "5.3.7", + "url": "https://github.com/phpredis/phpredis", + "license": { + "type": "file", + "path": "COPYING" + } + }, + "sqlite": { + "type": "url", + "url": "https://www.sqlite.org/2023/sqlite-autoconf-3410100.tar.gz", + "license": { + "type": "text", + "path": "The author disclaims copyright to this source code. In place of\na legal notice, here is a blessing:\n\n * May you do good and not evil.\n * May you find forgiveness for yourself and forgive others.\n * May you share freely, never taking more than you give." + } + }, + "swoole": { + "type": "ghtar", + "path": "php-src/ext/swoole", + "repo": "swoole/swoole-src", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "swow": { + "type": "git", + "path": "php-src/ext/swow-src", + "rev": "ci", + "url": "https://github.com/swow/swow", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "xz": { + "type": "filelist", + "url": "https://tukaani.org/xz/", + "regex": "/href=\"(?xz-(?[^\"]+)\\.tar\\.xz)\"/", + "license": { + "type": "file", + "path": "COPYING" + } + }, + "yaml": { + "type": "git", + "path": "php-src/ext/yaml", + "rev": "php7", + "url": "https://github.com/php/pecl-file_formats-yaml", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "zlib": { + "type": "filelist", + "url": "https://zlib.net/", + "regex": "/href=\"(?zlib-(?[^\"]+)\\.tar\\.gz)\"/i", + "license": { + "type": "text", + "text": "(C) 1995-2022 Jean-loup Gailly and Mark Adler\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n claim that you wrote the original software. If you use this software\n in a product, an acknowledgment in the product documentation would be\n appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n\nJean-loup Gailly Mark Adler\njloup@gzip.org madler@alumni.caltech.edu" + } + }, + "zstd": { + "type": "ghrel", + "repo": "facebook/zstd", + "match": "zstd.+\\.tar\\.gz", + "license": { + "type": "file", + "path": "LICENSE" + } + } +} \ No newline at end of file diff --git a/ext-support.md b/ext-support.md new file mode 100644 index 00000000..35efbc68 --- /dev/null +++ b/ext-support.md @@ -0,0 +1,59 @@ +# Extension List + +> - yes: supported and tested +> - untested: supported but not tested +> - empty: not supported yet +> - issue link: not supported yet due to issue + +| | Linux | macOS | Windows | +|------------|-------|----------|---------| +| bcmath | | yes | | +| bz2 | | untested | | +| calendar | | yes | | +| ctype | | | | +| curl | | yes | | +| date | | yes | | +| dom | | | | +| event | | | | +| exif | | | | +| filter | | | | +| fileinfo | | | | +| ftp | | | | +| gd | | untested | | +| gmp | | untested | | +| hash | | yes | | +| iconv | | | | +| inotify | | | | +| json | | yes | | +| libxml | | | | +| mbstring | | | | +| mcrypt | | | | +| mongodb | | | | +| mysqli | | | | +| mysqlnd | | | | +| openssl | | yes | | +| pcntl | | untested | | +| pcre | | yes | | +| pdo | | yes | | +| pdo_mysql | | | | +| pdo_sqlite | | yes | | +| pdo_pgsql | | | | +| phar | | | | +| posix | | | | +| protobuf | | | | +| readline | | | | +| redis | | | | +| Reflection | | yes | | +| shmop | | | | +| simplexml | | | | +| soap | | | | +| sockets | | | | +| sqlite3 | | untested | | +| swow | | | | +| swoole | | yes | | +| tokenizer | | | | +| xml | | | | +| xmlreader | | | | +| xmlwriter | | | | +| zip | | | | +| zlib | | | | diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php new file mode 100644 index 00000000..515da477 --- /dev/null +++ b/src/SPC/builder/BuilderBase.php @@ -0,0 +1,227 @@ + 要编译的 libs 列表 */ + protected array $libs = []; + + /** @var array 要编译的扩展列表 */ + protected array $exts = []; + + /** @var bool 本次编译是否只编译 libs,不编译 PHP */ + protected bool $libs_only = false; + + /** + * 构建指定列表的 libs + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function buildLibs(array $libraries): void + { + // 通过扫描目录查找 lib + $support_lib_list = []; + $classes = FileSystem::getClassesPsr4( + ROOT_DIR . '/src/SPC/builder/' . osfamily2dir() . '/library', + 'SPC\\builder\\' . osfamily2dir() . '\\library' + ); + foreach ($classes as $class) { + if (defined($class . '::NAME') && $class::NAME !== 'unknown' && Config::getLib($class::NAME) !== null) { + $support_lib_list[$class::NAME] = $class; + } + } + + // 如果传入了空,则默认检查和安置所有支持的lib,libraries为要build的,support_lib_list为支持的列表 + if ($libraries === [] && $this->isLibsOnly()) { + $libraries = array_keys($support_lib_list); + } + + // 排序 libs,根据依赖计算一个新的列表出来 + $libraries = DependencyUtil::getLibsByDeps($libraries); + + // 这里筛选 libraries,比如纯静态模式排除掉ffi + if (defined('BUILD_ALL_STATIC') && BUILD_ALL_STATIC) { + $k = array_search('libffi', $libraries, true); + $k !== false && array_splice($libraries, $k, 1); + } + + // 过滤不支持的库后添加 + foreach ($libraries as $library) { + if (!isset($support_lib_list[$library])) { + throw new RuntimeException('library [' . $library . '] is in the lib.json list but not supported to compile, but in the future I will support it!'); + } + $lib = new ($support_lib_list[$library])($this); + $this->addLib($lib); + } + + // 统计还没 fetch 到本地的库 + $this->checkLibsSource(); + + // 计算依赖,经过这里的遍历,如果没有抛出异常,说明依赖符合要求,可以继续下面的 + foreach ($this->libs as $lib) { + $lib->calcDependency(); + } + foreach ($this->libs as $lib) { + match ($lib->tryBuild()) { + BUILD_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] build success'), + BUILD_STATUS_ALREADY => logger()->notice('lib [' . $lib::NAME . '] already built'), + BUILD_STATUS_FAILED => logger()->error('lib [' . $lib::NAME . '] build failed'), + default => logger()->warning('lib [' . $lib::NAME . '] build status unknown'), + }; + } + } + + /** + * 添加要编译的 Lib 库 + * + * @param LibraryBase $library Lib 库对象 + */ + public function addLib(LibraryBase $library): void + { + $this->libs[$library::NAME] = $library; + } + + /** + * 获取要编译的 Lib 库对象 + * + * @param string $name 库名称 + */ + public function getLib(string $name): ?LibraryBase + { + return $this->libs[$name] ?? null; + } + + /** + * 添加要编译的扩展 + * + * @param Extension $extension 扩展对象 + */ + public function addExt(Extension $extension): void + { + $this->exts[$extension->getName()] = $extension; + } + + /** + * 获取要编译的扩展对象 + * + * @param string $name 扩展名称 + */ + public function getExt(string $name): ?Extension + { + return $this->exts[$name] ?? null; + } + + /** + * 设置本次 Builder 是否为仅编译库的模式 + */ + public function setLibsOnly(bool $status = true): void + { + $this->libs_only = $status; + } + + /** + * 检验 ext 扩展列表是否合理,并声明 Extension 对象,检查扩展的依赖 + * + * @throws FileSystemException + * @throws RuntimeException + */ + public function proveExts(array $extensions): void + { + if (defined('BUILD_ALL_STATIC') && BUILD_ALL_STATIC) { + $k = array_search('ffi', $extensions, true); + $k !== false && array_splice($extensions, $k, 1); + } + foreach ($extensions as $extension) { + $ext = new Extension($extension, $this); + $this->addExt($ext); + } + + foreach ($this->exts as $ext) { + // 检查下依赖就行了,作用是导入依赖给 Extension 对象,今后可以对库依赖进行选择性处理 + $ext->checkDependency(); + } + } + + /** + * 开始构建 PHP + * 构建 micro 的规则: + * - BUILD_MICRO_NONE(默认):只编译 cli + * - BUILD_MICRO_ONLY:只编译 micro + * - BUILD_MICRO_BOTH:同时编译 micro 和 cli + * + * @param int $build_micro_rule 规则 + * @param bool $with_clean 是否为新构建? + * @param bool $bloat 保留 + */ + abstract public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $with_clean = false, bool $bloat = false); + + /** + * 生成依赖的扩展编译启用参数 + * 例如 --enable-mbstring 等 + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function makeExtensionArgs(): string + { + $ret = []; + foreach ($this->exts as $ext) { + $ret[] = $ext->getConfigureArg(); + } + logger()->info('Using configure: ' . implode(' ', $ret)); + return implode(' ', $ret); + } + + /** + * 返回是否只编译 libs 的模式 + */ + public function isLibsOnly(): bool + { + return $this->libs_only; + } + + /** + * 检查是否存在 lib 库对应的源码,如果不存在,则抛出异常 + * + * @throws RuntimeException + */ + protected function checkLibsSource(): void + { + $not_downloaded = []; + foreach ($this->libs as $lib) { + if (!file_exists($lib->getSourceDir())) { + $not_downloaded[] = $lib::NAME; + } + } + if ($not_downloaded !== []) { + throw new RuntimeException( + '"' . implode(', ', $not_downloaded) . + '" totally ' . count($not_downloaded) . + ' source' . (count($not_downloaded) === 1 ? '' : 's') . + ' not downloaded, maybe you need to "fetch" ' . (count($not_downloaded) === 1 ? 'it' : 'them') . ' first?' + ); + } + } +} diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php new file mode 100644 index 00000000..1e864317 --- /dev/null +++ b/src/SPC/builder/Extension.php @@ -0,0 +1,299 @@ +name, 'type'); + $unix_only = Config::getExt($this->name, 'unix-only', false); + $windows_only = Config::getExt($this->name, 'windows-only', false); + if (PHP_OS_FAMILY !== 'Windows' && $windows_only) { + throw new RuntimeException("{$ext_type} extension {$name} is not supported on Linux and macOS platform"); + } + if (PHP_OS_FAMILY === 'Windows' && $unix_only) { + throw new RuntimeException("{$ext_type} extension {$name} is not supported on Windows platform"); + } + } + + /** + * 获取开启该扩展的 PHP 编译添加的参数 + * + * @throws FileSystemException|RuntimeException + */ + public function getConfigureArg(): string + { + $arg = $this->getEnableArg(); + switch (PHP_OS_FAMILY) { + case 'Windows': + $arg .= $this->getWindowsConfigureArg(); + break; + case 'Darwin': + case 'Linux': + $arg .= $this->getUnixConfigureArg(); + break; + } + return $arg; + } + + /** + * 根据 ext 的 arg-type 获取对应开启的参数,一般都是 --enable-xxx 和 --with-xxx + * + * @throws FileSystemException + * @throws RuntimeException + */ + public function getEnableArg(): string + { + $_name = str_replace('_', '-', $this->name); + return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) { + 'enable' => '--enable-' . $_name, + 'with' => '--with-' . $_name, + 'none', 'custom' => '', + default => throw new RuntimeException("argType does not accept {$arg_type}, use [enable/with] ."), + }; + } + + /** + * 导出当前扩展依赖的所有 lib 库生成的 .a 静态编译库文件,以字符串形式导出,用空格分割 + */ + public function getLibFilesString(): string + { + $ret = array_map( + fn ($x) => $x->getStaticLibFiles(), + $this->getLibraryDependencies(recursive: true) + ); + return implode(' ', $ret); + } + + /** + * 检查下依赖就行了,作用是导入依赖给 Extension 对象,今后可以对库依赖进行选择性处理 + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function checkDependency(): static + { + foreach (Config::getExt($this->name, 'lib-depends', []) as $name) { + $this->addLibraryDependency($name); + } + foreach (Config::getExt($this->name, 'lib-suggests', []) as $name) { + $this->addLibraryDependency($name, true); + } + foreach (Config::getExt($this->name, 'ext-depends', []) as $name) { + $this->addExtensionDependency($name); + } + foreach (Config::getExt($this->name, 'ext-suggests', []) as $name) { + $this->addExtensionDependency($name, true); + } + return $this; + } + + public function getExtensionDependency(): array + { + return array_filter($this->dependencies, fn ($x) => $x instanceof Extension); + } + + public function getName(): string + { + return $this->name; + } + + /** + * @throws RuntimeException + */ + protected function addLibraryDependency(string $name, bool $optional = false): void + { + $depLib = $this->builder->getLib($name); + if (!$depLib) { + if (!$optional) { + throw new RuntimeException("extension {$this->name} requires library {$name}"); + } + logger()->info("enabling {$this->name} without library {$name}"); + } else { + $this->dependencies[] = $depLib; + } + } + + /** + * @throws RuntimeException + */ + protected function addExtensionDependency(string $name, bool $optional = false): void + { + $depExt = $this->builder->getExt($name); + if (!$depExt) { + if (!$optional) { + throw new RuntimeException("{$this->name} requires extension {$name}"); + } + logger()->info("enabling {$this->name} without extension {$name}"); + } else { + $this->dependencies[] = $depExt; + } + } + + private function getWindowsConfigureArg(): string + { + $arg = ''; + switch ($this->name) { + case 'redis': + // $arg = '--enable-redis'; + // if ($this->builder->getLib('zstd')) { + // $arg .= ' --enable-redis-zstd --with-libzstd '; + // } + break; + case 'xml': + case 'soap': + case 'xmlreader': + case 'xmlwriter': + case 'dom': + $arg .= ' --with-libxml '; + break; + case 'swow': + if ($this->builder->getLib('openssl')) { + $arg .= ' --enable-swow-ssl'; + } + if ($this->builder->getLib('curl')) { + $arg .= ' --enable-swow-curl'; + } + break; + } + return $arg; + } + + private function getUnixConfigureArg(): string + { + $arg = ''; + switch ($this->name) { + /*case 'event': + $arg = ' --with-event-core --with-event-libevent-dir="' . BUILD_ROOT_PATH . '"'; + if ($this->builder->getLib('openssl')) { + $arg .= ' --with-event-openssl --with-openssl-dir="' . BUILD_ROOT_PATH . '"'; + } + break;*/ + case 'sqlite3': + $arg = ' --with-sqlite3="' . BUILD_ROOT_PATH . '" ' . + 'SQLITE_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'SQLITE_LIBS="' . $this->getLibFilesString() . '" '; + break; + case 'redis': + $arg = ' --enable-redis --disable-redis-session'; + if ($this->builder->getLib('zstd')) { + $arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '" '; + } + break; + case 'yaml': + $arg .= ' --with-yaml="' . BUILD_ROOT_PATH . '" '; + break; + case 'zstd': + $arg .= ' --with-libzstd'; + break; + case 'bz2': + $arg = ' --with-bz2="' . BUILD_ROOT_PATH . '" '; + break; + case 'openssl': + $arg .= ' ' . + 'OPENSSL_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'OPENSSL_LIBS="' . $this->getLibFilesString() . '" '; + break; + case 'curl': + $arg .= ' ' . + 'CURL_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'CURL_LIBS="' . $this->getLibFilesString() . '" '; + break; + case 'gd': + $arg .= ' ' . + 'PNG_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'PNG_LIBS="' . $this->getLibFilesString() . '" '; + break; + // TODO: other libraries + case 'phar': + case 'zlib': + $arg .= ' ' . + 'ZLIB_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'ZLIB_LIBS="' . $this->getLibFilesString() . '" '; + break; + case 'xml': // xml may use expat + if ($this->getLibraryDependencies()['expat'] ?? null) { + $arg .= ' --with-expat="' . BUILD_ROOT_PATH . '" ' . + 'EXPAT_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'EXPAT_LIBS="' . $this->getLibFilesString() . '" '; + break; + } + // no break + case 'soap': + case 'xmlreader': + case 'xmlwriter': + case 'dom': + $arg .= ' --with-libxml="' . BUILD_ROOT_PATH . '" ' . + 'LIBXML_CFLAGS=-I"' . realpath('include/libxml2') . '" ' . + 'LIBXML_LIBS="' . $this->getLibFilesString() . '" '; + break; + case 'ffi': + $arg .= ' ' . + 'FFI_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'FFI_LIBS="' . $this->getLibFilesString() . '" '; + break; + case 'zip': + $arg .= ' ' . + 'LIBZIP_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'LIBZIP_LIBS="' . $this->getLibFilesString() . '" '; + break; + case 'mbregex': + $arg .= ' ' . + 'ONIG_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' . + 'ONIG_LIBS="' . $this->getLibFilesString() . '" '; + break; + case 'swow': + $arg .= $this->builder->getLib('openssl') ? ' --enable-swow-ssl' : ' --disable-swow-ssl'; + $arg .= $this->builder->getLib('curl') ? ' --enable-swow-curl' : ' --disable-swow-curl'; + break; + case 'swoole': + if ($this->builder->getLib('openssl')) { + $arg .= ' --enable-openssl'; + } else { + $arg .= ' --disable-openssl --without-openssl'; + } + } + return $arg; + } + + private function getLibraryDependencies(bool $recursive = false): array + { + $ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase); + if (!$recursive) { + return $ret; + } + + $deps = []; + + $added = 1; + while ($added !== 0) { + $added = 0; + foreach ($ret as $depName => $dep) { + foreach ($dep->getDependencies(true) as $depdepName => $depdep) { + if (!in_array($depdepName, array_keys($deps), true)) { + $deps[$depdepName] = $depdep; + ++$added; + } + } + if (!in_array($depName, array_keys($deps), true)) { + $deps[$depName] = $dep; + } + } + } + + return $deps; + } +} diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php new file mode 100644 index 00000000..a7215024 --- /dev/null +++ b/src/SPC/builder/LibraryBase.php @@ -0,0 +1,190 @@ +source_dir = $source_dir ?? (SOURCE_PATH . '/' . static::NAME); + } + + /** + * 获取 lib 库的根目录 + */ + public function getSourceDir(): string + { + return $this->source_dir; + } + + /** + * 获取当前 lib 库的所有依赖列表 + * + * @param bool $recursive 是否递归获取(默认为 False) + * @return array 依赖的 Map + */ + public function getDependencies(bool $recursive = false): array + { + // 非递归情况下直接返回通过 addLibraryDependency 方法添加的依赖 + if (!$recursive) { + return $this->dependencies; + } + + // 下面为递归获取依赖列表,根据依赖顺序 + $deps = []; + + $added = 1; + while ($added !== 0) { + $added = 0; + foreach ($this->dependencies as $depName => $dep) { + foreach ($dep->getDependencies(true) as $depdepName => $depdep) { + if (!in_array($depdepName, array_keys($deps), true)) { + $deps[$depdepName] = $depdep; + ++$added; + } + } + if (!in_array($depName, array_keys($deps), true)) { + $deps[$depName] = $dep; + } + } + } + + return $deps; + } + + /** + * 计算依赖列表,不符合依赖将抛出异常 + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function calcDependency(): void + { + // 先从配置文件添加依赖,这里根据不同的操作系统分别选择不同的元信息 + /* + 选择规则: + 如果是 Windows 系统,则依次尝试有无 lib-depends-windows、lib-depends-win、lib-depends。 + 如果是 macOS 系统,则依次尝试 lib-depends-darwin、lib-depends-unix、lib-depends。 + 如果是 Linux 系统,则依次尝试 lib-depends-linux、lib-depends-unix、lib-depends。 + */ + foreach (Config::getLib(static::NAME, 'lib-depends', []) as $dep_name) { + $this->addLibraryDependency($dep_name); + } + foreach (Config::getLib(static::NAME, 'lib-suggests', []) as $dep_name) { + $this->addLibraryDependency($dep_name, true); + } + } + + /** + * 获取当前库编译出来获取到的静态库文件列表 + * + * @return string[] 获取编译出来后的需要的静态库文件列表 + * @throws FileSystemException + * @throws RuntimeException + */ + public function getStaticLibs(): array + { + return Config::getLib(static::NAME, 'static-libs', []); + } + + /** + * 获取当前 lib 编译出来的 C Header 文件列表 + * + * @return string[] 获取编译出来后需要的 C Header 文件列表 + * @throws FileSystemException + * @throws RuntimeException + */ + public function getHeaders(): array + { + return Config::getLib(static::NAME, 'headers', []); + } + + /** + * 证明该库是否已编译好且就绪,如果没有就绪,内部会调用 build 来进行构建该库 + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function tryBuild(bool $force_build = false): int + { + // 传入 true,表明直接编译 + if ($force_build) { + $this->build(); + return BUILD_STATUS_OK; + } + + // 看看这些库是不是存在,如果不存在,则调用编译并返回结果状态 + foreach ($this->getStaticLibs() as $name) { + if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { + $this->tryBuild(true); + return BUILD_STATUS_OK; + } + } + // 头文件同理 + foreach ($this->getHeaders() as $name) { + if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { + $this->tryBuild(true); + return BUILD_STATUS_OK; + } + } + // 到这里说明所有的文件都存在,就跳过编译 + return BUILD_STATUS_ALREADY; + } + + /** + * 获取构建当前 lib 的 Builder 对象 + */ + abstract public function getBuilder(): BuilderBase; + + /** + * 构建该库需要调用的命令和操作 + * + * @throws RuntimeException + */ + abstract protected function build(); + + /** + * 添加 lib 库的依赖库 + * + * @param string $name 依赖名称 + * @param bool $optional 是否是可选依赖(默认为 False) + * @throws RuntimeException + */ + protected function addLibraryDependency(string $name, bool $optional = false): void + { + // Log::i("add $name as dep of {$this->name}"); + $dep_lib = $this->getBuilder()->getLib($name); + if (!$dep_lib) { + if (!$optional) { + throw new RuntimeException(static::NAME . " requires library {$name}"); + } + logger()->debug('enabling ' . static::NAME . " without {$name}"); + } else { + $this->dependencies[$name] = $dep_lib; + } + } +} diff --git a/src/SPC/builder/LibraryInterface.php b/src/SPC/builder/LibraryInterface.php new file mode 100644 index 00000000..da8ba75a --- /dev/null +++ b/src/SPC/builder/LibraryInterface.php @@ -0,0 +1,10 @@ +set_x = defined('DEBUG_MODE') ? 'set -x' : 'true'; + // 初始化一些默认参数 + $this->cc = $cc ?? 'clang'; + $this->cxx = $cxx ?? 'clang++'; + $this->arch = $arch ?? php_uname('m'); + $this->gnu_arch = arch2gnu($this->arch); + // 根据 CPU 线程数设置编译进程数 + $this->concurrency = SystemUtil::getCpuCount(); + // 设置 cflags + $this->arch_c_flags = SystemUtil::getArchCFlags($this->arch); + $this->arch_cxx_flags = SystemUtil::getArchCFlags($this->arch); + // 设置 cmake + $this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('Darwin', $this->arch, $this->arch_c_flags); + // 设置 configure 依赖的环境变量 + $this->configure_env = + 'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig/" ' . + "CC='{$this->cc}' " . + "CXX='{$this->cxx}' " . + "CFLAGS='{$this->arch_c_flags} -Wimplicit-function-declaration'"; + // 保存丢失的命令 + $missing = []; + foreach (self::REQUIRED_COMMANDS as $cmd) { + if (SystemUtil::findCommand($cmd) === null) { + $missing[] = $cmd; + } + } + if (!empty($missing)) { + throw new RuntimeException('missing system commands: ' . implode(', ', $missing)); + } + + // 创立 pkg-config 和放头文件的目录 + f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true); + f_mkdir(BUILD_INCLUDE_PATH, recursive: true); + } + + /** + * 生成库构建采用的 autoconf 参数列表 + * + * @param string $name 要构建的 lib 库名,传入仅供输出日志 + * @param array $lib_specs 依赖的 lib 库的 autoconf 文件 + */ + public function makeAutoconfArgs(string $name, array $lib_specs): string + { + $ret = ''; + foreach ($lib_specs as $libName => $arr) { + $lib = $this->getLib($libName); + + $arr = $arr ?? []; + + $disableArgs = $arr[0] ?? null; + $prefix = $arr[1] ?? null; + if ($lib instanceof MacOSLibraryBase) { + logger()->info("{$name} \033[32;1mwith\033[0;1m {$libName} support"); + $ret .= $lib->makeAutoconfEnv($prefix) . ' '; + } else { + logger()->info("{$name} \033[31;1mwithout\033[0;1m {$libName} support"); + $ret .= ($disableArgs ?? "--with-{$libName}=no") . ' '; + } + } + return rtrim($ret); + } + + /** + * 返回 macOS 系统依赖的框架列表 + * + * @param bool $asString 是否以字符串形式返回(默认为 False) + */ + public function getFrameworks(bool $asString = false): array|string + { + $libs = []; + + // reorder libs + foreach ($this->libs as $lib) { + foreach ($lib->getDependencies() as $dep) { + $libs[] = $dep; + } + $libs[] = $lib; + } + + $frameworks = []; + /** @var MacOSLibraryBase $lib */ + foreach ($libs as $lib) { + array_push($frameworks, ...$lib->getFrameworks()); + } + + if ($asString) { + return implode(' ', array_map(fn ($x) => "-framework {$x}", $frameworks)); + } + return $frameworks; + } + + /** + * @throws RuntimeException + * @throws FileSystemException + */ + public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $with_clean = false, bool $bloat = false): void + { + $extra_libs = $this->getFrameworks(true) . ' ' . ($this->getExt('swoole') ? '-lc++ ' : ''); + if (!$bloat) { + $extra_libs .= implode(' ', $this->getAllStaticLibFiles()); + } else { + logger()->info('bloat linking'); + $extra_libs .= implode( + ' ', + array_map( + fn ($x) => "-Wl,-force_load,{$x}", + array_filter($this->getAllStaticLibFiles()) + ) + ); + } + + $extra_libs .= ' /Users/jerry/project/git-project/static-php-cli/buildroot/lib/libsqlite3.a'; + + // patch before configure + Patcher::patchPHPBeforeConfigure($this); + + f_passthru( + $this->set_x . ' && ' . + 'cd ' . SOURCE_PATH . '/php-src && ' . + './buildconf --force' + ); + + Patcher::patchPHPConfigure($this); + + if ($this->getLib('libxml2') || $this->getExt('iconv')) { + $extra_libs .= ' /Users/jerry/project/git-project/static-php-cli/buildroot/lib/libcrypto.a -liconv'; + } + + f_passthru( + $this->set_x . ' && ' . + 'cd ' . SOURCE_PATH . '/php-src && ' . + './configure ' . + '--prefix= ' . + '--with-valgrind=no ' . // 不检测内存泄漏 + '--enable-shared=no ' . + '--enable-static=yes ' . + "--host={$this->gnu_arch}-apple-darwin " . + "CFLAGS='{$this->arch_c_flags} -Werror=unknown-warning-option' " . + '--disable-all ' . + '--disable-cgi ' . + '--disable-phpdbg ' . + '--enable-cli ' . + '--enable-micro ' . + ($this->zts ? '--enable-zts' : '') . ' ' . + $this->makeExtensionArgs() . ' ' . + $this->configure_env + ); + + if ($with_clean) { + logger()->info('cleaning up'); + f_passthru( + $this->set_x . ' && ' . + 'cd ' . SOURCE_PATH . '/php-src && ' . + 'make clean' + ); + } + + switch ($build_micro_rule) { + case BUILD_MICRO_NONE: + logger()->info('building cli'); + $this->buildCli($extra_libs); + break; + case BUILD_MICRO_ONLY: + logger()->info('building micro'); + $this->buildMicro($extra_libs); + break; + case BUILD_MICRO_BOTH: + logger()->info('building cli and micro'); + $this->buildCli($extra_libs); + $this->buildMicro($extra_libs); + break; + } + + if (php_uname('m') === $this->arch) { + $this->sanityCheck($build_micro_rule); + } + + if ($this->phar_patched) { + f_passthru('cd ' . SOURCE_PATH . '/php-src && patch -p1 -R < sapi/micro/patches/phar.patch'); + } + } + + /** + * 构建 phpmicro + * + * @throws RuntimeException + */ + public function buildMicro(string $extra_libs): void + { + if ($this->getExt('phar')) { + $this->phar_patched = true; + try { + f_passthru('cd ' . SOURCE_PATH . '/php-src && patch -p1 < sapi/micro/patches/phar.patch'); + } catch (RuntimeException $e) { + logger()->error('failed to patch phat due to patch exit with code ' . $e->getCode()); + $this->phar_patched = false; + } + } + + f_passthru( + $this->set_x . ' && ' . + 'cd ' . SOURCE_PATH . '/php-src && ' . + "make -j{$this->concurrency} " . + 'EXTRA_CFLAGS="-g -Os -fno-ident" ' . + "EXTRA_LIBS=\"{$extra_libs} -lresolv\" " . + 'STRIP="dsymutil -f " ' . + // TODO: comment things + 'micro' + ); + } + + /** + * 构建 cli + * + * @throws RuntimeException + */ + public function buildCli(string $extra_libs): void + { + f_passthru( + $this->set_x . ' && ' . + 'cd ' . SOURCE_PATH . '/php-src && ' . + "make -j{$this->concurrency} " . + 'EXTRA_CFLAGS="-g -Os -fno-ident" ' . // 生成调试信息、优化编译后的尺寸、禁用标识符(如变量、函数名)缩短 + "EXTRA_LIBS=\"{$extra_libs} -lresolv -lsqlite3\" " . + // TODO: comment things + 'cli &&' . + 'dsymutil -f sapi/cli/php &&' . + 'strip sapi/cli/php' + ); + } +} diff --git a/src/SPC/builder/macos/SystemUtil.php b/src/SPC/builder/macos/SystemUtil.php new file mode 100644 index 00000000..ee6b271e --- /dev/null +++ b/src/SPC/builder/macos/SystemUtil.php @@ -0,0 +1,43 @@ + '--target=x86_64-apple-darwin', + 'arm64','aarch64' => '--target=arm64-apple-darwin', + default => throw new RuntimeException('unsupported arch: ' . $arch), + }; + } +} diff --git a/src/SPC/builder/macos/library/MacOSLibraryBase.php b/src/SPC/builder/macos/library/MacOSLibraryBase.php new file mode 100644 index 00000000..0478e10a --- /dev/null +++ b/src/SPC/builder/macos/library/MacOSLibraryBase.php @@ -0,0 +1,43 @@ + true,代表依赖 curl 但可选 + */ + protected array $dep_names; + + public function __construct(protected MacOSBuilder $builder) + { + parent::__construct(); + } + + public function getBuilder(): BuilderBase + { + return $this->builder; + } + + /** + * 获取当前 lib 库依赖的 macOS framework + */ + public function getFrameworks(): array + { + return Config::getLib(static::NAME, 'frameworks', []); + } +} diff --git a/src/SPC/builder/macos/library/brotli.php b/src/SPC/builder/macos/library/brotli.php new file mode 100644 index 00000000..3de7c297 --- /dev/null +++ b/src/SPC/builder/macos/library/brotli.php @@ -0,0 +1,49 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class brotli extends MacOSLibraryBase +{ + public const NAME = 'brotli'; + + protected function build() + { + [$lib, $include, $destdir] = SEPARATED_PATH; + f_passthru( + "{$this->builder->set_x} && " . + "cd {$this->source_dir} && " . + 'rm -rf build && ' . + 'mkdir -p build && ' . + 'cd build && ' . + "{$this->builder->configure_env} " . ' cmake ' . + // '--debug-find ' . + '-DCMAKE_BUILD_TYPE=Release ' . + '-DBUILD_SHARED_LIBS=OFF ' . + '-DCMAKE_INSTALL_PREFIX=/ ' . + "-DCMAKE_INSTALL_LIBDIR={$lib} " . + "-DCMAKE_INSTALL_INCLUDEDIR={$include} " . + "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . + '.. && ' . + "cmake --build . -j {$this->builder->concurrency} && " . + 'make install DESTDIR="' . $destdir . '"' + ); + } +} diff --git a/src/SPC/builder/macos/library/bzip2.php b/src/SPC/builder/macos/library/bzip2.php new file mode 100644 index 00000000..59c77d94 --- /dev/null +++ b/src/SPC/builder/macos/library/bzip2.php @@ -0,0 +1,39 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class bzip2 extends MacOSLibraryBase +{ + public const NAME = 'bzip2'; + + protected function build() + { + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "make {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' clean" . ' && ' . + "make -j{$this->builder->concurrency} {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' libbz2.a" . ' && ' . + // make install may fail when cross-compiling, so we copy files. + 'cp libbz2.a ' . BUILD_LIB_PATH . ' && ' . + 'cp bzlib.h ' . BUILD_INCLUDE_PATH + ); + } +} diff --git a/src/SPC/builder/macos/library/curl.php b/src/SPC/builder/macos/library/curl.php new file mode 100644 index 00000000..b91b6744 --- /dev/null +++ b/src/SPC/builder/macos/library/curl.php @@ -0,0 +1,118 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +use SPC\exception\RuntimeException; + +class curl extends MacOSLibraryBase +{ + public const NAME = 'curl'; + + protected function build() + { + $extra = ''; + // lib:openssl + $openssl = $this->getBuilder()->getLib('openssl'); + if ($openssl instanceof MacOSLibraryBase) { + $extra .= '-DCURL_USE_OPENSSL=ON '; + } else { + $extra .= '-DCURL_USE_OPENSSL=OFF -DCURL_ENABLE_SSL=OFF '; + } + // lib:zlib + $zlib = $this->getBuilder()->getLib('zlib'); + if ($zlib instanceof MacOSLibraryBase) { + $extra .= '-DZLIB_LIBRARY="' . $zlib->getStaticLibFiles(style: 'cmake') . '" ' . + '-DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' '; + } + // lib:libssh2 + $libssh2 = $this->builder->getLib('libssh2'); + if ($libssh2 instanceof MacOSLibraryBase) { + $extra .= '-DLIBSSH2_LIBRARY="' . $libssh2->getStaticLibFiles(style: 'cmake') . '" ' . + '-DLIBSSH2_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" '; + } else { + $extra .= '-DCURL_USE_LIBSSH2=OFF '; + } + // lib:brotli + $brotli = $this->builder->getLib('brotli'); + if ($brotli) { + $extra .= '-DCURL_BROTLI=ON ' . + '-DBROTLIDEC_LIBRARY="' . realpath(BUILD_LIB_PATH . '/libbrotlidec-static.a') . ';' . realpath(BUILD_LIB_PATH . '/libbrotlicommon-static.a') . '" ' . + '-DBROTLICOMMON_LIBRARY="' . realpath(BUILD_LIB_PATH . '/libbrotlicommon-static.a') . '" ' . + '-DBROTLI_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" '; + } else { + $extra .= '-DCURL_BROTLI=OFF '; + } + // lib:nghttp2 + $nghttp2 = $this->builder->getLib('nghttp2'); + if ($nghttp2 instanceof MacOSLibraryBase) { + $extra .= '-DUSE_NGHTTP2=ON ' . + '-DNGHTTP2_LIBRARY="' . $nghttp2->getStaticLibFiles(style: 'cmake') . '" ' . + '-DNGHTTP2_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" '; + } else { + $extra .= '-DUSE_NGHTTP2=OFF '; + } + // lib:ldap + $ldap = $this->builder->getLib('ldap'); + if ($ldap instanceof MacOSLibraryBase) { + // $extra .= '-DCURL_DISABLE_LDAP=OFF '; + // TODO: LDAP support + throw new RuntimeException('LDAP support is not implemented yet'); + } + $extra .= '-DCURL_DISABLE_LDAP=ON '; + // lib:zstd + $zstd = $this->builder->getLib('zstd'); + if ($zstd instanceof MacOSLibraryBase) { + $extra .= '-DCURL_ZSTD=ON ' . + '-DZstd_LIBRARY="' . $zstd->getStaticLibFiles(style: 'cmake') . '" ' . + '-DZstd_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" '; + } else { + $extra .= '-DCURL_ZSTD=OFF '; + } + // lib:idn2 + $idn2 = $this->builder->getLib('idn2'); + $extra .= $idn2 instanceof MacOSLibraryBase ? '-DUSE_LIBIDN2=ON ' : '-DUSE_LIBIDN2=OFF '; + // lib:psl + $libpsl = $this->builder->getLib('psl'); + $extra .= $libpsl instanceof MacOSLibraryBase ? '-DCURL_USE_LIBPSL=ON ' : '-DCURL_USE_LIBPSL=OFF '; + + [$lib, $include, $destdir] = SEPARATED_PATH; + // compile! + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + 'rm -rf build && ' . + 'mkdir -p build && ' . + 'cd build && ' . + "{$this->builder->configure_env} " . ' cmake ' . + // '--debug-find ' . + '-DCMAKE_BUILD_TYPE=Release ' . + '-DBUILD_SHARED_LIBS=OFF ' . + $extra . + '-DCMAKE_INSTALL_PREFIX= ' . + "-DCMAKE_INSTALL_LIBDIR={$lib} " . + "-DCMAKE_INSTALL_INCLUDEDIR={$include} " . + "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . + '.. && ' . + "make -j{$this->builder->concurrency} && " . + 'make install DESTDIR="' . $destdir . '"' + ); + } +} diff --git a/src/SPC/builder/macos/library/libffi.php b/src/SPC/builder/macos/library/libffi.php new file mode 100644 index 00000000..5f501575 --- /dev/null +++ b/src/SPC/builder/macos/library/libffi.php @@ -0,0 +1,45 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class libffi extends MacOSLibraryBase +{ + public const NAME = 'libffi'; + + protected function build() + { + [$lib, $include, $destdir] = SEPARATED_PATH; + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "{$this->builder->configure_env} ./configure " . + '--enable-static ' . + '--disable-shared ' . + "--host={$this->builder->arch}-apple-darwin " . + "--target={$this->builder->arch}-apple-darwin " . + '--prefix= ' . // use prefix=/ + "--libdir={$lib} && " . + 'make clean && ' . + "make -j{$this->builder->concurrency} && " . + "make install DESTDIR={$destdir}" + ); + } +} diff --git a/src/SPC/builder/macos/library/libpng.php b/src/SPC/builder/macos/library/libpng.php new file mode 100644 index 00000000..0fe75753 --- /dev/null +++ b/src/SPC/builder/macos/library/libpng.php @@ -0,0 +1,65 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; +use SPC\util\Patcher; + +class libpng extends MacOSLibraryBase +{ + public const NAME = 'libpng'; + + /** + * @throws FileSystemException + * @throws RuntimeException + */ + protected function build() + { + // 不同架构的专属优化 + $optimizations = match ($this->builder->arch) { + 'x86_64' => '--enable-intel-sse ', + 'arm64' => '--enable-arm-neon ', + default => '', + }; + + // patch configure + Patcher::patchUnixLibpng(); + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "{$this->builder->configure_env} " . + './configure ' . + "--host={$this->builder->gnu_arch}-apple-darwin " . + '--disable-shared ' . + '--enable-static ' . + '--enable-hardware-optimizations ' . + $optimizations . + '--prefix= && ' . // use prefix=/ + 'make clean && ' . + "make -j{$this->builder->concurrency} DEFAULT_INCLUDES='-I. -I" . BUILD_INCLUDE_PATH . "' LIBS= libpng16.la && " . + 'make install-libLTLIBRARIES install-data-am DESTDIR=' . BUILD_ROOT_PATH . ' && ' . + 'cd ' . BUILD_LIB_PATH . ' && ' . + 'ln -sf libpng16.a libpng.a' + ); + } +} diff --git a/src/SPC/builder/macos/library/libssh2.php b/src/SPC/builder/macos/library/libssh2.php new file mode 100644 index 00000000..c65d87d9 --- /dev/null +++ b/src/SPC/builder/macos/library/libssh2.php @@ -0,0 +1,56 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class libssh2 extends MacOSLibraryBase +{ + public const NAME = 'libssh2'; + + protected function build() + { + // lib:zlib + $enable_zlib = $this->builder->getLib('zlib') !== null ? 'ON' : 'OFF'; + + [$lib, $include, $destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + 'rm -rf build && ' . + 'mkdir -p build && ' . + 'cd build && ' . + "{$this->builder->configure_env} " . ' cmake ' . + // '--debug-find ' . + '-DCMAKE_BUILD_TYPE=Release ' . + '-DBUILD_SHARED_LIBS=OFF ' . + '-DBUILD_EXAMPLES=OFF ' . + '-DBUILD_TESTING=OFF ' . + "-DENABLE_ZLIB_COMPRESSION={$enable_zlib} " . + '-DCMAKE_INSTALL_PREFIX=/ ' . + "-DCMAKE_INSTALL_LIBDIR={$lib} " . + "-DCMAKE_INSTALL_INCLUDEDIR={$include} " . + "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . + '.. && ' . + "cmake --build . -j {$this->builder->concurrency} --target libssh2 && " . + 'make install DESTDIR="' . $destdir . '"' + ); + } +} diff --git a/src/SPC/builder/macos/library/libxml2.php b/src/SPC/builder/macos/library/libxml2.php new file mode 100644 index 00000000..ca600291 --- /dev/null +++ b/src/SPC/builder/macos/library/libxml2.php @@ -0,0 +1,66 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +use SPC\exception\RuntimeException; + +class libxml2 extends MacOSLibraryBase +{ + public const NAME = 'libxml2'; + + /** + * @throws RuntimeException + */ + protected function build() + { + $enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF'; + $enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF'; + $enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF'; + + [$lib, $include, $destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + 'rm -rf build && ' . + 'mkdir -p build && ' . + 'cd build && ' . + "{$this->builder->configure_env} " . ' cmake ' . + // '--debug-find ' . + '-DCMAKE_BUILD_TYPE=Release ' . + '-DBUILD_SHARED_LIBS=OFF ' . + '-DLIBXML2_WITH_ICONV=ON ' . + "-DLIBXML2_WITH_ZLIB={$enable_zlib} " . + "-DLIBXML2_WITH_ICU={$enable_icu} " . + "-DLIBXML2_WITH_LZMA={$enable_xz} " . + '-DLIBXML2_WITH_PYTHON=OFF ' . + '-DLIBXML2_WITH_PROGRAMS=OFF ' . + '-DLIBXML2_WITH_TESTS=OFF ' . + '-DCMAKE_INSTALL_PREFIX=/ ' . + "-DCMAKE_INSTALL_LIBDIR={$lib} " . + "-DCMAKE_INSTALL_INCLUDEDIR={$include} " . + "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . + '.. && ' . + "cmake --build . -j {$this->builder->concurrency} && " . + 'make install DESTDIR="' . $destdir . '"' + ); + } +} diff --git a/src/SPC/builder/macos/library/libyaml.php b/src/SPC/builder/macos/library/libyaml.php new file mode 100644 index 00000000..4af6328c --- /dev/null +++ b/src/SPC/builder/macos/library/libyaml.php @@ -0,0 +1,95 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +use SPC\exception\RuntimeException; + +class libyaml extends MacOSLibraryBase +{ + public const NAME = 'libyaml'; + + /** + * @throws RuntimeException + */ + public function build() + { + // prepare cmake/config.h.in + if (!is_file(SOURCE_PATH . '/libyaml/cmake/config.h.in')) { + f_mkdir(SOURCE_PATH . '/libyaml/cmake'); + file_put_contents( + SOURCE_PATH . '/libyaml/cmake/config.h.in', + <<<'EOF' +#define YAML_VERSION_MAJOR @YAML_VERSION_MAJOR@ +#define YAML_VERSION_MINOR @YAML_VERSION_MINOR@ +#define YAML_VERSION_PATCH @YAML_VERSION_PATCH@ +#define YAML_VERSION_STRING "@YAML_VERSION_STRING@" +EOF + ); + } + + // prepare yamlConfig.cmake.in + if (!is_file(SOURCE_PATH . '/libyaml/yamlConfig.cmake.in')) { + file_put_contents( + SOURCE_PATH . '/libyaml/yamlConfig.cmake.in', + <<<'EOF' +# Config file for the yaml library. +# +# It defines the following variables: +# yaml_LIBRARIES - libraries to link against + +@PACKAGE_INIT@ + +set_and_check(yaml_TARGETS "@PACKAGE_CONFIG_DIR_CONFIG@/yamlTargets.cmake") + +if(NOT yaml_TARGETS_IMPORTED) + set(yaml_TARGETS_IMPORTED 1) + include(${yaml_TARGETS}) +endif() + +set(yaml_LIBRARIES yaml) + +EOF + ); + } + + [$lib, $include, $destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + 'rm -rf build && ' . + 'mkdir -p build && ' . + 'cd build && ' . + "{$this->builder->configure_env} " . ' cmake ' . + // '--debug-find ' . + '-DCMAKE_BUILD_TYPE=Release ' . + '-DBUILD_TESTING=OFF ' . + '-DBUILD_SHARED_LIBS=OFF ' . + '-DCMAKE_INSTALL_PREFIX=/ ' . + "-DCMAKE_INSTALL_LIBDIR={$lib} " . + "-DCMAKE_INSTALL_INCLUDEDIR={$include} " . + "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . + '.. && ' . + "make -j{$this->builder->concurrency} && " . + 'make install DESTDIR=' . $destdir + ); + } +} diff --git a/src/SPC/builder/macos/library/libzip.php b/src/SPC/builder/macos/library/libzip.php new file mode 100644 index 00000000..1ae6a2f3 --- /dev/null +++ b/src/SPC/builder/macos/library/libzip.php @@ -0,0 +1,101 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class libzip extends MacOSLibraryBase +{ + public const NAME = 'libzip'; + + protected function build() + { + $extra = ''; + // lib:zlib + $zlib = $this->builder->getLib('zlib'); + if ($zlib instanceof MacOSLibraryBase) { + $extra .= '-DZLIB_LIBRARY="' . $zlib->getStaticLibFiles(style: 'cmake') . '" ' . + '-DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' '; + } + // lib:bzip2 + $libbzip2 = $this->builder->getLib('bzip2'); + if ($libbzip2 instanceof MacOSLibraryBase) { + $extra .= '-DENABLE_BZIP2=ON ' . + '-DBZIP2_LIBRARIES="' . $libbzip2->getStaticLibFiles(style: 'cmake') . '" ' . + '-DBZIP2_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' '; + } else { + $extra .= '-DENABLE_BZIP2=OFF '; + } + // lib:xz + $xz = $this->builder->getLib('xz'); + if ($xz instanceof MacOSLibraryBase) { + $extra .= '-DENABLE_LZMA=ON ' . + '-DLIBLZMA_LIBRARY="' . $xz->getStaticLibFiles(style: 'cmake') . '" ' . + '-DLIBLZMA_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' '; + } else { + $extra .= '-DENABLE_LZMA=OFF '; + } + // lib:zstd + $libzstd = $this->builder->getLib('zstd'); + if ($libzstd instanceof MacOSLibraryBase) { + $extra .= '-DENABLE_ZSTD=ON ' . + '-DZstd_LIBRARY="' . $libzstd->getStaticLibFiles(style: 'cmake') . '" ' . + '-DZstd_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" '; + } else { + $extra .= '-DENABLE_ZSTD=OFF '; + } + // lib:openssl + $libopenssl = $this->builder->getLib('openssl'); + if ($libopenssl instanceof MacOSLibraryBase) { + $extra .= '-DENABLE_OPENSSL=ON ' . + '-DOpenSSL_LIBRARY="' . $libopenssl->getStaticLibFiles(style: 'cmake') . '" ' . + '-DOpenSSL_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" '; + } else { + $extra .= '-DENABLE_OPENSSL=OFF '; + } + + [$lib, $include, $destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + 'rm -rf build && ' . + 'mkdir -p build && ' . + 'cd build && ' . + "{$this->builder->configure_env} " . ' cmake ' . + // '--debug-find ' . + '-DCMAKE_BUILD_TYPE=Release ' . + '-DENABLE_GNUTLS=OFF ' . + '-DENABLE_MBEDTLS=OFF ' . + '-DBUILD_SHARED_LIBS=OFF ' . + '-DBUILD_DOC=OFF ' . + '-DBUILD_EXAMPLES=OFF ' . + '-DBUILD_REGRESS=OFF ' . + '-DBUILD_TOOLS=OFF ' . + $extra . + '-DCMAKE_INSTALL_PREFIX=/ ' . + "-DCMAKE_INSTALL_LIBDIR={$lib} " . + "-DCMAKE_INSTALL_INCLUDEDIR={$include} " . + "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . + '.. && ' . + "make -j{$this->builder->concurrency} && " . + 'make install DESTDIR=' . $destdir + ); + } +} diff --git a/src/SPC/builder/macos/library/nghttp2.php b/src/SPC/builder/macos/library/nghttp2.php new file mode 100644 index 00000000..0578ac7e --- /dev/null +++ b/src/SPC/builder/macos/library/nghttp2.php @@ -0,0 +1,63 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class nghttp2 extends MacOSLibraryBase +{ + public const NAME = 'nghttp2'; + + protected function build() + { + $args = $this->builder->makeAutoconfArgs(static::NAME, [ + 'zlib' => null, + 'openssl' => null, + 'libxml2' => null, + 'libev' => null, + 'libcares' => null, + 'libngtcp2' => null, + 'libnghttp3' => null, + 'libbpf' => null, + 'libevent-openssl' => null, + 'jansson' => null, + 'jemalloc' => null, + 'systemd' => null, + 'cunit' => null, + ]); + + [,,$destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "{$this->builder->configure_env} " . ' ./configure ' . + '--enable-static ' . + '--disable-shared ' . + "--host={$this->builder->gnu_arch}-apple-darwin " . + '--enable-lib-only ' . + '--with-boost=no ' . + $args . ' ' . + '--prefix= && ' . // use prefix=/ + 'make clean && ' . + "make -j{$this->builder->concurrency} && " . + "make install DESTDIR={$destdir}" + ); + } +} diff --git a/src/SPC/builder/macos/library/onig.php b/src/SPC/builder/macos/library/onig.php new file mode 100644 index 00000000..5f9085e5 --- /dev/null +++ b/src/SPC/builder/macos/library/onig.php @@ -0,0 +1,44 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class onig extends MacOSLibraryBase +{ + public const NAME = 'onig'; + + protected function build() + { + [,,$destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "{$this->builder->configure_env} " . ' ./configure ' . + '--enable-static ' . + '--disable-shared ' . + "--host={$this->builder->arch}-apple-darwin " . + '--prefix= && ' . // use prefix=/ + 'make clean && ' . + "make -j{$this->builder->concurrency} && " . + 'make install DESTDIR=' . $destdir + ); + } +} diff --git a/src/SPC/builder/macos/library/openssl.php b/src/SPC/builder/macos/library/openssl.php new file mode 100644 index 00000000..748541ed --- /dev/null +++ b/src/SPC/builder/macos/library/openssl.php @@ -0,0 +1,52 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class openssl extends MacOSLibraryBase +{ + public const NAME = 'openssl'; + + protected function build() + { + [$lib,,$destdir] = SEPARATED_PATH; + + // lib:zlib + $extra = ''; + $ex_lib = ''; + $zlib = $this->builder->getLib('zlib'); + if ($zlib instanceof MacOSLibraryBase) { + $extra = 'zlib'; + $ex_lib = trim($zlib->getStaticLibFiles() . ' ' . $ex_lib); + } + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "{$this->builder->configure_env} ./Configure no-shared {$extra} " . + '--prefix=/ ' . // use prefix=/ + "--libdir={$lib} " . + " darwin64-{$this->builder->arch}-cc && " . + 'make clean && ' . + "make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\" && " . + 'make install_sw DESTDIR=' . $destdir + ); + } +} diff --git a/src/SPC/builder/macos/library/sqlite.php b/src/SPC/builder/macos/library/sqlite.php new file mode 100644 index 00000000..f29b3a54 --- /dev/null +++ b/src/SPC/builder/macos/library/sqlite.php @@ -0,0 +1,42 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class sqlite extends MacOSLibraryBase +{ + public const NAME = 'sqlite'; + + protected function build() + { + [,,$destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "{$this->builder->configure_env} ./configure " . + '--enable-static --disable-shared ' . + '--prefix= && ' . // use prefix=/ + 'make clean && ' . + "make -j{$this->builder->concurrency} && " . + 'make install DESTDIR=' . $destdir + ); + } +} diff --git a/src/SPC/builder/macos/library/xz.php b/src/SPC/builder/macos/library/xz.php new file mode 100644 index 00000000..fc3b16f9 --- /dev/null +++ b/src/SPC/builder/macos/library/xz.php @@ -0,0 +1,52 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class xz extends MacOSLibraryBase +{ + public const NAME = 'xz'; + + protected function build() + { + [,,$destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + 'autoreconf -i --force && ' . + "{$this->builder->configure_env} ./configure " . + '--enable-static ' . + '--disable-shared ' . + "--host={$this->builder->gnu_arch}-apple-darwin " . + '--disable-xz ' . + '--disable-xzdec ' . + '--disable-lzmadec ' . + '--disable-lzmainfo ' . + '--disable-scripts ' . + '--disable-doc ' . + '--with-libiconv ' . + '--prefix= && ' . // use prefix=/ + 'make clean && ' . + "make -j{$this->builder->concurrency} && " . + 'make install DESTDIR=' . $destdir + ); + } +} diff --git a/src/SPC/builder/macos/library/zlib.php b/src/SPC/builder/macos/library/zlib.php new file mode 100644 index 00000000..09b9ab98 --- /dev/null +++ b/src/SPC/builder/macos/library/zlib.php @@ -0,0 +1,42 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class zlib extends MacOSLibraryBase +{ + public const NAME = 'zlib'; + + protected function build() + { + [,,$destdir] = SEPARATED_PATH; + + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "{$this->builder->configure_env} ./configure " . + '--static ' . + '--prefix= && ' . // use prefix=/ + 'make clean && ' . + "make -j{$this->builder->concurrency} && " . + 'make install DESTDIR=' . $destdir + ); + } +} diff --git a/src/SPC/builder/macos/library/zstd.php b/src/SPC/builder/macos/library/zstd.php new file mode 100644 index 00000000..5b090fb0 --- /dev/null +++ b/src/SPC/builder/macos/library/zstd.php @@ -0,0 +1,41 @@ + + * + * static-php-cli is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\macos\library; + +class zstd extends MacOSLibraryBase +{ + public const NAME = 'zstd'; + + protected function build() + { + f_passthru( + $this->builder->set_x . ' && ' . + "cd {$this->source_dir} && " . + "make {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' clean" . ' && ' . + "make -j{$this->builder->concurrency} " . + "{$this->builder->configure_env} " . + "PREFIX='" . BUILD_ROOT_PATH . "' " . + '-C lib libzstd.a CPPFLAGS_STATLIB=-DZSTD_MULTITHREAD && ' . + 'cp lib/libzstd.a ' . BUILD_LIB_PATH . ' && ' . + 'cp lib/zdict.h lib/zstd_errors.h lib/zstd.h ' . BUILD_INCLUDE_PATH + ); + } +} diff --git a/src/SPC/builder/traits/LibraryTrait.php b/src/SPC/builder/traits/LibraryTrait.php new file mode 100644 index 00000000..0d550c85 --- /dev/null +++ b/src/SPC/builder/traits/LibraryTrait.php @@ -0,0 +1,9 @@ +libs as $lib) { + foreach ($lib->getDependencies() as $dep) { + $libs[] = $dep; + } + $libs[] = $lib; + } + + $libFiles = []; + $libNames = []; + // merge libs + foreach ($libs as $lib) { + if (!in_array($lib::NAME, $libNames, true)) { + $libNames[] = $lib::NAME; + array_unshift($libFiles, ...$lib->getStaticLibs()); + } + } + return array_map(fn ($x) => realpath(BUILD_LIB_PATH . "/{$x}"), $libFiles); + } + + /** + * @throws RuntimeException + */ + public function sanityCheck(int $build_micro_rule): void + { + logger()->info('running sanity check'); + if ($build_micro_rule !== BUILD_MICRO_ONLY) { + f_exec( + $this->set_x . ' && ' . + SOURCE_PATH . '/php-src/sapi/cli/php -r "echo \"hello\";"', + $output, + $ret + ); + if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { + throw new RuntimeException('cli failed sanity check'); + } + foreach ($this->exts as $ext) { + logger()->debug('checking ext: ' . $ext->getName()); + if (file_exists(ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php')) { + f_exec( + $this->set_x . ' && ' . SOURCE_PATH . '/php-src/sapi/cli/php ' . ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php', + $output, + $ret + ); + if ($ret !== 0) { + throw new RuntimeException('extension ' . $ext->getName() . ' failed sanity check'); + } + } + } + } + if ($build_micro_rule !== BUILD_MICRO_NONE) { + if (file_exists(SOURCE_PATH . '/hello.exe')) { + @unlink(SOURCE_PATH . '/hello.exe'); + } + file_put_contents( + SOURCE_PATH . '/hello.exe', + file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') . + 'getDependencies(recursive: true))); + } + + $sep = match ($style) { + 'autoconf' => ' ', + 'cmake' => ';', + default => throw new RuntimeException('style only support autoconf and cmake'), + }; + $ret = []; + /** @var LibraryBase $lib */ + foreach ($libs as $lib) { + $libFiles = []; + foreach ($lib->getStaticLibs() as $name) { + $name = str_replace(' ', '\ ', realpath(BUILD_LIB_PATH . "/{$name}")); + $name = str_replace('"', '\"', $name); + $libFiles[] = $name; + } + array_unshift($ret, implode($sep, $libFiles)); + } + return implode($sep, $ret); + } + + public function makeAutoconfEnv(string $prefix = null): string + { + if ($prefix === null) { + $prefix = str_replace('-', '_', strtoupper(static::NAME)); + } + return $prefix . '_CFLAGS="-I' . BUILD_INCLUDE_PATH . '" ' . + $prefix . '_LIBS="' . $this->getStaticLibFiles() . '"'; + } +} diff --git a/src/SPC/builder/traits/UnixSystemUtilTrait.php b/src/SPC/builder/traits/UnixSystemUtilTrait.php new file mode 100644 index 00000000..3ac83d69 --- /dev/null +++ b/src/SPC/builder/traits/UnixSystemUtilTrait.php @@ -0,0 +1,68 @@ +debug("making cmake tool chain file for {$os} {$target_arch} with CFLAGS='{$cflags}'"); + $root = BUILD_ROOT_PATH; + $ccLine = ''; + if ($cc) { + $ccLine = 'SET(CMAKE_C_COMPILER ' . self::findCommand($cc) . ')'; + } + $cxxLine = ''; + if ($cxx) { + $cxxLine = 'SET(CMAKE_CXX_COMPILER ' . self::findCommand($cxx) . ')'; + } + $toolchain = <<setDescription('Build CLI binary'); + $this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated'); + $this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', ''); + $this->addOption('build-micro', null, null, 'build micro only'); + $this->addOption('build-all', null, null, 'build both cli and micro'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + // 从参数中获取要编译的 libraries,并转换为数组 + $libraries = array_map('trim', array_filter(explode(',', $input->getOption('with-libs')))); + // 从参数中获取要编译的 extensions,并转换为数组 + $extensions = array_map('trim', array_filter(explode(',', $input->getArgument('extensions')))); + + define('BUILD_ALL_STATIC', true); + + if ($input->getOption('build-all')) { + $rule = BUILD_MICRO_BOTH; + logger()->info('Builder will build php-cli and phpmicro SAPI'); + } elseif ($input->getOption('build-micro')) { + $rule = BUILD_MICRO_ONLY; + logger()->info('Builder will build phpmicro SAPI'); + } else { + $rule = BUILD_MICRO_NONE; + logger()->info('Builder will build php-cli SAPI'); + } + try { + // 构建对象 + $builder = BuilderProvider::makeBuilderByInput($input); + // 根据提供的扩展列表获取依赖库列表并编译 + [$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries); + + logger()->info('Enabled extensions: ' . implode(', ', $extensions)); + logger()->info('Required libraries: ' . implode(', ', $libraries)); + if (!empty($not_included)) { + logger()->warning('some extensions will be enabled due to dependencies: ' . implode(',', $not_included)); + } + sleep(2); + // 编译和检查库是否完整 + $builder->buildLibs($libraries); + // 执行扩展检测 + $builder->proveExts($extensions); + // 构建 + $builder->buildPHP($rule, $input->getOption('with-clean'), $input->getOption('bloat')); + // 统计时间 + $time = round(microtime(true) - START_TIME, 3); + logger()->info('Build complete, used ' . $time . ' s !'); + if ($rule !== BUILD_MICRO_ONLY) { + logger()->info('Static php binary path: ' . SOURCE_PATH . '/php-src/sapi/cli/php'); + } + if ($rule !== BUILD_MICRO_NONE) { + logger()->info('phpmicro binary path: ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx'); + } + return 0; + } catch (\Throwable $e) { + if ($input->getOption('debug')) { + ExceptionHandler::getInstance()->handle($e); + } else { + logger()->emergency('Build failed, please check terminal output, or build with --debug option to see more details.'); + logger()->emergency($e->getMessage()); + } + return 1; + } + } +} diff --git a/src/SPC/command/BuildCommand.php b/src/SPC/command/BuildCommand.php new file mode 100644 index 00000000..11507546 --- /dev/null +++ b/src/SPC/command/BuildCommand.php @@ -0,0 +1,37 @@ +addOption('with-sdk-binary-dir', null, InputOption::VALUE_REQUIRED, 'path to binary sdk'); + $this->addOption('vs-ver', null, InputOption::VALUE_REQUIRED, 'vs version, e.g. "17" for Visual Studio 2022'); + $this->addOption('arch', null, InputOption::VALUE_REQUIRED, 'architecture, "x64" or "arm64"', 'x64'); + break; + case 'Linux': + $this->addOption('no-system-static', null, null, 'do not use system static libraries'); + // no break + case 'Darwin': + $this->addOption('cc', null, InputOption::VALUE_REQUIRED, 'C compiler'); + $this->addOption('cxx', null, InputOption::VALUE_REQUIRED, 'C++ compiler'); + $this->addOption('arch', null, InputOption::VALUE_REQUIRED, 'architecture', php_uname('m')); + break; + } + + // 是否在编译 make 前清除旧的文件 + $this->addOption('with-clean', null, null, 'fresh build, `make clean` before `make`'); + // 是否采用强制链接,让链接器强制加载静态库文件 + $this->addOption('bloat', null, null, 'add all libraries into binary'); + } +} diff --git a/src/SPC/command/BuildLibsCommand.php b/src/SPC/command/BuildLibsCommand.php new file mode 100644 index 00000000..acd4644e --- /dev/null +++ b/src/SPC/command/BuildLibsCommand.php @@ -0,0 +1,69 @@ +setDescription('Build dependencies'); + $this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated'); + $this->addOption('clean', null, null, 'Clean old download cache and source before fetch'); + $this->addOption('all', 'A', null, 'Build all libs that static-php-cli needed'); + } + + public function initialize(InputInterface $input, OutputInterface $output) + { + // --all 等于 "" + if ($input->getOption('all')) { + $input->setArgument('libraries', ''); + } + } + + /** + * @throws RuntimeException + * @throws FileSystemException + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + // 从参数中获取要编译的 libraries,并转换为数组 + $libraries = array_map('trim', array_filter(explode(',', $input->getArgument('libraries')))); + + // 删除旧资源 + if ($input->getOption('clean')) { + logger()->warning('You are doing some operations that not recoverable: removing directories below'); + logger()->warning(BUILD_ROOT_PATH); + logger()->warning('I will remove these dir after you press [Enter] !'); + echo 'Confirm operation? [Yes] '; + fgets(STDIN); + if (PHP_OS_FAMILY === 'Windows') { + f_passthru('rmdir /s /q ' . BUILD_ROOT_PATH); + } else { + f_passthru('rm -rf ' . BUILD_ROOT_PATH); + } + } + + // 构建对象 + $builder = BuilderProvider::makeBuilderByInput($input); + // 只编译 library 的情况下,标记 + $builder->setLibsOnly(); + // 编译和检查库完整 + $builder->buildLibs($libraries); + + $time = round(microtime(true) - START_TIME, 3); + logger()->info('Build libs complete, used ' . $time . ' s !'); + return 0; + } +} diff --git a/src/SPC/command/DeployCommand.php b/src/SPC/command/DeployCommand.php new file mode 100644 index 00000000..ae13b76c --- /dev/null +++ b/src/SPC/command/DeployCommand.php @@ -0,0 +1,116 @@ +setDescription('Deploy static-php-cli self to an .phar application'); + $this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.'); + $this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.'); + $this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.'); + $this->addOption('disable-gzip', 'z', InputOption::VALUE_NONE, 'disable gzip archive mode'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + // 第一阶段流程:如果没有写path,将会提示输入要打包的path + $prompt = new ArgFixer($input, $output); + // 首先得确认是不是关闭了readonly模式 + if (ini_get('phar.readonly') == 1) { + if ($input->getOption('auto-phar-fix')) { + $ask = true; + } else { + $ask = $prompt->requireBool('pack command needs "phar.readonly" = "Off" !' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true); + } + $output->writeln('Now running command in child process.'); + if ($ask) { + global $argv; + passthru(PHP_BINARY . ' -d phar.readonly=0 ' . implode(' ', $argv), $retcode); + exit($retcode); + } + } + // 获取路径 + $path = WORKING_DIR; + // 如果是目录,则将目录下的所有文件打包 + $phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar'); + + if (DataProvider::isRelativePath($phar_path)) { + $phar_path = '/tmp/' . $phar_path; + } + if (file_exists($phar_path)) { + $ask = $input->getOption('overwrite') ? true : $prompt->requireBool('The file "' . $phar_path . '" already exists, do you want to overwrite it?' . PHP_EOL . 'If you want to, just Enter'); + if (!$ask) { + $output->writeln('User canceled.'); + return 1; + } + @unlink($phar_path); + } + $phar = new \Phar($phar_path); + $phar->startBuffering(); + + $all = DataProvider::scanDirFiles($path, true, true); + + $all = array_filter($all, function ($x) { + $dirs = preg_match('/(^(bin|config|src|vendor)\\/|^(composer\\.json|README\\.md|source\\.json|LICENSE|README-en\\.md)$)/', $x); + return !($dirs !== 1); + }); + sort($all); + $map = []; + foreach ($all as $v) { + $map[$v] = $path . '/' . $v; + } + + $output->writeln('Start packing files...'); + try { + $phar->buildFromIterator(new SeekableArrayIterator($map, new ProgressBar($output))); + $phar->addFromString( + '.phar-entry.php', + str_replace( + '/../vendor/autoload.php', + '/vendor/autoload.php', + file_get_contents(ROOT_DIR . '/bin/static-php-cli') + ) + ); + $stub = '.phar-entry.php'; + $phar->setStub($phar->createDefaultStub($stub)); + } catch (\Throwable $e) { + $output->writeln($e); + return 1; + } + $phar->addFromString('.prod', 'true'); + if (!$input->getOption('disable-gzip')) { + $phar->compressFiles(\Phar::GZ); + } + $phar->stopBuffering(); + $output->writeln(PHP_EOL . 'Done! Phar file is generated at "' . $phar_path . '".'); + if (file_exists(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx')) { + $output->writeln('Detected you have already compiled micro binary, I will make executable now for you!'); + file_put_contents( + $phar_path . '.exe', + file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') . + file_get_contents($phar_path) + ); + chmod($phar_path . '.exe', 0755); + $output->writeln('Static: ' . $phar_path . '.exe'); + } + chmod($phar_path, 0755); + $output->writeln('Phar: ' . $phar_path . ''); + return 0; + } +} diff --git a/src/SPC/command/DumpLicenseCommand.php b/src/SPC/command/DumpLicenseCommand.php new file mode 100644 index 00000000..01911941 --- /dev/null +++ b/src/SPC/command/DumpLicenseCommand.php @@ -0,0 +1,29 @@ +setDescription('Dump licenses for required libraries'); + $this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln('not implemented'); + return 1; + } +} diff --git a/src/SPC/command/FetchSourceCommand.php b/src/SPC/command/FetchSourceCommand.php new file mode 100644 index 00000000..a5c4d333 --- /dev/null +++ b/src/SPC/command/FetchSourceCommand.php @@ -0,0 +1,238 @@ +setDescription('Fetch required sources'); + $this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated'); + $this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated'); + $this->addOption('hash', null, null, 'Hash only'); + $this->addOption('shallow-clone', null, null, 'Clone shallow'); + $this->addOption('with-openssl11', null, null, 'Use openssl 1.1'); + $this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1'); + $this->addOption('clean', null, null, 'Clean old download cache and source before fetch'); + $this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed'); + } + + public function initialize(InputInterface $input, OutputInterface $output) + { + // --all 等于 "" "",也就是所有东西都要下载 + if ($input->getOption('all')) { + $input->setArgument('extensions', ''); + $input->setArgument('libraries', ''); + } + parent::initialize($input, $output); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $this->input = $input; + try { + // 匹配版本 + $ver = $this->php_major_ver = $input->getOption('with-php') ?? '8.1'; + preg_match('/^\d+\.\d+$/', $ver, $matches); + if (!$matches) { + logger()->error("bad version arg: {$ver}, x.y required!"); + return 1; + } + + // 删除旧资源 + if ($input->getOption('clean')) { + logger()->warning('You are doing some operations that not recoverable: removing directories below'); + logger()->warning(SOURCE_PATH); + logger()->warning(DOWNLOAD_PATH); + logger()->warning('I will remove these dir after you press [Enter] !'); + echo 'Confirm operation? [Yes] '; + $r = strtolower(trim(fgets(STDIN))); + if ($r !== 'yes' && $r !== '') { + logger()->notice('Operation canceled.'); + return 1; + } + if (PHP_OS_FAMILY === 'Windows') { + f_passthru('rmdir /s /q ' . SOURCE_PATH); + f_passthru('rmdir /s /q ' . DOWNLOAD_PATH); + } else { + f_passthru('rm -rf ' . SOURCE_PATH); + f_passthru('rm -rf ' . DOWNLOAD_PATH); + } + } + + // 使用浅克隆可以减少调用 git 命令下载资源时的存储空间占用 + if ($input->getOption('shallow-clone')) { + define('GIT_SHALLOW_CLONE', true); + } + + // 读取源配置,随便读一个source,用于缓存 source 配置 + Config::getSource('openssl'); + + // 是否启用openssl11 + if ($input->getOption('with-openssl11')) { + logger()->debug('Using openssl 1.1'); + // 手动修改配置 + Config::$source['openssl']['regex'] = '/href="(?openssl-(?1.[^"]+)\.tar\.gz)\"/'; + } + + // 默认预选 phpmicro + $chosen_sources = ['micro']; + + // 从参数中获取要编译的 libraries,并转换为数组 + $libraries = array_map('trim', array_filter(explode(',', $input->getArgument('libraries')))); + if ($libraries) { + foreach ($libraries as $lib) { + // 从 lib 的 config 中找到对应 source 资源名称,组成一个 lib 的 source 列表 + $src_name = Config::getLib($lib, 'source'); + $chosen_sources[] = $src_name; + } + } else { // 如果传入了空串,那么代表 fetch 所有包 + $chosen_sources = [...$chosen_sources, ...array_map(fn ($x) => $x['source'], array_values(Config::getLibs()))]; + } + + // 从参数中获取要编译的 extensions,并转换为数组 + $extensions = array_map('trim', array_filter(explode(',', $input->getArgument('extensions')))); + if ($extensions) { + foreach ($extensions as $lib) { + if (Config::getExt($lib, 'type') !== 'builtin') { + $src_name = Config::getExt($lib, 'source'); + $chosen_sources[] = $src_name; + } + } + } else { + foreach (Config::getExts() as $ext) { + if ($ext['type'] !== 'builtin') { + $chosen_sources[] = $ext['source']; + } + } + } + $chosen_sources = array_unique($chosen_sources); + + // 是否只hash,不下载资源 + if ($input->getOption('hash')) { + $hash = $this->doHash($chosen_sources); + $output->writeln($hash); + return 0; + } + + // 创建目录 + f_mkdir(SOURCE_PATH); + f_mkdir(DOWNLOAD_PATH); + + // 下载 PHP + Downloader::fetchSource('php-src', Downloader::getLatestPHPInfo($ver)); + + // 下载别的依赖资源 + $cnt = count($chosen_sources); + $ni = 0; + foreach ($chosen_sources as $name) { + ++$ni; + logger()->info("Fetching source {$name} [{$ni}/{$cnt}]"); + Downloader::fetchSource($name, Config::getSource($name)); + } + + // patch 每份资源只需一次,如果已经下载好的资源已经patch了,就标记一下不patch了 + if (!file_exists(SOURCE_PATH . '/.patched')) { + $this->doPatch(); + } else { + logger()->notice('sources already patched'); + } + + // 打印拉取资源用时 + $time = round(microtime(true) - START_TIME, 3); + logger()->info('Fetch complete, used ' . $time . ' s !'); + return 0; + } catch (\Throwable $e) { + // 不开 debug 模式就不要再显示复杂的调试栈信息了 + if ($input->getOption('debug')) { + ExceptionHandler::getInstance()->handle($e); + } else { + logger()->emergency($e->getMessage() . ', previous message: ' . $e->getPrevious()->getMessage()); + } + return 1; + } + } + + /** + * 计算资源名称列表的 Hash + * + * @param array $chosen_sources 要计算 hash 的资源名称列表 + * @throws InvalidArgumentException + * @throws DownloaderException + * @throws FileSystemException + */ + private function doHash(array $chosen_sources): string + { + $files = []; + foreach ($chosen_sources as $name) { + $source = Config::getSource($name); + $filename = match ($source['type']) { + 'ghtar' => Downloader::getLatestGithubTarball($name, $source)[1], + 'ghtagtar' => Downloader::getLatestGithubTarball($name, $source, 'tags')[1], + 'ghrel' => Downloader::getLatestGithubRelease($name, $source)[1], + 'filelist' => Downloader::getFromFileList($name, $source)[1], + 'url' => $source['filename'] ?? basename($source['url']), + 'git' => null, + default => throw new InvalidArgumentException('unknown source type: ' . $source['type']), + }; + if ($filename !== null) { + logger()->info("found {$name} source: {$filename}"); + $files[] = $filename; + } + } + return hash('sha256', implode('|', $files)); + } + + /** + * 在拉回资源后,需要对一些文件做一些补丁 patch + * + * @throws FileSystemException + * @throws RuntimeException + */ + private function doPatch(): void + { + // patch 一些 PHP 的资源,以便编译 + Patcher::patchPHPDepFiles(); + + // openssl 3 需要 patch 额外的东西 + if (!$this->input->getOption('with-openssl11') && $this->php_major_ver === '8.0') { + Patcher::patchOpenssl3(); + } + + // openssl1.1.1q 在 MacOS 上直接编译会报错,patch 一下 + // @see: https://github.com/openssl/openssl/issues/18720 + if ($this->input->getOption('with-openssl11') && file_exists(SOURCE_PATH . '/openssl/test/v3ext.c') && PHP_OS_FAMILY === 'Darwin') { + Patcher::patchDarwinOpenssl11(); + } + + // swow 需要软链接内部的文件夹才能正常编译 + if (!file_exists(SOURCE_PATH . '/php-src/ext/swow')) { + Patcher::patchSwow(); + } + + // 标记 patch 完成,避免重复 patch + file_put_contents(SOURCE_PATH . '/.patched', ''); + } +} diff --git a/src/SPC/command/ListExtCommand.php b/src/SPC/command/ListExtCommand.php new file mode 100644 index 00000000..408c944e --- /dev/null +++ b/src/SPC/command/ListExtCommand.php @@ -0,0 +1,30 @@ +setDescription('List supported extensions'); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + foreach (Config::getExts() as $ext => $meta) { + echo $ext . PHP_EOL; + } + return 0; + } +} diff --git a/src/SPC/command/SortConfigCommand.php b/src/SPC/command/SortConfigCommand.php new file mode 100644 index 00000000..153a64c4 --- /dev/null +++ b/src/SPC/command/SortConfigCommand.php @@ -0,0 +1,60 @@ +setDescription('After config edited, sort it by alphabet'); + $this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".'); + } + + /** + * @throws ValidationException + * @throws FileSystemException + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + switch ($name = $input->getArgument('config-name')) { + case 'lib': + $file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true); + ConfigValidator::validateLibs($file); + ksort($file); + file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); + break; + case 'source': + $file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/source.json'), true); + ConfigValidator::validateSource($file); + ksort($file); + file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); + break; + case 'ext': + $file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true); + ConfigValidator::validateExts($file); + ksort($file); + file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); + break; + default: + $output->writeln("invalid config name: {$name}"); + return 1; + } + $output->writeln('sort success'); + return 0; + } +} diff --git a/src/SPC/exception/ValidationException.php b/src/SPC/exception/ValidationException.php new file mode 100644 index 00000000..83ff86b7 --- /dev/null +++ b/src/SPC/exception/ValidationException.php @@ -0,0 +1,9 @@ +info('Patching php'); + + $major_ver = $match[1] . $match[2]; + $check = !defined('DEBUG_MODE') ? ' -q' : ''; + // f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD'); + + $patch_list = [ + 'static_opcache', + 'static_extensions_win32', + 'cli_checks', + 'disable_huge_page', + 'vcruntime140', + 'win32', + 'zend_stream', + ]; + $patch_list = array_merge($patch_list, match (PHP_OS_FAMILY) { + 'Windows' => [ + 'cli_static', + ], + 'Darwin' => [ + 'macos_iconv', + ], + default => [], + }); + $patches = []; + $serial = ['80', '81', '82']; + foreach ($patch_list as $patchName) { + if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) { + $patches[] = "sapi/micro/patches/{$patchName}.patch"; + continue; + } + for ($i = array_search($major_ver, $serial, true); $i >= 0; --$i) { + $tryMajMin = $serial[$i]; + if (!file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}_{$tryMajMin}.patch")) { + continue; + } + $patches[] = "sapi/micro/patches/{$patchName}_{$tryMajMin}.patch"; + continue 2; + } + throw new RuntimeException("failed finding patch {$patchName}"); + } + + $patchesStr = str_replace('/', DIRECTORY_SEPARATOR, implode(' ', $patches)); + + f_passthru( + 'cd ' . SOURCE_PATH . '/php-src && ' . + (PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1' + ); + } + + public static function patchOpenssl3(): void + { + logger()->info('Patching PHP with openssl 3.0'); + $openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c'); + $openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c); + file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c); + } + + /** + * @throws RuntimeException + */ + public static function patchSwow(): void + { + logger()->info('Patching swow'); + if (PHP_OS_FAMILY === 'Windows') { + f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext'); + } else { + f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow'); + } + } + + public static function patchPHPBeforeConfigure(BuilderBase $builder): void + { + if ($builder->getExt('curl')) { + logger()->info('patching before-configure for curl checks'); + $file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])"; + $files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4'); + $file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [ + save_old_LDFLAGS=$LDFLAGS + ac_stuff="$5" + + save_ext_shared=$ext_shared + ext_shared=yes + PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS) + AC_CHECK_LIB([$1],[$2],[ + LDFLAGS=$save_old_LDFLAGS + ext_shared=$save_ext_shared + $3 + ],[ + LDFLAGS=$save_old_LDFLAGS + ext_shared=$save_ext_shared + unset ac_cv_lib_$1[]_$2 + $4 + ])dnl +])'; + file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2); + } + + // if ($builder->getExt('pdo_sqlite')) { + // FileSystem::replaceFile() + // } + } + + /** + * @throws FileSystemException + * @throws RuntimeException + */ + public static function patchPHPConfigure(BuilderBase $builder): void + { + $frameworks = $builder instanceof MacOSBuilder ? ' ' . $builder->getFrameworks(true) . ' ' : ''; + $curl = $builder->getExt('curl'); + if ($curl) { + logger()->info('patching configure for curl checks'); + FileSystem::replaceFile( + SOURCE_PATH . '/php-src/configure', + REPLACE_FILE_PREG, + '/-lcurl/', + $curl->getLibFilesString() . $frameworks + ); + } + $bzip2 = $builder->getExt('bz2'); + if ($bzip2) { + logger()->info('patching configure for bzip2 checks'); + FileSystem::replaceFile( + SOURCE_PATH . '/php-src/configure', + REPLACE_FILE_PREG, + '/-lbz2/', + $bzip2->getLibFilesString() . $frameworks + ); + } + $pdo_sqlite = $builder->getExt('pdo_sqlite'); + if ($pdo_sqlite) { + logger()->info('patching configure for pdo_sqlite linking'); + FileSystem::replaceFile( + SOURCE_PATH . '/php-src/configure', + REPLACE_FILE_PREG, + '/sqlite3_column_table_name=yes/', + 'sqlite3_column_table_name=no' + ); + } + logger()->info('patching configure for disable capstone'); + FileSystem::replaceFile( + SOURCE_PATH . '/php-src/configure', + REPLACE_FILE_PREG, + '/have_capstone="yes"/', + 'have_capstone="no"' + ); + if (property_exists($builder, 'arch') && php_uname('m') !== $builder->arch) { + // cross-compiling + switch ($builder->arch) { + case 'aarch64': + case 'arm64': + // almost all bsd/linux supports this + logger()->info('patching configure for shm mmap checks (cross-compiling)'); + FileSystem::replaceFile( + SOURCE_PATH . '/php-src/configure', + REPLACE_FILE_PREG, + '/have_shm_mmap_anon=no/', + 'have_shm_mmap_anon=yes' + ); + FileSystem::replaceFile( + SOURCE_PATH . '/php-src/configure', + REPLACE_FILE_PREG, + '/have_shm_mmap_posix=no/', + 'have_shm_mmap_posix=yes' + ); + break; + case 'x86_64': + break; + default: + throw new RuntimeException('unsupported arch: ' . $builder->arch); + } + } + } + + /** + * @throws FileSystemException + */ + public static function patchUnixLibpng(): void + { + FileSystem::replaceFile( + SOURCE_PATH . '/libpng/configure', + REPLACE_FILE_STR, + '-lz', + BUILD_LIB_PATH . '/libz.a' + ); + } + + /** + * @throws FileSystemException + */ + public static function patchDarwinOpenssl11(): void + { + FileSystem::replaceFile( + SOURCE_PATH . '/openssl/test/v3ext.c', + REPLACE_FILE_STR, + '#include ', + '#include ' . PHP_EOL . '#include ' + ); + } + + public static function patchLinuxPkgConfig(string $path): void + { + logger()->info("fixing pc {$path}"); + + $workspace = BUILD_ROOT_PATH; + if ($workspace === '/') { + $workspace = ''; + } + + $content = file_get_contents($path); + $content = preg_replace('/^prefix=.+$/m', "prefix={$workspace}", $content); + $content = preg_replace('/^libdir=.+$/m', 'libdir=${prefix}/lib', $content); + $content = preg_replace('/^includedir=.+$/m', 'includedir=${prefix}/include', $content); + file_put_contents($path, $content); + } + + /** + * @throws FileSystemException + * @throws RuntimeException + */ + public static function patchLinuxConfigHeader(string $libc): void + { + switch ($libc) { + case 'musl_wrapper': + // bad checks + FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCPY 1$/m', ''); + FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCAT 1$/m', ''); + // no break + case 'musl': + FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_FUNC_ATTRIBUTE_IFUNC 1$/m', ''); + break; + case 'glibc': + // avoid lcrypt dependency + FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT 1$/m', ''); + FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT_R 1$/m', ''); + FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT_H 1$/m', ''); + break; + default: + throw new RuntimeException('not implemented'); + } + } +} diff --git a/src/globals/defines.php b/src/globals/defines.php index 67a8b645..c517208a 100644 --- a/src/globals/defines.php +++ b/src/globals/defines.php @@ -35,9 +35,14 @@ const REPLACE_FILE_STR = 1; const REPLACE_FILE_PREG = 2; const REPLACE_FILE_USER = 3; +// 编译输出类型 const BUILD_MICRO_NONE = 0; const BUILD_MICRO_ONLY = 1; - const BUILD_MICRO_BOTH = 2; +// 编译状态 +const BUILD_STATUS_OK = 0; +const BUILD_STATUS_ALREADY = 1; +const BUILD_STATUS_FAILED = 2; + ConsoleLogger::$date_format = 'H:i:s'; diff --git a/src/globals/tests/bcmath.php b/src/globals/tests/bcmath.php new file mode 100644 index 00000000..4888f854 --- /dev/null +++ b/src/globals/tests/bcmath.php @@ -0,0 +1,8 @@ +