diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php index afc4f20c..5c077073 100644 --- a/src/SPC/builder/linux/LinuxBuilder.php +++ b/src/SPC/builder/linux/LinuxBuilder.php @@ -18,9 +18,6 @@ class LinuxBuilder extends BuilderBase /** Unix compatible builder methods */ use UnixBuilderTrait; - /** @var string Using libc [musl,glibc] */ - public string $libc; - /** @var array Tune cflags */ public array $tune_c_flags; @@ -40,20 +37,19 @@ class LinuxBuilder extends BuilderBase $this->options = $options; // ---------- set necessary options ---------- - // set C Compiler (default: alpine: gcc, others: musl-gcc) - $this->setOptionIfNotExist('cc', match (SystemUtil::getOSRelease()['dist']) { - 'alpine' => 'gcc', - default => 'musl-gcc' - }); - // set C++ Compiler (default: g++) - $this->setOptionIfNotExist('cxx', 'g++'); + // set C/C++ compilers (default: alpine: gcc, others: musl-cross-make) + if (SystemUtil::isMuslDist()) { + $this->setOptionIfNotExist('cc', 'gcc'); + $this->setOptionIfNotExist('cxx', 'g++'); + } else { + $arch = arch2gnu(php_uname('m')); + $this->setOptionIfNotExist('cc', "{$arch}-linux-musl-gcc"); + $this->setOptionIfNotExist('cxx', "{$arch}-linux-musl-g++"); + } // set arch (default: current) $this->setOptionIfNotExist('arch', php_uname('m')); $this->setOptionIfNotExist('gnu-arch', arch2gnu($this->getOption('arch'))); - // ---------- set necessary compile environments ---------- - // set libc - $this->libc = $this->getOption('cc', 'gcc') === 'musl-gcc' ? 'musl_wrapper' : 'musl'; // SystemUtil::selectLibc($this->cc); // concurrency $this->concurrency = SystemUtil::getCpuCount(); // cflags @@ -81,7 +77,7 @@ class LinuxBuilder extends BuilderBase 'CXX' => $this->getOption('cxx'), 'PATH' => BUILD_ROOT_PATH . '/bin:' . getenv('PATH'), ]); - // cross-compile does not support yet + // cross-compiling is not supported yet /*if (php_uname('m') !== $this->arch) { $this->cross_compile_prefix = SystemUtil::getCrossCompilePrefix($this->cc, $this->arch); logger()->info('using cross compile prefix: ' . $this->cross_compile_prefix); @@ -134,7 +130,7 @@ class LinuxBuilder extends BuilderBase } else { $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", array_filter($this->getAllStaticLibFiles()))); } - // add libstdc++, some extensions or libraries need it (C++ cannot be linked statically) + // add libstdc++, some extensions or libraries need it $extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCppExtension() ? '-lstdc++ ' : ''); $this->setOption('extra-libs', $extra_libs); @@ -174,9 +170,11 @@ class LinuxBuilder extends BuilderBase $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; + $arch = arch2gnu(php_uname('m')); shell()->cd(SOURCE_PATH . '/php-src') ->exec( + (!SystemUtil::isMuslDist() ? "LD_LIBRARY_PATH=/usr/local/musl/{$arch}-linux-musl/lib " : '') . './configure ' . '--prefix= ' . '--with-valgrind=no ' . diff --git a/src/SPC/builder/linux/SystemUtil.php b/src/SPC/builder/linux/SystemUtil.php index ddecc6e0..34b9cdef 100644 --- a/src/SPC/builder/linux/SystemUtil.php +++ b/src/SPC/builder/linux/SystemUtil.php @@ -53,6 +53,11 @@ class SystemUtil return $ret; } + public static function isMuslDist(): bool + { + return static::getOSRelease()['dist'] === 'alpine'; + } + public static function getCpuCount(): int { $ncpu = 1; diff --git a/src/SPC/doctor/item/LinuxMuslCheck.php b/src/SPC/doctor/item/LinuxMuslCheck.php index 3e5cb75a..c65326ab 100644 --- a/src/SPC/doctor/item/LinuxMuslCheck.php +++ b/src/SPC/doctor/item/LinuxMuslCheck.php @@ -8,57 +8,122 @@ use SPC\builder\linux\SystemUtil; use SPC\doctor\AsCheckItem; use SPC\doctor\AsFixItem; use SPC\doctor\CheckResult; +use SPC\exception\DownloaderException; +use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; +use SPC\store\Downloader; +use SPC\store\FileSystem; class LinuxMuslCheck { /** @noinspection PhpUnused */ + /** + * @throws WrongUsageException + */ #[AsCheckItem('if musl-libc is installed', limit_os: 'Linux')] - public function checkMusl(): ?CheckResult + public function checkMusl(): CheckResult { - $file = sprintf('/lib/ld-musl-%s.so.1', php_uname('m')); - if (file_exists($file)) { + if (SystemUtil::isMuslDist()) { return CheckResult::ok(); } - - // non-exist, need to recognize distro - $distro = SystemUtil::getOSRelease(); - return match ($distro['dist']) { - 'ubuntu', 'alpine', 'debian', 'rhel', 'almalinux' => CheckResult::fail('musl-libc is not installed on your system', 'fix-musl', [$distro]), - default => CheckResult::fail('musl-libc is not installed on your system'), - }; + $arch = arch2gnu(php_uname('m')); + $cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a"; + $cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc"; + $musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m')); + if (file_exists($musl_wrapper_lib) && file_exists($cross_compile_lib) && file_exists($cross_compile_gcc)) { + return CheckResult::ok(); + } + return CheckResult::fail('musl-libc is not installed on your system', 'fix-musl'); } + /** @noinspection PhpUnused */ /** - * @throws RuntimeException - * @noinspection PhpUnused + * @throws DownloaderException + * @throws FileSystemException + * @throws WrongUsageException */ #[AsFixItem('fix-musl')] - public function fixMusl(array $distro): bool + public function fixMusl(): bool { - $rhel_install = 'wget https://musl.libc.org/releases/musl-1.2.4.tar.gz && tar -zxvf musl-1.2.4.tar.gz && \ - rm -f musl-1.2.4.tar.gz && cd musl-1.2.4 && - if [[ ! "$PATH" =~ (^|:)"/usr/local/musl/bin"(:|$) ]]; then echo "export PATH=/usr/local/musl/bin:$PATH" >> ~/.bash_profile - fi && \ - ./configure --enable-wrapper=gcc && \ - make -j && make install && cd .. && rm -rf musl-1.2.4'; - $install_cmd = match ($distro['dist']) { - 'ubuntu', 'debian' => 'apt-get install musl musl-tools -y', - 'alpine' => 'apk add musl musl-utils musl-dev', - 'rhel' => $rhel_install, - 'almalinux' => $rhel_install, - default => throw new RuntimeException('Current linux distro does not have an auto-install script for musl packages yet.'), - }; - $prefix = ''; - if (get_current_user() !== 'root') { - $prefix = 'sudo '; - logger()->warning('Current user is not root, using sudo for running command'); - } + $arch = arch2gnu(php_uname('m')); + $musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m')); + $cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a"; + $cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc"; + try { - shell(true)->exec($prefix . $install_cmd); + if (!file_exists(DOWNLOAD_PATH)) { + FileSystem::createDir(DOWNLOAD_PATH); + } + if (!file_exists($musl_wrapper_lib)) { + $this->installMuslWrapper(); + } + if (!file_exists($cross_compile_lib) || !file_exists($cross_compile_gcc)) { + $this->installMuslCrossMake(); + } + + $prefix = ''; + if (get_current_user() !== 'root') { + $prefix = 'sudo '; + logger()->warning('Current user is not root, using sudo for running command'); + } + $profile = '/etc/profile'; + if (file_exists('~/.bash_profile')) { + $profile = '~/.bash_profile'; + } elseif (file_exists('~/.profile')) { + $profile = '~/.profile'; + } + if (!file_exists($profile)) { + shell(true)->exec($prefix . 'touch ' . $profile); + } + if (!getenv('PATH') || !str_contains(getenv('PATH'), '/usr/local/musl/bin')) { + $fix_path = 'echo "export PATH=/usr/local/musl/bin:$PATH" >> ' . $profile . ' && export PATH=/usr/local/musl/bin:$PATH'; + shell(true)->exec($prefix . $fix_path); + } return true; } catch (RuntimeException) { return false; } } + + /** + * @throws DownloaderException + * @throws RuntimeException + * @throws FileSystemException + */ + public function installMuslWrapper(): void + { + $prefix = ''; + if (get_current_user() !== 'root') { + $prefix = 'sudo '; + logger()->warning('Current user is not root, using sudo for running command'); + } + $musl_source = [ + 'type' => 'url', + 'url' => 'https://musl.libc.org/releases/musl-1.2.4.tar.gz', + ]; + Downloader::downloadSource('musl-1.2.4', $musl_source); + FileSystem::extractSource('musl-1.2.4', DOWNLOAD_PATH . '/musl-1.2.4.tar.gz'); + $musl_gcc_install_cmd = 'cd ' . SOURCE_PATH . '/musl-1.2.4 && \ + ./configure --enable-wrapper=gcc && \ + make -j && make install'; + shell(true)->exec($prefix . $musl_gcc_install_cmd); + } + + /** + * @throws DownloaderException + * @throws RuntimeException + * @throws FileSystemException + */ + public function installMuslCrossMake(): void + { + $arch = arch2gnu(php_uname('m')); + $musl_compile_source = [ + 'type' => 'url', + 'url' => "https://musl.cc/{$arch}-linux-musl-cross.tgz", + ]; + Downloader::downloadSource('musl-compile', $musl_compile_source); + FileSystem::extractSource('musl-compile', DOWNLOAD_PATH . "/{$arch}-linux-musl-cross.tgz"); + shell(true)->exec('cp -rf ' . SOURCE_PATH . '/musl-compile/* /usr/local/musl'); + } }