Skip to content

Commit 600ba82

Browse files
committed
Enhance musl-wrapper and musl-toolchain installation process
1 parent d16f5a9 commit 600ba82

File tree

8 files changed

+102
-35
lines changed

8 files changed

+102
-35
lines changed

config/artifact.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
}
99
}
1010
},
11+
"musl-wrapper": {
12+
"source": "https://musl.libc.org/releases/musl-1.2.5.tar.gz"
13+
},
1114
"php-src": {
1215
"source": {
1316
"type": "php-release"
@@ -28,8 +31,16 @@
2831
},
2932
"musl-toolchain": {
3033
"binary": {
31-
"linux-x86_64": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/x86_64-musl-toolchain.tgz",
32-
"linux-aarch64": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/aarch64-musl-toolchain.tgz"
34+
"linux-x86_64": {
35+
"type": "url",
36+
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/x86_64-musl-toolchain.tgz",
37+
"extract": "{pkg_root_path}/musl-toolchain"
38+
},
39+
"linux-aarch64": {
40+
"type": "url",
41+
"url": "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/aarch64-musl-toolchain.tgz",
42+
"extract": "{pkg_root_path}/musl-toolchain"
43+
}
3344
}
3445
},
3546
"pkg-config": {

src/SPC/builder/linux/SystemUtil.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public static function getSupportedDistros(): array
141141
{
142142
return [
143143
// debian-like
144-
'debian', 'ubuntu', 'Deepin',
144+
'debian', 'ubuntu', 'Deepin', 'neon',
145145
// rhel-like
146146
'redhat',
147147
// centos

src/SPC/doctor/item/LinuxToolCheckList.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public function checkSystemOSPackages(): ?CheckResult
112112
public function fixBuildTools(array $distro, array $missing): bool
113113
{
114114
$install_cmd = match ($distro['dist']) {
115-
'ubuntu', 'debian', 'Deepin' => 'apt-get install -y',
115+
'ubuntu', 'debian', 'Deepin', 'neon' => 'apt-get install -y',
116116
'alpine' => 'apk add',
117117
'redhat' => 'dnf install -y',
118118
'centos' => 'yum install -y',
@@ -128,7 +128,7 @@ public function fixBuildTools(array $distro, array $missing): bool
128128
logger()->warning('Current user (' . $user . ') is not root, using sudo for running command (may require password input)');
129129
}
130130

131-
$is_debian = in_array($distro['dist'], ['debian', 'ubuntu', 'Deepin']);
131+
$is_debian = in_array($distro['dist'], ['debian', 'ubuntu', 'Deepin', 'neon']);
132132
$to_install = $is_debian ? str_replace('xz', 'xz-utils', $missing) : $missing;
133133
// debian, alpine libtool -> libtoolize
134134
$to_install = str_replace('libtoolize', 'libtool', $to_install);

src/StaticPHP/Artifact/ArtifactDownloader.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ public function download(bool $interactive = true): void
304304
$skipped = [];
305305
foreach ($this->artifacts as $artifact) {
306306
++$current;
307-
if ($this->downloadWithType($artifact, $current, $count) === SPC_DOWNLOAD_STATUS_SKIPPED) {
307+
if ($this->downloadWithType($artifact, $current, $count, interactive: $interactive) === SPC_DOWNLOAD_STATUS_SKIPPED) {
308308
$skipped[] = $artifact->getName();
309309
continue;
310310
}
@@ -342,7 +342,7 @@ public function getOption(string $name, mixed $default = null): mixed
342342
return $this->options[$name] ?? $default;
343343
}
344344

345-
private function downloadWithType(Artifact $artifact, int $current, int $total, bool $parallel = false): int
345+
private function downloadWithType(Artifact $artifact, int $current, int $total, bool $parallel = false, bool $interactive = true): int
346346
{
347347
$queue = $this->generateQueue($artifact);
348348
// already downloaded
@@ -374,7 +374,7 @@ private function downloadWithType(Artifact $artifact, int $current, int $total,
374374
};
375375
$try_h = $try ? 'Try downloading' : 'Downloading';
376376
logger()->info("{$try_h} artifact '{$artifact->getName()}' {$item['display']} ...");
377-
if ($parallel === false) {
377+
if ($parallel === false && $interactive) {
378378
InteractiveTerm::indicateProgress("[{$current}/{$total}] Downloading artifact " . ConsoleColor::green($artifact->getName()) . " {$item['display']} from {$type_display_name} ...");
379379
}
380380
// is valid download type
@@ -392,7 +392,12 @@ private function downloadWithType(Artifact $artifact, int $current, int $total,
392392
$instance = new $call();
393393
$lock = $instance->download($artifact->getName(), $item['config'], $this);
394394
} else {
395-
throw new ValidationException("Artifact has invalid download type '{$item['config']['type']}' for {$item['display']}.");
395+
if ($item['config']['type'] === 'custom') {
396+
$msg = "Artifact [{$artifact->getName()}] has no valid custom " . SystemTarget::getCurrentPlatformString() . ' download callback defined.';
397+
} else {
398+
$msg = "Artifact has invalid download type '{$item['config']['type']}' for {$item['display']}.";
399+
}
400+
throw new ValidationException($msg);
396401
}
397402
if (!$lock instanceof DownloadResult) {
398403
throw new ValidationException("Artifact {$artifact->getName()} has invalid custom return value. Must be instance of DownloadResult.");
@@ -408,13 +413,13 @@ private function downloadWithType(Artifact $artifact, int $current, int $total,
408413
}
409414
// process lock
410415
ApplicationContext::get(ArtifactCache::class)->lock($artifact, $item['lock'], $lock, SystemTarget::getCurrentPlatformString());
411-
if ($parallel === false) {
416+
if ($parallel === false && $interactive) {
412417
$ver = $lock->hasVersion() ? (' (' . ConsoleColor::yellow($lock->version) . ')') : '';
413418
InteractiveTerm::finish('Downloaded ' . ($verified ? 'and verified ' : '') . 'artifact ' . ConsoleColor::green($artifact->getName()) . $ver . " {$item['display']} .");
414419
}
415420
return SPC_DOWNLOAD_STATUS_SUCCESS;
416421
} catch (DownloaderException|ExecutionException $e) {
417-
if ($parallel === false) {
422+
if ($parallel === false && $interactive) {
418423
InteractiveTerm::finish("Download artifact {$artifact->getName()} {$item['display']} failed !", false);
419424
InteractiveTerm::error("Failed message: {$e->getMessage()}", true);
420425
}

src/StaticPHP/Artifact/ArtifactExtractor.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,17 @@ public function extractForPackages(array $packages, bool $force_source = false):
7878
/**
7979
* Extract a single artifact.
8080
*
81-
* @param Artifact $artifact The artifact to extract
82-
* @param bool $force_source If true, always extract source (ignore binary)
81+
* @param Artifact|string $artifact The artifact to extract
82+
* @param bool $force_source If true, always extract source (ignore binary)
8383
*/
84-
public function extract(Artifact $artifact, bool $force_source = false): int
84+
public function extract(Artifact|string $artifact, bool $force_source = false): int
8585
{
86-
$name = $artifact->getName();
86+
if (is_string($artifact)) {
87+
$name = $artifact;
88+
$artifact = ArtifactLoader::getArtifactInstance($name);
89+
} else {
90+
$name = $artifact->getName();
91+
}
8792

8893
// Already extracted in this session
8994
if (isset($this->extracted[$name])) {

src/StaticPHP/Doctor/Doctor.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function checkAll(bool $interactive = true): bool
3434
InteractiveTerm::notice('Starting doctor checks ...');
3535
}
3636
foreach ($this->getValidCheckList() as $check) {
37-
if (!$this->checkItem($check)) {
37+
if (!$this->checkItem($check, $interactive)) {
3838
return false;
3939
}
4040
}
@@ -47,7 +47,7 @@ public function checkAll(bool $interactive = true): bool
4747
* @param CheckItem|string $check The check item to be checked
4848
* @return bool True if the check passed or was fixed, false otherwise
4949
*/
50-
public function checkItem(CheckItem|string $check): bool
50+
public function checkItem(CheckItem|string $check, bool $interactive = true): bool
5151
{
5252
if (is_string($check)) {
5353
$found = null;
@@ -63,7 +63,8 @@ public function checkItem(CheckItem|string $check): bool
6363
}
6464
$check = $found;
6565
}
66-
$this->output?->write("Checking <comment>{$check->item_name}</comment> ... ");
66+
$prepend = $interactive ? ' - ' : '';
67+
$this->output?->write("{$prepend}Checking <comment>{$check->item_name}</comment> ... ");
6768

6869
// call check
6970
$result = call_user_func($check->callback);

src/StaticPHP/Doctor/Item/LinuxMuslCheck.php

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,30 @@
44

55
namespace StaticPHP\Doctor\Item;
66

7+
use StaticPHP\Artifact\ArtifactCache;
8+
use StaticPHP\Artifact\ArtifactDownloader;
9+
use StaticPHP\Artifact\ArtifactExtractor;
710
use StaticPHP\Attribute\Doctor\CheckItem;
811
use StaticPHP\Attribute\Doctor\FixItem;
912
use StaticPHP\Attribute\Doctor\OptionalCheck;
13+
use StaticPHP\DI\ApplicationContext;
1014
use StaticPHP\Doctor\CheckResult;
15+
use StaticPHP\Runtime\Shell\Shell;
16+
use StaticPHP\Toolchain\Interface\ToolchainInterface;
1117
use StaticPHP\Toolchain\MuslToolchain;
1218
use StaticPHP\Toolchain\ZigToolchain;
1319
use StaticPHP\Util\FileSystem;
20+
use StaticPHP\Util\InteractiveTerm;
21+
use StaticPHP\Util\SourcePatcher;
1422
use StaticPHP\Util\System\LinuxUtil;
1523

1624
#[OptionalCheck([self::class, 'optionalCheck'])]
1725
class LinuxMuslCheck
1826
{
1927
public static function optionalCheck(): bool
2028
{
21-
return getenv('SPC_TOOLCHAIN') === MuslToolchain::class ||
22-
(getenv('SPC_TOOLCHAIN') === ZigToolchain::class && !LinuxUtil::isMuslDist());
29+
$toolchain = ApplicationContext::get(ToolchainInterface::class);
30+
return $toolchain instanceof MuslToolchain || $toolchain instanceof ZigToolchain && !LinuxUtil::isMuslDist();
2331
}
2432

2533
/** @noinspection PhpUnused */
@@ -51,23 +59,46 @@ public function checkMuslCrossMake(): CheckResult
5159
#[FixItem('fix-musl-wrapper')]
5260
public function fixMusl(): bool
5361
{
54-
// TODO: implement musl-wrapper installation
55-
// This should:
56-
// 1. Download musl source using Downloader::downloadSource()
57-
// 2. Extract the source using FileSystem::extractSource()
58-
// 3. Apply CVE patches using SourcePatcher::patchFile()
59-
// 4. Build and install musl wrapper
60-
// 5. Add path using putenv instead of editing /etc/profile
61-
return false;
62+
$downloader = new ArtifactDownloader();
63+
$downloader->add('musl-wrapper')->download(false);
64+
$extractor = new ArtifactExtractor(ApplicationContext::get(ArtifactCache::class));
65+
$extractor->extract('musl-wrapper');
66+
67+
// Apply CVE-2025-26519 patch and install musl wrapper
68+
SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0001.patch', SOURCE_PATH . '/musl-wrapper');
69+
SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0002.patch', SOURCE_PATH . '/musl-wrapper');
70+
71+
$prefix = '';
72+
if (get_current_user() !== 'root') {
73+
$prefix = 'sudo ';
74+
logger()->warning('Current user is not root, using sudo for running command');
75+
}
76+
shell()->cd(SOURCE_PATH . '/musl-wrapper')
77+
->exec('CC=gcc CXX=g++ AR=ar LD=ld ./configure --disable-gcc-wrapper')
78+
->exec('CC=gcc CXX=g++ AR=ar LD=ld make -j')
79+
->exec("CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install");
80+
return true;
6281
}
6382

6483
#[FixItem('fix-musl-cross-make')]
6584
public function fixMuslCrossMake(): bool
6685
{
67-
// TODO: implement musl-cross-make installation
68-
// This should:
69-
// 1. Install musl-toolchain package using PackageManager::installPackage()
70-
// 2. Copy toolchain files to /usr/local/musl
71-
return false;
86+
// sudo
87+
$prefix = '';
88+
if (get_current_user() !== 'root') {
89+
$prefix = 'sudo ';
90+
logger()->warning('Current user is not root, using sudo for running command');
91+
}
92+
Shell::passthruCallback(function () {
93+
InteractiveTerm::advance();
94+
});
95+
$downloader = new ArtifactDownloader();
96+
$extractor = new ArtifactExtractor(ApplicationContext::get(ArtifactCache::class));
97+
$downloader->add('musl-toolchain')->download(false);
98+
$extractor->extract('musl-toolchain');
99+
$pkg_root = PKG_ROOT_PATH . '/musl-toolchain';
100+
shell()->exec("{$prefix}cp -rf {$pkg_root}/* /usr/local/musl");
101+
FileSystem::removeDir($pkg_root);
102+
return true;
72103
}
73104
}

src/StaticPHP/Util/System/LinuxUtil.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ class LinuxUtil extends UnixUtil
1313
* Get current linux distro name and version.
1414
*
1515
* @noinspection PhpMissingBreakStatementInspection
16-
* @return array{dist: string, ver: string} Linux distro info (unknown if not found)
16+
* @return array{dist: string, ver: string, family: string} Linux distro info (unknown if not found)
1717
*/
1818
public static function getOSRelease(): array
1919
{
2020
$ret = [
2121
'dist' => 'unknown',
2222
'ver' => 'unknown',
23+
'family' => 'unknown',
2324
];
2425
switch (true) {
2526
case file_exists('/etc/centos-release'):
@@ -44,6 +45,9 @@ public static function getOSRelease(): array
4445
if (preg_match('/^ID=(.*)$/', $line, $matches)) {
4546
$ret['dist'] = $matches[1];
4647
}
48+
if (preg_match('/^ID_LIKE=(.*)$/', $line, $matches)) {
49+
$ret['family'] = $matches[1];
50+
}
4751
if (preg_match('/^VERSION_ID=(.*)$/', $line, $matches)) {
4852
$ret['ver'] = $matches[1];
4953
}
@@ -91,7 +95,7 @@ public static function getSupportedDistros(): array
9195
{
9296
return [
9397
// debian-like
94-
'debian', 'ubuntu', 'Deepin',
98+
'debian', 'ubuntu', 'Deepin', 'neon',
9599
// rhel-like
96100
'redhat',
97101
// centos
@@ -103,6 +107,16 @@ public static function getSupportedDistros(): array
103107
];
104108
}
105109

110+
/**
111+
* Check if current linux distro is debian-based.
112+
*/
113+
public static function isDebianDist(): bool
114+
{
115+
$dist = static::getOSRelease()['dist'];
116+
$family = explode(' ', static::getOSRelease()['family']);
117+
return in_array($dist, ['debian', 'ubuntu', 'Deepin', 'neon']) || in_array('debian', $family);
118+
}
119+
106120
/**
107121
* Get libc version string from ldd.
108122
*/

0 commit comments

Comments
 (0)