2023-03-26 22:27:51 +08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2025-08-06 20:28:25 +08:00
|
|
|
namespace SPC\util\shell;
|
2023-03-26 22:27:51 +08:00
|
|
|
|
2025-06-09 10:24:06 +08:00
|
|
|
use SPC\builder\freebsd\library\BSDLibraryBase;
|
|
|
|
|
use SPC\builder\linux\library\LinuxLibraryBase;
|
|
|
|
|
use SPC\builder\macos\library\MacOSLibraryBase;
|
2025-08-06 20:28:25 +08:00
|
|
|
use SPC\exception\SPCInternalException;
|
2025-10-20 23:04:33 +02:00
|
|
|
use SPC\util\SPCTarget;
|
2023-03-26 22:27:51 +08:00
|
|
|
use ZM\Logger\ConsoleColor;
|
|
|
|
|
|
2025-08-06 20:28:25 +08:00
|
|
|
/**
|
|
|
|
|
* Unix-like OS shell command executor.
|
|
|
|
|
*
|
|
|
|
|
* This class provides methods to execute shell commands in a Unix-like environment.
|
|
|
|
|
* It supports setting environment variables and changing the working directory.
|
|
|
|
|
*/
|
|
|
|
|
class UnixShell extends Shell
|
2023-03-26 22:27:51 +08:00
|
|
|
{
|
2023-04-22 21:23:12 +08:00
|
|
|
public function __construct(?bool $debug = null)
|
2023-03-26 22:27:51 +08:00
|
|
|
{
|
2023-12-24 20:17:06 +08:00
|
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
2025-08-06 20:28:25 +08:00
|
|
|
throw new SPCInternalException('Windows cannot use UnixShell');
|
2023-12-24 20:17:06 +08:00
|
|
|
}
|
2025-08-06 20:28:25 +08:00
|
|
|
parent::__construct($debug);
|
2023-03-26 22:27:51 +08:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 20:28:25 +08:00
|
|
|
public function exec(string $cmd): static
|
2023-03-26 22:27:51 +08:00
|
|
|
{
|
2025-10-23 22:14:57 +02:00
|
|
|
$cmd = clean_spaces($cmd);
|
2023-03-26 22:27:51 +08:00
|
|
|
/* @phpstan-ignore-next-line */
|
|
|
|
|
logger()->info(ConsoleColor::yellow('[EXEC] ') . ConsoleColor::green($cmd));
|
2025-08-06 20:28:25 +08:00
|
|
|
$original_command = $cmd;
|
|
|
|
|
$this->logCommandInfo($original_command);
|
|
|
|
|
$this->last_cmd = $cmd = $this->getExecString($cmd);
|
|
|
|
|
$this->passthru($cmd, $this->debug, $original_command);
|
2023-03-26 22:27:51 +08:00
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-09 10:24:06 +08:00
|
|
|
/**
|
|
|
|
|
* Init the environment variable that common build will be used.
|
|
|
|
|
*
|
|
|
|
|
* @param BSDLibraryBase|LinuxLibraryBase|MacOSLibraryBase $library Library class
|
|
|
|
|
*/
|
2025-06-09 11:12:34 +08:00
|
|
|
public function initializeEnv(BSDLibraryBase|LinuxLibraryBase|MacOSLibraryBase $library): UnixShell
|
2025-06-09 10:24:06 +08:00
|
|
|
{
|
|
|
|
|
$this->setEnv([
|
|
|
|
|
'CFLAGS' => $library->getLibExtraCFlags(),
|
2025-07-26 20:12:18 +07:00
|
|
|
'CXXFLAGS' => $library->getLibExtraCXXFlags(),
|
2025-06-09 10:24:06 +08:00
|
|
|
'LDFLAGS' => $library->getLibExtraLdFlags(),
|
2025-10-20 23:04:33 +02:00
|
|
|
'LIBS' => $library->getLibExtraLibs() . SPCTarget::getRuntimeLibs(),
|
2025-06-09 10:24:06 +08:00
|
|
|
]);
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-26 22:27:51 +08:00
|
|
|
public function execWithResult(string $cmd, bool $with_log = true): array
|
|
|
|
|
{
|
|
|
|
|
if ($with_log) {
|
|
|
|
|
/* @phpstan-ignore-next-line */
|
|
|
|
|
logger()->info(ConsoleColor::blue('[EXEC] ') . ConsoleColor::green($cmd));
|
|
|
|
|
} else {
|
2024-07-09 00:44:03 +08:00
|
|
|
/* @phpstan-ignore-next-line */
|
|
|
|
|
logger()->debug(ConsoleColor::blue('[EXEC] ') . ConsoleColor::gray($cmd));
|
2023-03-26 22:27:51 +08:00
|
|
|
}
|
2025-06-19 09:14:39 +07:00
|
|
|
$cmd = $this->getExecString($cmd);
|
2023-03-26 22:27:51 +08:00
|
|
|
exec($cmd, $out, $code);
|
|
|
|
|
return [$code, $out];
|
|
|
|
|
}
|
2023-04-29 18:59:47 +08:00
|
|
|
|
2025-08-06 20:28:25 +08:00
|
|
|
/**
|
|
|
|
|
* Returns unix-style environment variable string.
|
|
|
|
|
*/
|
2025-06-09 19:32:31 +08:00
|
|
|
public function getEnvString(): string
|
2023-04-29 18:59:47 +08:00
|
|
|
{
|
|
|
|
|
$str = '';
|
|
|
|
|
foreach ($this->env as $k => $v) {
|
|
|
|
|
$str .= ' ' . $k . '="' . $v . '"';
|
|
|
|
|
}
|
|
|
|
|
return trim($str);
|
|
|
|
|
}
|
2025-06-19 09:14:39 +07:00
|
|
|
|
2025-08-06 20:28:25 +08:00
|
|
|
protected function logCommandInfo(string $cmd): void
|
|
|
|
|
{
|
|
|
|
|
// write executed command to log file using fwrite
|
|
|
|
|
$log_file = fopen(SPC_SHELL_LOG, 'a');
|
|
|
|
|
fwrite($log_file, "\n>>>>>>>>>>>>>>>>>>>>>>>>>> [" . date('Y-m-d H:i:s') . "]\n");
|
|
|
|
|
fwrite($log_file, "> Executing command: {$cmd}\n");
|
|
|
|
|
// get the backtrace to find the file and line number
|
|
|
|
|
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
|
|
|
|
if (isset($backtrace[1]['file'], $backtrace[1]['line'])) {
|
|
|
|
|
$file = $backtrace[1]['file'];
|
|
|
|
|
$line = $backtrace[1]['line'];
|
|
|
|
|
fwrite($log_file, "> Called from: {$file} at line {$line}\n");
|
|
|
|
|
}
|
|
|
|
|
fwrite($log_file, "> Environment variables: {$this->getEnvString()}\n");
|
|
|
|
|
if ($this->cd !== null) {
|
|
|
|
|
fwrite($log_file, "> Working dir: {$this->cd}\n");
|
|
|
|
|
}
|
|
|
|
|
fwrite($log_file, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 09:14:39 +07:00
|
|
|
private function getExecString(string $cmd): string
|
|
|
|
|
{
|
2025-08-06 20:28:25 +08:00
|
|
|
// logger()->debug('Executed at: ' . debug_backtrace()[0]['file'] . ':' . debug_backtrace()[0]['line']);
|
2025-06-19 09:14:39 +07:00
|
|
|
$env_str = $this->getEnvString();
|
|
|
|
|
if (!empty($env_str)) {
|
|
|
|
|
$cmd = "{$env_str} {$cmd}";
|
|
|
|
|
}
|
|
|
|
|
if ($this->cd !== null) {
|
|
|
|
|
$cmd = 'cd ' . escapeshellarg($this->cd) . ' && ' . $cmd;
|
|
|
|
|
}
|
|
|
|
|
return $cmd;
|
|
|
|
|
}
|
2023-03-26 22:27:51 +08:00
|
|
|
}
|