Compare commits

...

22 Commits

Author SHA1 Message Date
crazywhalecc
97eff64b5b add micro:combine command 2023-07-20 01:15:28 +08:00
crazywhalecc
e522258693 fix libpng mac source bug 2023-07-20 01:15:05 +08:00
crazywhalecc
b84c68fe11 add doctor linux support 2023-07-17 21:44:17 +08:00
crazywhalecc
8505feaa66 fix createDir not working 2023-07-17 21:41:26 +08:00
crazywhalecc
41f49c20ff add --from-zip command to download locally 2023-07-17 21:36:50 +08:00
crazywhalecc
1912ae36e6 separate system tools list 2023-07-17 21:11:06 +08:00
crazywhalecc
08efc81cf0 add --auto-fix option 2023-07-17 21:10:49 +08:00
crazywhalecc
a3b09c69cc correct alpine build commands 2023-07-17 21:05:39 +08:00
crazywhalecc
5cf105c3a5 fix distro 2023-07-17 20:59:59 +08:00
crazywhalecc
7408781d13 add linux-header installer for alpine 2023-07-17 20:58:48 +08:00
crazywhalecc
0afc8ea2c3 add linux tool check 2023-07-17 20:31:42 +08:00
crazywhalecc
67b073776c add linux tool check 2023-07-17 20:29:02 +08:00
crazywhalecc
8b48cf7f70 add linux tool check 2023-07-17 20:28:08 +08:00
crazywhalecc
63287dd9c4 add linux tool check 2023-07-17 20:26:48 +08:00
crazywhalecc
fc7ac921e2 add document contributing guide 2023-07-17 18:39:34 +08:00
crazywhalecc
57e3193a2a add document 2023-07-17 18:01:45 +08:00
crazywhalecc
388f547878 add document 2023-07-17 17:59:44 +08:00
crazywhalecc
f2e483540b add opcache support 2023-07-16 18:13:11 +08:00
crazywhalecc
7d31c371e0 change gmp to github mirror 2023-07-16 17:59:21 +08:00
crazywhalecc
bc750733d5 mark php version 2023-07-16 17:55:20 +08:00
crazywhalecc
a66e22d9ed fix cron 2023-07-16 17:54:30 +08:00
crazywhalecc
cc5974d07d add download cache builder 2023-07-16 17:53:37 +08:00
12 changed files with 331 additions and 120 deletions

40
.github/workflows/download-cache.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: archive download sources weekly
on:
workflow_dispatch:
schedule:
- cron: "* 14 * * 5"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
download:
name: cache download sources
runs-on: ubuntu-latest
strategy:
matrix:
php-version: [ "8.0", "8.1", "8.2" ]
steps:
- uses: actions/checkout@v3
# Cache composer dependencies
- id: cache-composer-deps
uses: actions/cache@v3
with:
path: vendor
key: composer-dependencies
# If there's no Composer cache, install dependencies
- if: steps.cache-composer-deps.outputs.cache-hit != 'true'
run: composer update --no-dev
# If there's no dependencies cache, fetch sources, with or without debug
- if: steps.cache-download.outputs.cache-hit != 'true'
run: ./bin/spc download --with-php=${{ matrix.php-version }} --all --debug
# Upload downloaded files
- uses: actions/upload-artifact@v3
with:
name: download-files-${{ matrix.php-version }}
path: downloads/

View File

@@ -37,7 +37,11 @@ Here is the architecture support status, where `CI` represents support for GitHu
Currently supported PHP versions for compilation are: `7.4`, `8.0`, `8.1`, `8.2`.
## Usage
## Docs
docs here: <https://static-php-cli.zhamao.me>.
## Simple Usage
Please first select the extension you want to compile based on the extension list below.
@@ -49,7 +53,7 @@ If you don't compile yourself, you can download pre-compiled artifact from Actio
### Supported Extensions
[Supported Extension List](/ext-support.md)
[Supported Extension List](https://static-php-cli.zhamao.me/en/guide/extensions.html)
> If there is no extension you need here, you can submit an issue.
@@ -100,7 +104,7 @@ Basic usage for building php and micro with some extensions:
cd static-php-cli
composer update
chmod +x bin/spc
# Check system tool dependencies, fix them automatically (only support macOS) (TODO: Linux distro support)
# Check system tool dependencies, fix them automatically
./bin/spc doctor
# fetch all libraries
./bin/spc fetch --all
@@ -217,6 +221,8 @@ The basic principles for contributing are as follows:
camelCase and underscore formats should be followed, and mixing within the same module is prohibited.
- When compiling external libraries and creating patches, compatibility with different operating systems should be considered.
If you want to contribute document content, please go to [crazywhalecc/static-php-cli-docs](https://github.com/crazywhalecc/static-php-cli-docs).
## Sponsor this project
You can sponsor my project on [this page](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md).

View File

@@ -36,6 +36,10 @@ If you are using English, see [English README](README-en.md).
目前支持编译的 PHP 版本为:`7.4``8.0``8.1``8.2`
## 文档
点击这里查看文档:<https://static-php-cli.zhamao.me>。
## 使用
请先根据下方扩展列表选择你要编译的扩展。
@@ -48,7 +52,7 @@ If you are using English, see [English README](README-en.md).
### 支持的扩展情况
[扩展支持列表](/ext-support.md)
[扩展支持列表](https://static-php-cli.zhamao.me/zh/guide/extensions.html)
> 如果这里没有你需要的扩展,可以提交 Issue。
@@ -94,7 +98,7 @@ chmod +x bin/setup-runtime
cd static-php-cli
composer update
chmod +x bin/spc
# 检查环境依赖,并根据提示的命令安装缺失的编译工具(目前仅支持 macOSLinux 后续会支持)
# 检查环境依赖,并根据提示的命令安装缺失的编译工具
./bin/spc doctor
# 拉取所有依赖库
./bin/spc fetch --all
@@ -200,6 +204,8 @@ cat micro.sfx code.php > single-app && chmod +x single-app
另外,添加新扩展的贡献方式,可以参考下方 `进阶`
如果你想贡献文档内容,请到项目仓库 [crazywhalecc/static-php-cli-docs](https://github.com/crazywhalecc/static-php-cli-docs) 贡献。
## 赞助本项目
你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。

View File

@@ -91,9 +91,8 @@
}
},
"gmp": {
"type": "filelist",
"url": "https://gmplib.org/download/gmp/",
"regex": "/href=\"(?<file>gmp-(?<version>[^\"]+)\\.tar\\.xz)\"/",
"type": "ghtagtar",
"repo": "alisw/GMP",
"license": {
"type": "text",
"text": "Since version 6, GMP is distributed under the dual licenses, GNU LGPL v3 and GNU GPL v2. These licenses make the library free to use, share, and improve, and allow you to pass on the result. The GNU licenses give freedoms, but also set firm restrictions on the use with non-free programs."

View File

@@ -1,111 +1,3 @@
# Extension List
> - yes: supported and tested
> - untested: supported, but not tested
> - empty: not supported yet
> - no with issue link: not supported yet due to issue
> - partial with issue link: supported but not perfect due to issue
| | Linux | macOS | Windows |
|-----------------|---------------------------------------------------------------------|----------------------------------------------------------------|---------|
| apcu | yes, untested | yes, untested | |
| bcmath | yes | yes | |
| bz2 | yes | yes | |
| calendar | yes | yes | |
| ctype | yes | yes | |
| curl | yes | yes | |
| dba | yes | yes | |
| dom | yes | yes | |
| enchant | | | |
| event | yes | yes | |
| exif | yes | yes | |
| ffi | | yes, [docs]() | |
| filter | yes | yes | |
| fileinfo | yes | yes | |
| ftp | yes | yes | |
| gd | yes | yes | |
| gettext | | | |
| gmp | yes | yes | |
| iconv | yes | yes | |
| imagick | yes | yes | |
| inotify | yes | yes | |
| intl | [partial](https://github.com/crazywhalecc/static-php-cli/issues/66) | yes | |
| mbstring | yes | yes | |
| mbregex | yes | yes | |
| mcrypt | | [no](https://github.com/crazywhalecc/static-php-cli/issues/32) | |
| mongodb | yes | yes | |
| mysqli | yes | yes | |
| mysqlnd | yes | yes | |
| openssl | yes | yes | |
| password-argon2 | | | |
| pcntl | yes | yes | |
| pdo | yes | yes | |
| pdo_mysql | yes | yes | |
| pdo_sqlite | yes | yes | |
| pdo_pgsql | | | |
| phar | yes | yes | |
| posix | yes | yes | |
| protobuf | yes | yes | |
| readline | yes, untested | yes, untested | |
| redis | yes | yes | |
| session | yes | yes | |
| shmop | yes | yes | |
| simplexml | yes | yes | |
| soap | yes | yes | |
| sockets | yes | yes | |
| sodium | yes | yes | |
| sqlite3 | yes | yes | |
| ssh2 | yes, untested | yes, untested | |
| swow | yes | yes | |
| swoole | [partial](https://github.com/crazywhalecc/static-php-cli/issues/51) | yes | |
| tokenizer | yes | yes | |
| xlswriter | yes | yes | |
| xml | yes | yes | |
| xmlreader | yes, untested | yes, untested | |
| xmlwriter | yes, untested | yes, untested | |
| zip | yes, untested | yes, untested | |
| zlib | yes | yes | |
| zstd | yes | yes | |
## Additional Requirements
- phpmicro requires PHP >= 8.0
- swoole >= 5.0 requires PHP >= 8.0
- swow requires PHP >= 8.0
## Typical Extension List Example
Here are some extension list example for different use.
- For general use: `"bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,ftp,filter,gd,iconv,xml,mbstring,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,redis,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip"`
- For static-php-cli self: `"posix,pcntl,phar,tokenizer,iconv,zlib"`
- For static-php-cli self (with dev dependencies): `"posix,pcntl,phar,tokenizer,iconv,zlib,xml,dom,xmlwriter,xmlreader,fileinfo"`
- Minimum, with no extension: `""`
Compile with all supported extensions (exclude `swow`, `swoole`, because these will change the default behavior of php):
```bash
bin/spc build --build-all bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,iconv,mbregex,mbstring,mongodb,mysqli,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,protobuf,redis,session,shmop,simplexml,soap,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,yaml,zip,zlib,zstd --with-libs=libjpeg,freetype,libwebp,libavif --debug
```
## Additional Libraries
Some extensions have soft dependencies, you can enable extra features by adding these libs using `--with-libs`.
For example, to compile with gd extension, with `libwebp, libgif, libavif, libjpeg, freetype` extra features:
```bash
bin/spc build gd --with-libs=libjpeg,freetype,libwebp,libavif --build-cli
```
> If you don't add them, your compilation will not enable these features.
## Limitations
- swow and swoole cannot be compiled at the same time.
- openssl needs manual configuration for ssl certificate. (TODO: I will write a wiki about it)
- some extensions need system configuration, e.g. `curl` and `openssl` will search ssl certificate on your system.
## Bugs and TODOs
See [#32](https://github.com/crazywhalecc/static-php-cli/issues/32).
See: [Docs - Extensions](https://static-php-cli.zhamao.me/en/guide/extensions.html)

View File

@@ -21,7 +21,7 @@ class LinuxBuilder extends BuilderBase
use UnixBuilderTrait;
/** @var string[] Linux 环境下编译依赖的命令 */
public const REQUIRED_COMMANDS = ['make', 'bison', 'flex', 'pkg-config', 'git', 'autoconf', 'automake', 'tar', 'unzip', /* 'xz', 好像不需要 */ 'gzip', 'bzip2', 'cmake'];
public const REQUIRED_COMMANDS = ['make', 'bison', 'flex', 'git', 'autoconf', 'automake', 'tar', 'unzip', /* 'xz', 好像不需要 */ 'gzip', 'bzip2', 'cmake'];
/** @var string 使用的 libc */
public string $libc;

View File

@@ -40,6 +40,7 @@ class libpng extends MacOSLibraryBase
};
shell()->cd($this->source_dir)
->exec('chmod +x ./configure')
->exec('chmod +x ./install-sh')
->exec(
"{$this->builder->configure_env} ./configure " .
"--host={$this->builder->gnu_arch}-apple-darwin " .

View File

@@ -10,11 +10,16 @@ use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand('doctor', 'Diagnose whether the current environment can compile normally')]
class DoctorCommand extends BaseCommand
{
public function configure()
{
$this->addOption('auto-fix', null, null, 'Automatically fix failed items (if possible)');
}
public function handle(): int
{
try {
$checker = new CheckListHandler($this->input, $this->output);
$checker->runCheck(FIX_POLICY_PROMPT);
$checker->runCheck($this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT);
$this->output->writeln('<info>Doctor check complete !</info>');
} catch (\Throwable $e) {
$this->output->writeln('<error>' . $e->getMessage() . '</error>');

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SPC\command;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
@@ -18,6 +19,8 @@ use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand('download', 'Download required sources', ['fetch'])]
class DownloadCommand extends BaseCommand
{
use UnixSystemUtilTrait;
protected string $php_major_ver;
public function configure()
@@ -28,12 +31,13 @@ class DownloadCommand extends BaseCommand
$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');
$this->addOption('from-zip', 'Z', InputOption::VALUE_REQUIRED, 'Fetch from zip archive');
}
public function initialize(InputInterface $input, OutputInterface $output)
{
// --all 等于 "" "",也就是所有东西都要下载
if ($input->getOption('all') || $input->getOption('clean')) {
if ($input->getOption('all') || $input->getOption('clean') || $input->getOption('from-zip')) {
$input->setArgument('sources', '');
}
parent::initialize($input, $output);
@@ -66,6 +70,44 @@ class DownloadCommand extends BaseCommand
return 0;
}
// --from-zip
if ($path = $this->getOption('from-zip')) {
if (!file_exists($path)) {
logger()->critical('File ' . $path . ' not exist or not a zip archive.');
return 1;
}
// remove old download files first
if (is_dir(DOWNLOAD_PATH)) {
logger()->warning('You are doing some operations that not recoverable: removing directories below');
logger()->warning(DOWNLOAD_PATH);
logger()->alert('I will remove these dir after 5 seconds !');
sleep(5);
f_passthru((PHP_OS_FAMILY === 'Windows' ? 'rmdir /s /q ' : 'rm -rf ') . DOWNLOAD_PATH);
}
// unzip command check
if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) {
logger()->critical('Missing unzip command, you need to install it first !');
logger()->critical('You can use "bin/spc doctor" command to check and install required tools');
return 1;
}
// create downloads
try {
if (PHP_OS_FAMILY !== 'Windows') {
f_passthru('mkdir ' . DOWNLOAD_PATH . ' && cd ' . DOWNLOAD_PATH . ' && unzip ' . escapeshellarg($path));
}
// Windows TODO
if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) {
throw new RuntimeException('.lock.json not exist in "downloads/"');
}
} catch (RuntimeException $e) {
logger()->critical('Extract failed: ' . $e->getMessage());
return 1;
}
logger()->info('Extract success');
return 0;
}
// Define PHP major version
$ver = $this->php_major_ver = $this->getOption('with-php') ?? '8.1';
define('SPC_BUILD_PHP_VERSION', $ver);

View File

@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\store\FileSystem;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand('micro:combine', 'Combine micro.sfx and php code together')]
class MicroCombineCommand extends BaseCommand
{
public function configure()
{
$this->addArgument('file', InputArgument::REQUIRED, 'The php or phar file to be combined');
$this->addOption('with-micro', 'M', InputOption::VALUE_REQUIRED, 'Customize your micro.sfx file');
$this->addOption('with-ini-set', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'ini to inject into micro.sfx when combining');
$this->addOption('with-ini-file', 'N', InputOption::VALUE_REQUIRED, 'ini file to inject into micro.sfx when combining');
$this->addOption('output', 'O', InputOption::VALUE_REQUIRED, 'Customize your output binary file name');
}
public function handle(): int
{
// 0. Initialize path variables
$internal = FileSystem::convertPath(BUILD_ROOT_PATH . '/bin/micro.sfx');
$micro_file = $this->input->getOption('with-micro');
$file = $this->getArgument('file');
$ini_set = $this->input->getOption('with-ini-set');
$ini_file = $this->input->getOption('with-ini-file');
$target_ini = [];
$output = $this->input->getOption('output') ?? 'my-app';
$ini_part = '';
// 1. Make sure specified micro.sfx file exists
if ($micro_file !== null && !file_exists($micro_file)) {
$this->output->writeln('<error>The micro.sfx file you specified is incorrect or does not exist!</error>');
return 1;
}
// 2. Make sure buildroot/bin/micro.sfx exists
if ($micro_file === null && !file_exists($internal)) {
$this->output->writeln('<error>You haven\'t compiled micro.sfx yet, please use "build" command and "--build-micro" to compile phpmicro first!</error>');
return 1;
}
// 3. Use buildroot/bin/micro.sfx
if ($micro_file === null) {
$micro_file = $internal;
}
// 4. Make sure php or phar file exists
if (!is_file(FileSystem::convertPath($file))) {
$this->output->writeln('<error>The file to combine does not exist!</error>');
return 1;
}
// 5. Confirm ini files (ini-set has higher priority)
if ($ini_file !== null) {
// Check file exist first
if (!file_exists($ini_file)) {
$this->output->writeln('<error>The ini file to combine does not exist! (' . $ini_file . ')</error>');
return 1;
}
$arr = parse_ini_file($ini_file);
if ($arr === false) {
$this->output->writeln('<error>Cannot parse ini file</error>');
return 1;
}
$target_ini = array_merge($target_ini, $arr);
}
// 6. Confirm ini sets
if ($ini_set !== []) {
foreach ($ini_set as $item) {
$arr = parse_ini_string($item);
if ($arr === false) {
$this->output->writeln('<error>--with-ini-set parse failed</error>');
return 1;
}
$target_ini = array_merge($target_ini, $arr);
}
}
// 7. Generate ini injection parts
if (!empty($target_ini)) {
$ini_str = $this->encodeINI($target_ini);
logger()->debug('Injecting ini parts: ' . PHP_EOL . $ini_str);
$ini_part = "\xfd\xf6\x69\xe6";
$ini_part .= pack('N', strlen($ini_str));
$ini_part .= $ini_str;
}
// 8. Combine !
$output = FileSystem::isRelativePath($output) ? (WORKING_DIR . '/' . $output) : $output;
$file_target = file_get_contents($micro_file) . $ini_part . file_get_contents($file);
$result = file_put_contents($output, $file_target);
if ($result === false) {
$this->output->writeln('<error>Combine failed.</error>');
return 1;
}
// 9. chmod +x
chmod($output, 0755);
$this->output->writeln('<info>Combine success! Binary file: ' . $output . '</info>');
return 0;
}
private function encodeINI(array $array): string
{
$res = [];
foreach ($array as $key => $val) {
if (is_array($val)) {
$res[] = "[{$key}]";
foreach ($val as $skey => $sval) {
$res[] = "{$skey}=" . (is_numeric($sval) ? $sval : '"' . $sval . '"');
}
} else {
$res[] = "{$key}=" . (is_numeric($val) ? $val : '"' . $val . '"');
}
}
return implode("\n", $res);
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace SPC\doctor\item;
use SPC\builder\linux\SystemUtil;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult;
use SPC\exception\RuntimeException;
class LinuxToolCheckList
{
use UnixSystemUtilTrait;
public const TOOLS_ALPINE = [
'make', 'bison', 'flex',
'git', 'autoconf', 'automake',
'tar', 'unzip', 'gzip',
'bzip2', 'cmake', 'gcc',
'g++', 'patch',
];
public const TOOLS_DEBIAN = [
'make', 'bison', 'flex',
'git', 'autoconf', 'automake',
'tar', 'unzip', 'gzip',
'bzip2', 'cmake', 'patch',
];
#[AsCheckItem('if necessary tools are installed', limit_os: 'Linux')]
public function checkCliTools(): ?CheckResult
{
$distro = SystemUtil::getOSRelease();
$required = match ($distro['dist']) {
'alpine' => self::TOOLS_ALPINE,
default => self::TOOLS_DEBIAN,
};
$missing = [];
foreach ($required as $cmd) {
if ($this->findCommand($cmd) === null) {
$missing[] = $cmd;
}
}
if (!empty($missing)) {
return match ($distro['dist']) {
'ubuntu', 'alpine', 'debian' => CheckResult::fail(implode(', ', $missing) . ' not installed on your system', 'install-linux-tools', [$distro, $missing]),
default => CheckResult::fail(implode(', ', $missing) . ' not installed on your system'),
};
}
return CheckResult::ok();
}
#[AsCheckItem('if necessary packages are installed', limit_os: 'Linux')]
public function checkSystemOSPackages(): ?CheckResult
{
$distro = SystemUtil::getOSRelease();
if ($distro['dist'] === 'alpine') {
// check linux-headers installation
if (!file_exists('/usr/include/linux/mman.h')) {
return CheckResult::fail('linux-headers not installed on your system', 'install-linux-tools', [$distro, ['linux-headers']]);
}
}
return CheckResult::ok();
}
#[AsFixItem('install-linux-tools')]
public function fixBuildTools(array $distro, array $missing): bool
{
$install_cmd = match ($distro['dist']) {
'ubuntu', 'debian' => 'apt install -y',
'alpine' => 'apk add',
default => throw new RuntimeException('Current linux distro is not supported for auto-install musl packages'),
};
$prefix = '';
if (get_current_user() !== 'root') {
$prefix = 'sudo ';
logger()->warning('Current user is not root, using sudo for running command');
}
try {
shell(true)->exec($prefix . $install_cmd . ' ' . implode(' ', $missing));
} catch (RuntimeException) {
return false;
}
return true;
}
}

View File

@@ -439,6 +439,20 @@ class FileSystem
self::$_extract_hook[$name][] = $callback;
}
/**
* Check whether the path is a relative path (judging according to whether the first character is "/")
*
* @param string $path Path
*/
public static function isRelativePath(string $path): bool
{
// 适配 Windows 的多盘符目录形式
if (DIRECTORY_SEPARATOR === '\\') {
return !(strlen($path) > 2 && ctype_alpha($path[0]) && $path[1] === ':');
}
return strlen($path) > 0 && $path[0] !== '/';
}
private static function emitSourceExtractHook(string $name)
{
foreach ((self::$_extract_hook[$name] ?? []) as $hook) {