Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Downloader enhancement #685

Merged
merged 23 commits into from
Mar 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2c0bb1f
Quiet console output for non --debug mode
crazywhalecc Mar 30, 2025
5648681
Adjust console output and PHPDoc
crazywhalecc Mar 30, 2025
87c0535
Allow locking different arch pre-built content
crazywhalecc Mar 30, 2025
0e4a3f5
Add install-pkg and pre-built test
crazywhalecc Mar 30, 2025
ab4d7fa
Fix typo
crazywhalecc Mar 30, 2025
67d2ad5
Add debug console output for Downloader
crazywhalecc Mar 30, 2025
631a1b5
Add libc version for pre-built content name
crazywhalecc Mar 30, 2025
8e5657e
Separate musl-dist and non-musl-dist
crazywhalecc Mar 30, 2025
4e5c0f0
Add additional log output for pre-built finder
crazywhalecc Mar 30, 2025
a940200
Return default version for musl and musl-wrapper
crazywhalecc Mar 30, 2025
01d3cb4
Test arm runner
crazywhalecc Mar 30, 2025
50cfc58
Re-enable musl version detect
crazywhalecc Mar 30, 2025
23bd216
Add upx cmd for tests
crazywhalecc Mar 30, 2025
2d7c052
Remove comment
crazywhalecc Mar 30, 2025
11f0957
Add SPC_DOCKER_DEBUG for gnu docker, remove classmap for alpine docker
crazywhalecc Mar 30, 2025
acb8cea
Add glibc build for CI
crazywhalecc Mar 30, 2025
c58ea0c
Fix PHP warning in test-extensions.php
crazywhalecc Mar 30, 2025
67afffe
Remove redundant suffix, add libc version suffix
crazywhalecc Mar 30, 2025
62d619b
Fix redundant pre-built name calling
crazywhalecc Mar 30, 2025
7dec34b
Fix redundant pre-built name calling
crazywhalecc Mar 30, 2025
237d39f
Fix CI wrong runner name
crazywhalecc Mar 30, 2025
6dd6d80
Fix end of line space
crazywhalecc Mar 30, 2025
5c04638
Full spell for SPC_DOWNLOAD
crazywhalecc Mar 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .github/workflows/build-unix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ on:
os:
required: true
description: Build target OS
default: 'linux-x86_64'
type: choice
options:
- 'linux-x86_64'
- 'linux-aarch64'
- 'linux-x86_64-glibc'
- 'linux-aarch64-glibc'
- 'macos-x86_64'
- 'macos-aarch64'
php-version:
Expand All @@ -22,7 +25,6 @@ on:
- '8.3'
- '8.2'
- '8.1'
- '8.0'
extensions:
description: Extensions to build (comma separated)
required: true
Expand Down Expand Up @@ -77,9 +79,19 @@ jobs:
RUNS_ON="ubuntu-latest"
;;
linux-aarch64)
DOWN_CMD="SPC_USE_ARCH=aarch64 ./bin/spc-alpine-docker download"
BUILD_CMD="SPC_USE_ARCH=aarch64 ./bin/spc-alpine-docker build"
RUNS_ON="ubuntu-latest"
DOWN_CMD="./bin/spc-alpine-docker download"
BUILD_CMD="./bin/spc-alpine-docker build"
RUNS_ON="ubuntu-24.04-arm"
;;
linux-x86_64-glibc)
DOWN_CMD="./bin/spc-gnu-docker download"
BUILD_CMD="./bin/spc-gnu-docker build"
RUNS_ON="ubuntu-22.04"
;;
linux-aarch64-glibc)
DOWN_CMD="./bin/spc-gnu-docker download"
BUILD_CMD="./bin/spc-gnu-docker build"
RUNS_ON="ubuntu-22.04-arm"
;;
macos-x86_64)
DOWN_CMD="composer update --no-dev --classmap-authoritative && ./bin/spc doctor --auto-fix && ./bin/spc download"
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,18 @@ jobs:
run: composer update -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist

- name: "Run Build Tests (doctor)"
run: bin/spc doctor --auto-fix --debug
run: php src/globals/test-extensions.php doctor_cmd ${{ matrix.os }} ${{ matrix.php }}

- name: "Prepare UPX for Windows"
if: matrix.os == 'windows-latest'
if: ${{ startsWith(matrix.os, 'windows-') }}
run: |
bin/spc install-pkg upx
php src/globals/test-extensions.php install_upx_cmd ${{ matrix.os }} ${{ matrix.php }}
echo "UPX_CMD=$(php src/globals/test-extensions.php upx)" >> $env:GITHUB_ENV

- name: "Prepare UPX for Linux"
if: matrix.os == 'ubunut-latest'
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
run: |
bin/spc install-pkg upx
php src/globals/test-extensions.php install_upx_cmd ${{ matrix.os }} ${{ matrix.php }}
echo "UPX_CMD=$(php src/globals/test-extensions.php upx)" >> $GITHUB_ENV

- name: "Run Build Tests (download)"
Expand Down
2 changes: 1 addition & 1 deletion bin/spc-alpine-docker
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ WORKDIR /app
ADD ./src /app/src
COPY ./composer.* /app/
ADD ./bin /app/bin
RUN composer install --no-dev --classmap-authoritative
RUN composer install --no-dev
EOF
fi

Expand Down
6 changes: 5 additions & 1 deletion bin/spc-gnu-docker
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,8 @@ echo 'SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt"'
# shellcheck disable=SC2086
# shellcheck disable=SC2090

$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH bin/spc $@
if [ "$SPC_DOCKER_DEBUG" = "yes" ]; then
$DOCKER_EXECUTABLE run --rm -it --privileged $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH
else
$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" --env-file /tmp/spc-gnu-docker.env $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH bin/spc $@
fi
6 changes: 3 additions & 3 deletions config/pre-built.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"repo": "static-php/static-php-cli-hosted",
"prefer-stable": true,
"match-pattern": "{name}-{arch}-{os}.txz",
"suffix": "txz"
}
"match-pattern-linux": "{name}-{arch}-{os}-{libc}-{libcver}.txz",
"match-pattern": "{name}-{arch}-{os}.txz"
}
6 changes: 4 additions & 2 deletions src/SPC/builder/LibraryBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\Downloader;
use SPC\store\FileSystem;
use SPC\store\SourceManager;

Expand Down Expand Up @@ -45,8 +46,9 @@ public function setup(bool $force = false): int
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
$source = Config::getLib(static::NAME, 'source');
// if source is locked as pre-built, we just tryInstall it
if (isset($lock[$source]) && ($lock[$source]['lock_as'] ?? SPC_LOCK_SOURCE) === SPC_LOCK_PRE_BUILT) {
return $this->tryInstall($lock[$source]['filename'], $force);
$pre_built_name = Downloader::getPreBuiltLockName($source);
if (isset($lock[$pre_built_name]) && ($lock[$pre_built_name]['lock_as'] ?? SPC_DOWNLOAD_SOURCE) === SPC_DOWNLOAD_PRE_BUILT) {
return $this->tryInstall($lock[$pre_built_name]['filename'], $force);
}
return $this->tryBuild($force);
}
Expand Down
35 changes: 35 additions & 0 deletions src/SPC/builder/linux/SystemUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,39 @@ public static function getSupportedDistros(): array
'arch', 'manjaro',
];
}

/**
* Get libc version string from ldd
*/
public static function getLibcVersionIfExists(): ?string
{
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'glibc') {
$result = shell()->execWithResult('ldd --version', false);
if ($result[0] !== 0) {
return null;
}
// get first line
$first_line = $result[1][0];
// match ldd version: "ldd (some useless text) 2.17" match 2.17
$pattern = '/ldd\s+\(.*?\)\s+(\d+\.\d+)/';
if (preg_match($pattern, $first_line, $matches)) {
return $matches[1];
}
return null;
}
if (PHP_OS_FAMILY === 'Linux' && getenv('SPC_LIBC') === 'musl') {
if (self::isMuslDist()) {
$result = shell()->execWithResult('ldd 2>&1', false);
} else {
$result = shell()->execWithResult('/usr/local/musl/lib/libc.so 2>&1', false);
}
// Match Version * line
// match ldd version: "Version 1.2.3" match 1.2.3
$pattern = '/Version\s+(\d+\.\d+\.\d+)/';
if (preg_match($pattern, $result[1][1] ?? '', $matches)) {
return $matches[1];
}
}
return null;
}
}
24 changes: 15 additions & 9 deletions src/SPC/command/DeleteDownloadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\WrongUsageException;
use SPC\store\Downloader;
use SPC\store\FileSystem;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -47,30 +48,35 @@ public function handle(): int
$chosen_sources = $sources;
$lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];

$deleted_sources = [];
foreach ($chosen_sources as $source) {
$source = trim($source);
if (!isset($lock[$source])) {
logger()->warning("Source/Package [{$source}] not locked or not downloaded, skipped.");
continue;
foreach ([$source, Downloader::getPreBuiltLockName($source)] as $name) {
if (isset($lock[$name])) {
$deleted_sources[] = $name;
}
}
}

foreach ($deleted_sources as $lock_name) {
// remove download file/dir if exists
if ($lock[$source]['source_type'] === 'archive') {
if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$source]['filename']))) {
if ($lock[$lock_name]['source_type'] === 'archive') {
if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$lock_name]['filename']))) {
logger()->info('Deleting file ' . $path);
unlink($path);
} else {
logger()->warning("Source/Package [{$source}] file not found, skip deleting file.");
logger()->warning("Source/Package [{$lock_name}] file not found, skip deleting file.");
}
} else {
if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$source]['dirname']))) {
if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$lock_name]['dirname']))) {
logger()->info('Deleting dir ' . $path);
FileSystem::removeDir($path);
} else {
logger()->warning("Source/Package [{$source}] directory not found, skip deleting dir.");
logger()->warning("Source/Package [{$lock_name}] directory not found, skip deleting dir.");
}
}
// remove locked sources
unset($lock[$source]);
unset($lock[$lock_name]);
}
FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
logger()->info('Delete success!');
Expand Down
21 changes: 15 additions & 6 deletions src/SPC/command/DownloadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace SPC\command;

use SPC\builder\linux\SystemUtil;
use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
Expand Down Expand Up @@ -212,7 +213,7 @@ public function handle(): int
if (isset($config['filename'])) {
$new_config['filename'] = $config['filename'];
}
logger()->info("Fetching source {$source} from custom url [{$ni}/{$cnt}]");
logger()->info("[{$ni}/{$cnt}] Downloading source {$source} from custom url: {$new_config['url']}");
Downloader::downloadSource($source, $new_config, true);
} elseif (isset($custom_gits[$source])) {
$config = Config::getSource($source);
Expand All @@ -224,23 +225,30 @@ public function handle(): int
if (isset($config['path'])) {
$new_config['path'] = $config['path'];
}
logger()->info("Fetching source {$source} from custom git [{$ni}/{$cnt}]");
logger()->info("[{$ni}/{$cnt}] Downloading source {$source} from custom git: {$new_config['url']}");
Downloader::downloadSource($source, $new_config, true);
} else {
$config = Config::getSource($source);
// Prefer pre-built, we need to search pre-built library
if ($this->getOption('prefer-pre-built') && ($config['provide-pre-built'] ?? false) === true) {
// We need to replace pattern
$find = str_replace(['{name}', '{arch}', '{os}'], [$source, arch2gnu(php_uname('m')), strtolower(PHP_OS_FAMILY)], Config::getPreBuilt('match-pattern'));
$replace = [
'{name}' => $source,
'{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY),
'{libc}' => getenv('SPC_LIBC') ?: 'default',
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default',
];
$find = str_replace(array_keys($replace), array_values($replace), Config::getPreBuilt('match-pattern'));
// find filename in asset list
if (($url = $this->findPreBuilt($pre_built_libs, $find)) !== null) {
logger()->info("Fetching pre-built content {$source} [{$ni}/{$cnt}]");
Downloader::downloadSource($source, ['type' => 'url', 'url' => $url], $force_all || in_array($source, $force_list), SPC_LOCK_PRE_BUILT);
logger()->info("[{$ni}/{$cnt}] Downloading pre-built content {$source}");
Downloader::downloadSource($source, ['type' => 'url', 'url' => $url], $force_all || in_array($source, $force_list), SPC_DOWNLOAD_PRE_BUILT);
continue;
}
logger()->warning("Pre-built content not found for {$source}, fallback to source download");
}
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]");
logger()->info("[{$ni}/{$cnt}] Downloading source {$source}");
Downloader::downloadSource($source, $config, $force_all || in_array($source, $force_list));
}
}
Expand Down Expand Up @@ -352,6 +360,7 @@ private function calculateSourcesByLib(array $libs, bool $include_suggests = tru
*/
private function findPreBuilt(array $assets, string $filename): ?string
{
logger()->debug("Finding pre-built asset {$filename}");
foreach ($assets as $asset) {
if ($asset['name'] === $filename) {
return $asset['browser_download_url'];
Expand Down
15 changes: 13 additions & 2 deletions src/SPC/command/dev/PackLibCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use SPC\builder\BuilderProvider;
use SPC\builder\LibraryBase;
use SPC\builder\linux\SystemUtil;
use SPC\command\BuildCommand;
use SPC\exception\ExceptionHandler;
use SPC\exception\FileSystemException;
Expand All @@ -23,6 +24,7 @@ class PackLibCommand extends BuildCommand
public function configure(): void
{
$this->addArgument('library', InputArgument::REQUIRED, 'The library will be compiled');
$this->addOption('show-libc-ver', null, null);
}

public function handle(): int
Expand All @@ -47,7 +49,7 @@ public function handle(): int
// Get lock info
$lock = json_decode(file_get_contents(DOWNLOAD_PATH . '/.lock.json'), true) ?? [];
$source = Config::getLib($lib->getName(), 'source');
if (!isset($lock[$source]) || ($lock[$source]['lock_as'] ?? SPC_LOCK_SOURCE) === SPC_LOCK_PRE_BUILT) {
if (!isset($lock[$source]) || ($lock[$source]['lock_as'] ?? SPC_DOWNLOAD_SOURCE) === SPC_DOWNLOAD_PRE_BUILT) {
logger()->critical("The library {$lib->getName()} is downloaded as pre-built, we need to build it instead of installing pre-built.");
return static::FAILURE;
}
Expand All @@ -69,7 +71,16 @@ public function handle(): int
// write list to packlib_files.txt
FileSystem::writeFile(WORKING_DIR . '/packlib_files.txt', implode("\n", $increase_files));
// pack
$filename = WORKING_DIR . '/dist/' . $lib->getName() . '-' . arch2gnu(php_uname('m')) . '-' . strtolower(PHP_OS_FAMILY) . '.' . Config::getPreBuilt('suffix');
$filename = Config::getPreBuilt('match-pattern');
$replace = [
'{name}' => $lib->getName(),
'{arch}' => arch2gnu(php_uname('m')),
'{os}' => strtolower(PHP_OS_FAMILY),
'{libc}' => getenv('SPC_LIBC') ?: 'default',
'{libcver}' => PHP_OS_FAMILY === 'Linux' ? (SystemUtil::getLibcVersionIfExists() ?? 'default') : 'default',
];
$filename = str_replace(array_keys($replace), array_values($replace), $filename);
$filename = WORKING_DIR . '/dist/' . $filename;
f_passthru('tar -czf ' . $filename . ' -T ' . WORKING_DIR . '/packlib_files.txt');
logger()->info('Pack library ' . $lib->getName() . ' to ' . $filename . ' complete.');
}
Expand Down
19 changes: 19 additions & 0 deletions src/SPC/store/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,30 @@ class Config

public static ?array $pre_built = null;

/**
* @throws WrongUsageException
* @throws FileSystemException
*/
public static function getPreBuilt(string $name): mixed
{
if (self::$pre_built === null) {
self::$pre_built = FileSystem::loadConfigArray('pre-built');
}
$supported_sys_based = ['match-pattern', 'prefer-stable', 'repo'];
if (in_array($name, $supported_sys_based)) {
$m_key = match (PHP_OS_FAMILY) {
'Windows' => ['-windows', '-win', ''],
'Darwin' => ['-macos', '-unix', ''],
'Linux' => ['-linux', '-unix', ''],
'BSD' => ['-freebsd', '-bsd', '-unix', ''],
default => throw new WrongUsageException('OS ' . PHP_OS_FAMILY . ' is not supported'),
};
foreach ($m_key as $v) {
if (isset(self::$pre_built["{$name}{$v}"])) {
return self::$pre_built["{$name}{$v}"];
}
}
}
return self::$pre_built[$name] ?? null;
}

Expand Down
Loading