diff --git a/src/StaticPHP/Package/LibraryPackage.php b/src/StaticPHP/Package/LibraryPackage.php index 42f60d79..cd8c626e 100644 --- a/src/StaticPHP/Package/LibraryPackage.php +++ b/src/StaticPHP/Package/LibraryPackage.php @@ -113,60 +113,6 @@ class LibraryPackage extends Package } } - /** - * Get extra CFLAGS for current package. - * You need to define the environment variable in the format of {LIBRARY_NAME}_CFLAGS - * where {LIBRARY_NAME} is the snake_case name of the library. - * For example, for libjpeg, the environment variable should be libjpeg_CFLAGS. - */ - public function getLibExtraCFlags(): string - { - // get environment variable - $env = getenv($this->getSnakeCaseName() . '_CFLAGS') ?: ''; - // get default c flags - $arch_c_flags = getenv('SPC_DEFAULT_CFLAGS') ?: ''; - if (!empty(getenv('SPC_DEFAULT_CFLAGS')) && !str_contains($env, $arch_c_flags)) { - $env .= ' ' . $arch_c_flags; - } - return trim($env); - } - - /** - * Get extra CXXFLAGS for current package. - * You need to define the environment variable in the format of {LIBRARY_NAME}_CXXFLAGS - * where {LIBRARY_NAME} is the snake_case name of the library. - * For example, for libjpeg, the environment variable should be libjpeg_CXXFLAGS. - */ - public function getLibExtraCxxFlags(): string - { - // get environment variable - $env = getenv($this->getSnakeCaseName() . '_CXXFLAGS') ?: ''; - // get default cxx flags - $arch_cxx_flags = getenv('SPC_DEFAULT_CXXFLAGS') ?: ''; - if (!empty(getenv('SPC_DEFAULT_CXXFLAGS')) && !str_contains($env, $arch_cxx_flags)) { - $env .= ' ' . $arch_cxx_flags; - } - return trim($env); - } - - /** - * Get extra LDFLAGS for current package. - * You need to define the environment variable in the format of {LIBRARY_NAME}_LDFLAGS - * where {LIBRARY_NAME} is the snake_case name of the library. - * For example, for libjpeg, the environment variable should be libjpeg_LDFLAGS. - */ - public function getLibExtraLdFlags(): string - { - // get environment variable - $env = getenv($this->getSnakeCaseName() . '_LDFLAGS') ?: ''; - // get default ld flags - $arch_ld_flags = getenv('SPC_DEFAULT_LDFLAGS') ?: ''; - if (!empty(getenv('SPC_DEFAULT_LDFLAGS')) && !str_contains($env, $arch_ld_flags)) { - $env .= ' ' . $arch_ld_flags; - } - return trim($env); - } - /** * Patch pkgconfig file prefix, exec_prefix, libdir, includedir for correct build. * @@ -364,17 +310,6 @@ class LibraryPackage extends Package return $res['libs']; } - /** - * Get extra LIBS for current package. - * You need to define the environment variable in the format of {LIBRARY_NAME}_LIBS - * where {LIBRARY_NAME} is the snake_case name of the library. - * For example, for libjpeg, the environment variable should be libjpeg_LIBS. - */ - public function getLibExtraLibs(): string - { - return getenv($this->getSnakeCaseName() . '_LIBS') ?: ''; - } - /** * Get tar compress options from suffix * diff --git a/src/StaticPHP/Package/Package.php b/src/StaticPHP/Package/Package.php index b3f8ccb6..ba53f81d 100644 --- a/src/StaticPHP/Package/Package.php +++ b/src/StaticPHP/Package/Package.php @@ -284,6 +284,71 @@ abstract class Package return $this->getArtifact()?->hasPlatformBinary() ?? false; } + /** + * Get extra CFLAGS for current package. + * You need to define the environment variable in the format of {PACKAGE_NAME}_CFLAGS + * where {PACKAGE_NAME} is the snake_case name of the package. + * For example, for libjpeg, the environment variable should be libjpeg_CFLAGS. + */ + public function getLibExtraCFlags(): string + { + // get environment variable + $env = getenv($this->getSnakeCaseName() . '_CFLAGS') ?: ''; + // get default c flags + $arch_c_flags = getenv('SPC_DEFAULT_CFLAGS') ?: ''; + if (!empty(getenv('SPC_DEFAULT_CFLAGS')) && !str_contains($env, $arch_c_flags)) { + $env .= ' ' . $arch_c_flags; + } + return trim($env); + } + + /** + * Get extra CXXFLAGS for current package. + * You need to define the environment variable in the format of {PACKAGE_NAME}_CXXFLAGS + * where {PACKAGE_NAME} is the snake_case name of the package. + * For example, for libjpeg, the environment variable should be libjpeg_CXXFLAGS. + */ + public function getLibExtraCxxFlags(): string + { + // get environment variable + $env = getenv($this->getSnakeCaseName() . '_CXXFLAGS') ?: ''; + // get default cxx flags + $arch_cxx_flags = getenv('SPC_DEFAULT_CXXFLAGS') ?: ''; + if (!empty(getenv('SPC_DEFAULT_CXXFLAGS')) && !str_contains($env, $arch_cxx_flags)) { + $env .= ' ' . $arch_cxx_flags; + } + return trim($env); + } + + /** + * Get extra LDFLAGS for current package. + * You need to define the environment variable in the format of {PACKAGE_NAME}_LDFLAGS + * where {PACKAGE_NAME} is the snake_case name of the package. + * For example, for libjpeg, the environment variable should be libjpeg_LDFLAGS. + */ + public function getLibExtraLdFlags(): string + { + // get environment variable + $env = getenv($this->getSnakeCaseName() . '_LDFLAGS') ?: ''; + // get default ld flags + $arch_ld_flags = getenv('SPC_DEFAULT_LDFLAGS') ?: ''; + if (!empty(getenv('SPC_DEFAULT_LDFLAGS')) && !str_contains($env, $arch_ld_flags)) { + $env .= ' ' . $arch_ld_flags; + } + return trim($env); + } + + /** + * Get extra LIBS for current package. + * You need to define the environment variable in the format of {PACKAGE_NAME}_LIBS + * where {PACKAGE_NAME} is the snake_case name of the package. + * For example, for libjpeg, the environment variable should be libjpeg_LIBS. + */ + public function getLibExtraLibs(): string + { + return getenv($this->getSnakeCaseName() . '_LIBS') ?: ''; + } + /** * Get the snake_case name of the package. */ diff --git a/src/StaticPHP/Package/PackageBuilder.php b/src/StaticPHP/Package/PackageBuilder.php index 582fa474..943b3ce5 100644 --- a/src/StaticPHP/Package/PackageBuilder.php +++ b/src/StaticPHP/Package/PackageBuilder.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace StaticPHP\Package; +use StaticPHP\Artifact\ArtifactCache; use StaticPHP\Config\PackageConfig; use StaticPHP\DI\ApplicationContext; use StaticPHP\Exception\SPCException; @@ -39,8 +40,8 @@ class PackageBuilder public function buildPackage(Package $package, bool $force = false): int { // init build dirs - if (!$package instanceof LibraryPackage) { - throw new SPCInternalException('Please, never try to build non-library packages directly.'); + if (!$package instanceof LibraryPackage && !$package instanceof ToolPackage) { + throw new SPCInternalException('Please, never try to build non-library, non-tool packages directly.'); } FileSystem::createDir($package->getBuildRootPath()); FileSystem::createDir($package->getIncludeDir()); @@ -78,6 +79,14 @@ class PackageBuilder $this->installLicense($package, $license); } } + + // Record the installed version for tool packages built from source, so + // `check-update --installed` reflects what's actually on disk (mirrors the + // binary-install recording in PackageInstaller::installBinary()). + if ($package instanceof ToolPackage) { + $source_info = ApplicationContext::get(ArtifactCache::class)->getSourceInfo($package->getName()); + ToolVersionRegistry::record($package->getName(), $source_info['version'] ?? null); + } } catch (SPCException $e) { // Ensure package information is bound if not already if ($e->getPackageInfo() === null) { diff --git a/src/StaticPHP/Package/PackageInstaller.php b/src/StaticPHP/Package/PackageInstaller.php index a5481583..cf93368d 100644 --- a/src/StaticPHP/Package/PackageInstaller.php +++ b/src/StaticPHP/Package/PackageInstaller.php @@ -168,9 +168,6 @@ class PackageInstaller // Early validation: check if packages can be built or installed before downloading $this->validatePackagesBeforeBuild(); - // Check that all required tools are installed before proceeding - $this->ensureRequiredTools(); - // check download if ($this->download) { $downloaderOptions = DownloaderOptions::extractFromConsoleOptions($this->options, 'dl'); @@ -178,7 +175,7 @@ class PackageInstaller // These must always download binary (not source), regardless of global prefer-source setting. $binary_only_packages = array_filter( $this->packages, - fn ($p) => $p instanceof LibraryPackage + fn ($p) => ($p instanceof LibraryPackage || $p instanceof ToolPackage) && !$this->isBuildPackage($p) && !$p->hasStage('build') && ($p->getArtifact()?->hasPlatformBinary() ?? false) @@ -216,8 +213,8 @@ class PackageInstaller $builder = ApplicationContext::get(PackageBuilder::class); foreach ($this->packages as $package) { $is_to_build = $this->isBuildPackage($package); - $has_build_stage = $package instanceof LibraryPackage && $package->hasStage('build'); - $should_use_binary = $package instanceof LibraryPackage && ($package->getArtifact()?->shouldUseBinary() ?? false); + $has_build_stage = ($package instanceof LibraryPackage || $package instanceof ToolPackage) && $package->hasStage('build'); + $should_use_binary = ($package instanceof LibraryPackage || $package instanceof ToolPackage) && ($package->getArtifact()?->shouldUseBinary() ?? false); $has_source = $package->hasSource(); if (!$is_to_build && $should_use_binary) { // install binary @@ -280,6 +277,11 @@ class PackageInstaller // perform after-install actions and emit post-install events $this->emitPostInstallEvents(); + + // Final verification: declared build-time tools should have been auto-installed by the + // pipeline above (they were merged into $this->packages in resolvePackages()). This only + // throws if a tool genuinely failed to become available (e.g. no artifact for this platform). + $this->ensureRequiredTools(); } public function isBuildPackage(Package|string $package): bool @@ -357,12 +359,16 @@ class PackageInstaller } // Fallback: if the download cache is missing (e.g. download failed or cache was cleared), // still check whether the files are physically present in buildroot. - // Note: TargetPackage extends LibraryPackage, but target packages (e.g. zig) have no + // Note: TargetPackage extends LibraryPackage, but target packages have no // static-libs/headers configured, so isInstalled() would trivially return true for them. // Only apply this fallback to pure library packages. if ($package instanceof LibraryPackage && !($package instanceof TargetPackage)) { return $package->isInstalled(); } + // Tool packages: fall back to checking their provided binaries directly on disk. + if ($package instanceof ToolPackage) { + return $package->isInstalled(); + } return false; } @@ -511,6 +517,16 @@ class PackageInstaller } $status = $extractor->extract($artifact); + + // Record the installed version for tool packages, so `check-update --installed` can + // compare against what's actually on disk instead of only the download cache (which + // only reflects the last download and may be stale or cleared). Recorded regardless of + // $status so the registry self-heals if it was deleted separately from the install dir. + if ($package instanceof ToolPackage) { + $cache_info = ApplicationContext::get(ArtifactCache::class)->getBinaryInfo($artifact->getName(), SystemTarget::getCurrentPlatformString()); + ToolVersionRegistry::record($artifact->getName(), $cache_info['version'] ?? null); + } + if ($status === SPC_STATUS_ALREADY_EXTRACTED) { return SPC_STATUS_ALREADY_INSTALLED; } @@ -661,8 +677,8 @@ class PackageInstaller */ private function validatePackageArtifact(Package $package): void { - // target and library must have at least source or platform binary - if (in_array($package->getType(), ['library', 'target']) && !$package->getArtifact()?->hasSource() && !$package->getArtifact()?->hasPlatformBinary()) { + // target, library and tool packages must have at least source or platform binary + if (in_array($package->getType(), ['library', 'target', 'tool']) && !$package->getArtifact()?->hasSource() && !$package->getArtifact()?->hasPlatformBinary()) { throw new WrongUsageException("Validation failed: Target package '{$package->getName()}' has no source or current platform (" . SystemTarget::getCurrentPlatformString() . ') binary defined.'); } } @@ -695,6 +711,32 @@ class PackageInstaller $this->packages[$pkg_name] = PackageLoader::getPackage($pkg_name); } + // Merge in build-time tool packages declared via 'tools'/'tools@platform' fields on the + // packages resolved above. Tools are intentionally NOT part of the library dependency graph + // (DependencyResolver), but still need to ride the normal download/extract/install pipeline + // so they get auto-installed like any other package. + // + // Tools are prepended so they install BEFORE any package that declares them — otherwise a + // library that invokes e.g. jom.exe during its build stage would fail because the tool + // hasn't been extracted yet (tools were appended after the dependency graph in insertion + // order, which is also the build-loop iteration order). + $tool_packages = []; + foreach ($this->collectRequiredTools() as $tool_name) { + if (isset($this->packages[$tool_name])) { + continue; + } + try { + $tool = PackageLoader::getPackage($tool_name); + } catch (WrongUsageException) { + continue; // will be reported as missing by ensureRequiredTools() + } + if ($tool instanceof ToolPackage) { + $tool_packages[$tool_name] = $tool; + } + } + // Prepend: tools first, then the dependency-resolved packages. + $this->packages = [...$tool_packages, ...$this->packages]; + foreach ($this->packages as $package) { $this->injectPackageEnvs($package); } @@ -703,8 +745,12 @@ class PackageInstaller /** * Ensure all required tools are installed, throwing if any are missing. * - * Called early in the build pipeline (before download/extract). - * When tools are missing, lists them with install hints. + * Called at the end of run(), after the normal download/extract/build/install pipeline + * has had a chance to auto-install any tool package merged in resolvePackages(). This is + * a final safety net: it only throws if a declared tool still isn't available afterward + * (e.g. no artifact defined for the current platform, or the tool name doesn't resolve to + * a registered ToolPackage). "General" tools not declared via any package's 'tools' field + * (e.g. zig, musl-toolchain) are not covered here and remain purely Doctor-driven. */ private function ensureRequiredTools(): void { diff --git a/src/StaticPHP/Package/ToolPackage.php b/src/StaticPHP/Package/ToolPackage.php index b78afa91..7e0e6ba3 100644 --- a/src/StaticPHP/Package/ToolPackage.php +++ b/src/StaticPHP/Package/ToolPackage.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace StaticPHP\Package; use StaticPHP\Config\PackageConfig; +use StaticPHP\Runtime\SystemTarget; use StaticPHP\Util\FileSystem; -use StaticPHP\Util\GlobalPathTrait; /** * Represents a build-time tool package. @@ -26,6 +26,11 @@ use StaticPHP\Util\GlobalPathTrait; * provides: [nasm.exe, ndisasm.exe] # executables this tool installs * binary-subdir: '' # subdirectory under install root (default: '') * min-version: '2.16' # minimum required version (optional) + * + * Fields nested under 'tool' support the same '@windows'/'@unix'/'@macos'/'@linux' suffix + * overrides as top-level package fields (e.g. 'provides@windows' overrides 'provides' when + * building on Windows), useful when a tool provides differently-named binaries per OS + * (e.g. upx vs upx.exe). * artifact: * binary: * windows-x86_64: @@ -36,7 +41,44 @@ use StaticPHP\Util\GlobalPathTrait; */ class ToolPackage extends Package { - use GlobalPathTrait; + /** + * Get the build root ('--prefix') for a tool that builds from source. + * + * Unlike LibraryPackage (which installs into BUILD_ROOT_PATH), tool packages that build + * from source (e.g. pkg-config, via UnixAutoconfExecutor/UnixCMakeExecutor) install into + * their own install root (PKG_ROOT_PATH by default), consistent with pre-built tool binaries. + */ + public function getBuildRootPath(): string + { + return $this->getInstallRoot(); + } + + /** + * Tool packages don't produce headers for other packages to consume. Kept self-contained + * under the tool's own install root so a from-source build never accidentally picks up + * unrelated headers from BUILD_ROOT_PATH. + */ + public function getIncludeDir(): string + { + return $this->getInstallRoot() . DIRECTORY_SEPARATOR . 'include'; + } + + /** + * Tool packages don't produce libraries for other packages to consume. Kept self-contained + * under the tool's own install root (see getIncludeDir()). + */ + public function getLibDir(): string + { + return $this->getInstallRoot() . DIRECTORY_SEPARATOR . 'lib'; + } + + /** + * Where this tool's own executables live, i.e. {install-root}/{binary-subdir}. + */ + public function getBinDir(): string + { + return $this->getBinaryDir(); + } /** * Get the install root directory for this tool. @@ -50,7 +92,7 @@ class ToolPackage extends Package if ($root = getenv($env_var)) { return $root; } - $config_root = $this->getToolConfig()['install-root'] ?? null; + $config_root = $this->getToolField('install-root'); if ($config_root !== null) { return FileSystem::replacePathVariable((string) $config_root); } @@ -65,7 +107,7 @@ class ToolPackage extends Package */ public function getBinaryDir(): string { - $subdir = $this->getToolConfig()['binary-subdir'] ?? ''; + $subdir = $this->getToolField('binary-subdir') ?? ''; if ($subdir === '') { return $this->getInstallRoot(); } @@ -75,14 +117,15 @@ class ToolPackage extends Package /** * Get the list of executables this tool provides. * - * Reads from YAML 'tool.provides' field. Each entry is a bare filename - * (e.g. 'nasm.exe'), resolved relative to getBinaryDir(). + * Reads from YAML 'tool.provides' field (with '@windows'/'@unix'/'@macos'/'@linux' + * suffix override support). Each entry is a bare filename (e.g. 'nasm.exe'), resolved + * relative to getBinaryDir(). * * @return string[] Bare executable names (not full paths) */ public function getProvides(): array { - return $this->getToolConfig()['provides'] ?? []; + return $this->getToolField('provides') ?? []; } /** @@ -112,6 +155,20 @@ class ToolPackage extends Package return array_all($this->getProvides(), fn ($binary) => file_exists($this->getBinary($binary))); } + /** + * Get the version currently installed on disk, as recorded by ToolVersionRegistry when this + * tool's binary was last (re-)installed via PackageInstaller::installBinary(). + * + * Returns null if the tool was never installed through the package installer (e.g. installed + * manually, or not installed at all), or if its artifact doesn't expose a version string. + * This reflects what's actually on disk, unlike the download cache which only reflects the + * last download and may be stale or cleared. + */ + public function getInstalledVersion(): ?string + { + return ToolVersionRegistry::get($this->name); + } + /** * Get the minimum required version for this tool, if specified. * @@ -119,7 +176,7 @@ class ToolPackage extends Package */ public function getMinVersion(): ?string { - $version = $this->getToolConfig()['min-version'] ?? null; + $version = $this->getToolField('min-version'); return $version !== null ? (string) $version : null; } @@ -148,4 +205,29 @@ class ToolPackage extends Package } return $config['tool']; } + + /** + * Get a field from the nested 'tool' config block, honoring the same + * '@windows'/'@unix'/'@macos'/'@linux'/'@bsd'/'@freebsd' suffix override priority + * that PackageConfig::get() applies to top-level fields. This lets a tool declare a + * per-OS override, e.g. 'provides' + 'provides@windows', without needing platform- + * specific package config files. + */ + private function getToolField(string $field): mixed + { + $tool = $this->getToolConfig(); + $suffixes = match (SystemTarget::getTargetOS()) { + 'Windows' => ['@windows', ''], + 'Darwin' => ['@macos', '@unix', ''], + 'Linux' => ['@linux', '@unix', ''], + 'BSD' => ['@freebsd', '@bsd', '@unix', ''], + }; + foreach ($suffixes as $suffix) { + $key = "{$field}{$suffix}"; + if (isset($tool[$key])) { + return $tool[$key]; + } + } + return null; + } } diff --git a/src/StaticPHP/Runtime/Executor/Executor.php b/src/StaticPHP/Runtime/Executor/Executor.php index a57e11e0..804b680f 100644 --- a/src/StaticPHP/Runtime/Executor/Executor.php +++ b/src/StaticPHP/Runtime/Executor/Executor.php @@ -5,12 +5,13 @@ declare(strict_types=1); namespace StaticPHP\Runtime\Executor; use StaticPHP\Package\LibraryPackage; +use StaticPHP\Package\ToolPackage; abstract class Executor { - public function __construct(protected LibraryPackage $package) {} + public function __construct(protected LibraryPackage|ToolPackage $package) {} - public static function create(LibraryPackage $package): static + public static function create(LibraryPackage|ToolPackage $package): static { return new static($package); } diff --git a/src/StaticPHP/Runtime/Executor/UnixAutoconfExecutor.php b/src/StaticPHP/Runtime/Executor/UnixAutoconfExecutor.php index 2cce15c5..84d3ca07 100644 --- a/src/StaticPHP/Runtime/Executor/UnixAutoconfExecutor.php +++ b/src/StaticPHP/Runtime/Executor/UnixAutoconfExecutor.php @@ -10,6 +10,7 @@ use StaticPHP\Exception\SPCInternalException; use StaticPHP\Package\LibraryPackage; use StaticPHP\Package\PackageBuilder; use StaticPHP\Package\PackageInstaller; +use StaticPHP\Package\ToolPackage; use StaticPHP\Runtime\Shell\UnixShell; use StaticPHP\Util\InteractiveTerm; use ZM\Logger\ConsoleColor; @@ -22,7 +23,7 @@ class UnixAutoconfExecutor extends Executor protected PackageInstaller $installer; - public function __construct(protected LibraryPackage $package, ?PackageInstaller $installer = null) + public function __construct(protected LibraryPackage|ToolPackage $package, ?PackageInstaller $installer = null) { parent::__construct($package); if ($installer !== null) { diff --git a/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php b/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php index d8ff5148..6d5e6343 100644 --- a/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php +++ b/src/StaticPHP/Runtime/Executor/UnixCMakeExecutor.php @@ -11,6 +11,7 @@ use StaticPHP\Package\LibraryPackage; use StaticPHP\Package\PackageBuilder; use StaticPHP\Package\PackageInstaller; use StaticPHP\Package\TargetPackage; +use StaticPHP\Package\ToolPackage; use StaticPHP\Runtime\Shell\UnixShell; use StaticPHP\Runtime\SystemTarget; use StaticPHP\Util\FileSystem; @@ -40,7 +41,7 @@ class UnixCMakeExecutor extends Executor protected PackageInstaller $installer; - public function __construct(protected LibraryPackage $package, ?PackageInstaller $installer = null) + public function __construct(protected LibraryPackage|ToolPackage $package, ?PackageInstaller $installer = null) { parent::__construct($package); if ($installer !== null) { diff --git a/src/StaticPHP/Runtime/Executor/WindowsCMakeExecutor.php b/src/StaticPHP/Runtime/Executor/WindowsCMakeExecutor.php index f79afb41..6ad51272 100644 --- a/src/StaticPHP/Runtime/Executor/WindowsCMakeExecutor.php +++ b/src/StaticPHP/Runtime/Executor/WindowsCMakeExecutor.php @@ -9,6 +9,7 @@ use StaticPHP\Exception\SPCInternalException; use StaticPHP\Package\LibraryPackage; use StaticPHP\Package\PackageBuilder; use StaticPHP\Package\PackageInstaller; +use StaticPHP\Package\ToolPackage; use StaticPHP\Runtime\Shell\WindowsCmd; use StaticPHP\Util\FileSystem; use StaticPHP\Util\InteractiveTerm; @@ -35,7 +36,7 @@ class WindowsCMakeExecutor extends Executor protected PackageInstaller $installer; - public function __construct(protected LibraryPackage $package) + public function __construct(protected LibraryPackage|ToolPackage $package) { parent::__construct($this->package); $this->builder = ApplicationContext::get(PackageBuilder::class); diff --git a/src/StaticPHP/Runtime/Shell/UnixShell.php b/src/StaticPHP/Runtime/Shell/UnixShell.php index 7d74f65f..8ecad042 100644 --- a/src/StaticPHP/Runtime/Shell/UnixShell.php +++ b/src/StaticPHP/Runtime/Shell/UnixShell.php @@ -6,6 +6,7 @@ namespace StaticPHP\Runtime\Shell; use StaticPHP\Exception\SPCInternalException; use StaticPHP\Package\LibraryPackage; +use StaticPHP\Package\ToolPackage; use StaticPHP\Runtime\SystemTarget; use ZM\Logger\ConsoleColor; @@ -40,9 +41,9 @@ class UnixShell extends Shell /** * Init the environment variable that common build will be used. * - * @param LibraryPackage $library Library package + * @param LibraryPackage|ToolPackage $library Library or tool package */ - public function initializeEnv(LibraryPackage $library): UnixShell + public function initializeEnv(LibraryPackage|ToolPackage $library): UnixShell { $this->setEnv([ 'CFLAGS' => $library->getLibExtraCFlags(),