Skip to content

Commit dbc6dbe

Browse files
committed
Add Zig package support with downloader and installation checks
1 parent baddd60 commit dbc6dbe

File tree

4 files changed

+220
-1
lines changed

4 files changed

+220
-1
lines changed

config/pkg.target.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@
8282
},
8383
"zig": {
8484
"type": "target",
85-
"artifact": "zig"
85+
"artifact": "zig",
86+
"static-bins": [
87+
"{pkg_root_path}/zig/zig"
88+
]
8689
},
8790
"nasm": {
8891
"type": "target",

src/Package/Artifact/zig.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Package\Artifact;
6+
7+
use StaticPHP\Artifact\ArtifactDownloader;
8+
use StaticPHP\Artifact\Downloader\DownloadResult;
9+
use StaticPHP\Attribute\Artifact\AfterBinaryExtract;
10+
use StaticPHP\Attribute\Artifact\CustomBinary;
11+
use StaticPHP\Exception\DownloaderException;
12+
use StaticPHP\Runtime\SystemTarget;
13+
14+
class zig
15+
{
16+
#[CustomBinary('zig', [
17+
'linux-x86_64',
18+
'linux-aarch64',
19+
'macos-x86_64',
20+
'macos-aarch64',
21+
])]
22+
public function downBinary(ArtifactDownloader $downloader): DownloadResult
23+
{
24+
$index_json = default_shell()->executeCurl('https://ziglang.org/download/index.json', retries: $downloader->getRetry());
25+
$index_json = json_decode($index_json ?: '', true);
26+
$latest_version = null;
27+
foreach ($index_json as $version => $data) {
28+
$latest_version = $version;
29+
break;
30+
}
31+
32+
if (!$latest_version) {
33+
throw new DownloaderException('Could not determine latest Zig version');
34+
}
35+
$zig_arch = SystemTarget::getTargetArch();
36+
$zig_os = match (SystemTarget::getTargetOS()) {
37+
'Windows' => 'win',
38+
'Darwin' => 'macos',
39+
'Linux' => 'linux',
40+
default => throw new DownloaderException('Unsupported OS for Zig: ' . SystemTarget::getTargetOS()),
41+
};
42+
$platform_key = "{$zig_arch}-{$zig_os}";
43+
if (!isset($index_json[$latest_version][$platform_key])) {
44+
throw new DownloaderException("No download available for {$platform_key} in Zig version {$latest_version}");
45+
}
46+
$download_info = $index_json[$latest_version][$platform_key];
47+
$url = $download_info['tarball'];
48+
$sha256 = $download_info['shasum'];
49+
$filename = basename($url);
50+
$path = DOWNLOAD_PATH . DIRECTORY_SEPARATOR . $filename;
51+
default_shell()->executeCurlDownload($url, $path, retries: $downloader->getRetry());
52+
// verify hash
53+
$file_hash = hash_file('sha256', $path);
54+
if ($file_hash !== $sha256) {
55+
throw new DownloaderException("Hash mismatch for downloaded Zig binary. Expected {$sha256}, got {$file_hash}");
56+
}
57+
return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $latest_version], extract: PKG_ROOT_PATH . '/zig', verified: true, version: $latest_version);
58+
}
59+
60+
#[AfterBinaryExtract('zig', [
61+
'linux-x86_64',
62+
'linux-aarch64',
63+
'macos-x86_64',
64+
'macos-aarch64',
65+
])]
66+
public function postExtractZig(string $target_path): void
67+
{
68+
$files = ['zig', 'zig-cc', 'zig-c++', 'zig-ar', 'zig-ld.lld', 'zig-ranlib', 'zig-objcopy'];
69+
$all_exist = true;
70+
foreach ($files as $file) {
71+
if (!file_exists("{$target_path}/{$file}")) {
72+
$all_exist = false;
73+
break;
74+
}
75+
}
76+
if ($all_exist) {
77+
return;
78+
}
79+
80+
$script_path = ROOT_DIR . '/src/globals/scripts/zig-cc.sh';
81+
$script_content = file_get_contents($script_path);
82+
83+
file_put_contents("{$target_path}/zig-cc", $script_content);
84+
chmod("{$target_path}/zig-cc", 0755);
85+
86+
$script_content = str_replace('zig cc', 'zig c++', $script_content);
87+
file_put_contents("{$target_path}/zig-c++", $script_content);
88+
file_put_contents("{$target_path}/zig-ar", "#!/usr/bin/env bash\nexec zig ar $@");
89+
file_put_contents("{$target_path}/zig-ld.lld", "#!/usr/bin/env bash\nexec zig ld.lld $@");
90+
file_put_contents("{$target_path}/zig-ranlib", "#!/usr/bin/env bash\nexec zig ranlib $@");
91+
file_put_contents("{$target_path}/zig-objcopy", "#!/usr/bin/env bash\nexec zig objcopy $@");
92+
chmod("{$target_path}/zig-c++", 0755);
93+
chmod("{$target_path}/zig-ar", 0755);
94+
chmod("{$target_path}/zig-ld.lld", 0755);
95+
chmod("{$target_path}/zig-ranlib", 0755);
96+
chmod("{$target_path}/zig-objcopy", 0755);
97+
}
98+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace StaticPHP\Doctor\Item;
6+
7+
use StaticPHP\Attribute\Doctor\CheckItem;
8+
use StaticPHP\Attribute\Doctor\FixItem;
9+
use StaticPHP\Attribute\Doctor\OptionalCheck;
10+
use StaticPHP\DI\ApplicationContext;
11+
use StaticPHP\Doctor\CheckResult;
12+
use StaticPHP\Package\PackageInstaller;
13+
use StaticPHP\Toolchain\Interface\ToolchainInterface;
14+
use StaticPHP\Toolchain\ZigToolchain;
15+
16+
#[OptionalCheck([self::class, 'optionalCheck'])]
17+
class ZigCheck
18+
{
19+
public static function optionalCheck(): bool
20+
{
21+
return ApplicationContext::get(ToolchainInterface::class) instanceof ZigToolchain;
22+
}
23+
24+
/** @noinspection PhpUnused */
25+
#[CheckItem('if zig is installed', level: 800)]
26+
public function checkZig(): CheckResult
27+
{
28+
$installer = new PackageInstaller();
29+
$package = 'zig';
30+
$installer->addInstallPackage($package);
31+
$installed = $installer->isPackageInstalled($package);
32+
if ($installed) {
33+
return CheckResult::ok();
34+
}
35+
return CheckResult::fail('zig is not installed', 'install-zig');
36+
}
37+
38+
#[FixItem('install-zig')]
39+
public function installZig(): bool
40+
{
41+
$arch = arch2gnu(php_uname('m'));
42+
$os = match (PHP_OS_FAMILY) {
43+
'Windows' => 'win',
44+
'Darwin' => 'macos',
45+
'BSD' => 'freebsd',
46+
default => 'linux',
47+
};
48+
$installer = new PackageInstaller();
49+
$installer->addInstallPackage('zig');
50+
$installer->run(false);
51+
return $installer->isPackageInstalled('zig');
52+
}
53+
}

src/globals/scripts/zig-cc.sh

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env bash
2+
3+
if [ "$BUILD_ROOT_PATH" = "" ]; then
4+
echo "The script must be run in the SPC build environment."
5+
exit 1
6+
fi
7+
8+
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
9+
BUILDROOT_ABS=$BUILD_ROOT_PATH
10+
PARSED_ARGS=()
11+
12+
while [[ $# -gt 0 ]]; do
13+
case "$1" in
14+
-isystem)
15+
shift
16+
ARG="$1"
17+
shift
18+
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
19+
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem" "$ARG")
20+
;;
21+
-isystem*)
22+
ARG="${1#-isystem}"
23+
shift
24+
ARG_ABS="$(realpath "$ARG" 2>/dev/null || true)"
25+
[[ "$ARG_ABS" == "$BUILDROOT_ABS" ]] && PARSED_ARGS+=("-I$ARG") || PARSED_ARGS+=("-isystem$ARG")
26+
;;
27+
-march=*|-mcpu=*)
28+
OPT_NAME="${1%%=*}"
29+
OPT_VALUE="${1#*=}"
30+
# Skip armv8- flags entirely as Zig doesn't support them
31+
if [[ "$OPT_VALUE" == armv8-* ]]; then
32+
shift
33+
continue
34+
fi
35+
# replace -march=x86-64 with -march=x86_64
36+
OPT_VALUE="${OPT_VALUE//-/_}"
37+
PARSED_ARGS+=("${OPT_NAME}=${OPT_VALUE}")
38+
shift
39+
;;
40+
*)
41+
PARSED_ARGS+=("$1")
42+
shift
43+
;;
44+
esac
45+
done
46+
47+
[[ -n "$SPC_TARGET" ]] && TARGET="-target $SPC_TARGET" || TARGET=""
48+
49+
if [[ "$SPC_TARGET" =~ \.[0-9]+\.[0-9]+ ]]; then
50+
output=$(zig cc $TARGET $SPC_COMPILER_EXTRA "${PARSED_ARGS[@]}" 2>&1)
51+
status=$?
52+
53+
if [[ $status -eq 0 ]]; then
54+
echo "$output"
55+
exit 0
56+
fi
57+
58+
if echo "$output" | grep -qE "version '.*' in target triple"; then
59+
filtered_output=$(echo "$output" | grep -vE "version '.*' in target triple")
60+
echo "$filtered_output"
61+
exit 0
62+
fi
63+
fi
64+
65+
exec zig cc $TARGET $SPC_COMPILER_EXTRA "${PARSED_ARGS[@]}"

0 commit comments

Comments
 (0)