|
| 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\Doctor\CheckResult; |
| 10 | +use StaticPHP\Exception\EnvironmentException; |
| 11 | +use StaticPHP\Util\System\LinuxUtil; |
| 12 | + |
| 13 | +class LinuxToolCheck |
| 14 | +{ |
| 15 | + public const TOOLS_ALPINE = [ |
| 16 | + 'make', 'bison', 're2c', 'flex', |
| 17 | + 'git', 'autoconf', 'automake', 'gettext-dev', |
| 18 | + 'tar', 'unzip', 'gzip', |
| 19 | + 'bzip2', 'cmake', 'gcc', |
| 20 | + 'g++', 'patch', 'binutils-gold', |
| 21 | + 'libtoolize', 'which', |
| 22 | + 'patchelf', |
| 23 | + ]; |
| 24 | + |
| 25 | + public const TOOLS_DEBIAN = [ |
| 26 | + 'make', 'bison', 're2c', 'flex', |
| 27 | + 'git', 'autoconf', 'automake', 'autopoint', |
| 28 | + 'tar', 'unzip', 'gzip', 'gcc', 'g++', |
| 29 | + 'bzip2', 'cmake', 'patch', |
| 30 | + 'xz', 'libtoolize', 'which', |
| 31 | + 'patchelf', |
| 32 | + ]; |
| 33 | + |
| 34 | + public const TOOLS_RHEL = [ |
| 35 | + 'perl', 'make', 'bison', 're2c', 'flex', |
| 36 | + 'git', 'autoconf', 'automake', |
| 37 | + 'tar', 'unzip', 'gzip', 'gcc', 'g++', |
| 38 | + 'bzip2', 'cmake', 'patch', 'which', |
| 39 | + 'xz', 'libtool', 'gettext-devel', |
| 40 | + 'patchelf', 'file', |
| 41 | + ]; |
| 42 | + |
| 43 | + public const TOOLS_ARCH = [ |
| 44 | + 'base-devel', 'cmake', |
| 45 | + ]; |
| 46 | + |
| 47 | + private const PROVIDED_COMMAND = [ |
| 48 | + 'perl' => '/usr/share/perl5/FindBin.pm', |
| 49 | + 'binutils-gold' => 'ld.gold', |
| 50 | + 'base-devel' => 'automake', |
| 51 | + 'gettext-devel' => 'gettextize', |
| 52 | + 'gettext-dev' => 'gettextize', |
| 53 | + 'perl-IPC-Cmd' => '/usr/share/perl5/vendor_perl/IPC/Cmd.pm', |
| 54 | + 'perl-Time-Piece' => '/usr/lib64/perl5/Time/Piece.pm', |
| 55 | + ]; |
| 56 | + |
| 57 | + /** @noinspection PhpUnused */ |
| 58 | + #[CheckItem('if necessary tools are installed', limit_os: 'Linux', level: 999)] |
| 59 | + public function checkCliTools(): ?CheckResult |
| 60 | + { |
| 61 | + $distro = LinuxUtil::getOSRelease(); |
| 62 | + |
| 63 | + $required = match ($distro['dist']) { |
| 64 | + 'alpine' => self::TOOLS_ALPINE, |
| 65 | + 'redhat' => self::TOOLS_RHEL, |
| 66 | + 'centos' => array_merge(self::TOOLS_RHEL, ['perl-IPC-Cmd', 'perl-Time-Piece']), |
| 67 | + 'arch' => self::TOOLS_ARCH, |
| 68 | + default => self::TOOLS_DEBIAN, |
| 69 | + }; |
| 70 | + $missing = []; |
| 71 | + foreach ($required as $package) { |
| 72 | + if (LinuxUtil::findCommand(self::PROVIDED_COMMAND[$package] ?? $package) === null) { |
| 73 | + $missing[] = $package; |
| 74 | + } |
| 75 | + } |
| 76 | + if (!empty($missing)) { |
| 77 | + return CheckResult::fail(implode(', ', $missing) . ' not installed on your system', 'install-linux-tools', [$distro, $missing]); |
| 78 | + } |
| 79 | + return CheckResult::ok(); |
| 80 | + } |
| 81 | + |
| 82 | + #[CheckItem('if cmake version >= 3.22', limit_os: 'Linux')] |
| 83 | + public function checkCMakeVersion(): ?CheckResult |
| 84 | + { |
| 85 | + $ver = get_cmake_version(); |
| 86 | + if ($ver === null) { |
| 87 | + return CheckResult::fail('Failed to get cmake version'); |
| 88 | + } |
| 89 | + if (version_compare($ver, '3.22.0') < 0) { |
| 90 | + return CheckResult::fail('cmake version is too low (' . $ver . '), please update it manually!'); |
| 91 | + } |
| 92 | + return CheckResult::ok($ver); |
| 93 | + } |
| 94 | + |
| 95 | + /** @noinspection PhpUnused */ |
| 96 | + #[CheckItem('if necessary linux headers are installed', limit_os: 'Linux')] |
| 97 | + public function checkSystemOSPackages(): ?CheckResult |
| 98 | + { |
| 99 | + if (LinuxUtil::isMuslDist()) { |
| 100 | + // check linux-headers installation |
| 101 | + if (!file_exists('/usr/include/linux/mman.h')) { |
| 102 | + return CheckResult::fail('linux-headers not installed on your system', 'install-linux-tools', [LinuxUtil::getOSRelease(), ['linux-headers']]); |
| 103 | + } |
| 104 | + } |
| 105 | + return CheckResult::ok(); |
| 106 | + } |
| 107 | + |
| 108 | + #[FixItem('install-linux-tools')] |
| 109 | + public function fixBuildTools(array $distro, array $missing): bool |
| 110 | + { |
| 111 | + $install_cmd = match ($distro['dist']) { |
| 112 | + 'ubuntu', 'debian', 'Deepin', 'neon' => 'apt-get install -y', |
| 113 | + 'alpine' => 'apk add', |
| 114 | + 'redhat' => 'dnf install -y', |
| 115 | + 'centos' => 'yum install -y', |
| 116 | + 'arch' => 'pacman -S --noconfirm', |
| 117 | + default => null, |
| 118 | + }; |
| 119 | + if ($install_cmd === null) { |
| 120 | + // try family |
| 121 | + $family = explode(' ', strtolower($distro['family'])); |
| 122 | + if (in_array('debian', $family)) { |
| 123 | + $install_cmd = 'apt-get install -y'; |
| 124 | + } elseif (in_array('rhel', $family) || in_array('fedora', $family)) { |
| 125 | + $install_cmd = 'dnf install -y'; |
| 126 | + } else { |
| 127 | + throw new EnvironmentException( |
| 128 | + "Current linux distro [{$distro['dist']}] does not have an auto-install script for packages yet.", |
| 129 | + 'You can submit an issue to request support: https://github.com/crazywhalecc/static-php-cli/issues' |
| 130 | + ); |
| 131 | + } |
| 132 | + } |
| 133 | + $prefix = ''; |
| 134 | + if (($user = exec('whoami')) !== 'root') { |
| 135 | + $prefix = 'sudo '; |
| 136 | + logger()->warning("Current user ({$user}) is not root, using sudo for running command (may require password input)"); |
| 137 | + } |
| 138 | + |
| 139 | + $is_debian = LinuxUtil::isDebianDist(); |
| 140 | + $to_install = $is_debian ? str_replace('xz', 'xz-utils', $missing) : $missing; |
| 141 | + // debian, alpine libtool -> libtoolize |
| 142 | + $to_install = str_replace('libtoolize', 'libtool', $to_install); |
| 143 | + shell()->exec($prefix . $install_cmd . ' ' . implode(' ', $to_install)); |
| 144 | + |
| 145 | + return true; |
| 146 | + } |
| 147 | +} |
0 commit comments