--- /dev/null
+<?php
+
+namespace pharext;
+
+class ExecCmd
+{
+ /**
+ * Sudo command, if the cmd needs escalated privileges
+ * @var string
+ */
+ private $sudo;
+
+ /**
+ * Executable of the cmd
+ * @var string
+ */
+ private $command;
+
+ /**
+ * Passthrough cmd output
+ * @var bool
+ */
+ private $verbose;
+
+ /**
+ * Output of cmd run
+ * @var string
+ */
+ private $output;
+
+ /**
+ * Return code of cmd run
+ * @var int
+ */
+ private $status;
+
+ /**
+ * @param string $command
+ * @param bool verbose
+ */
+ public function __construct($command, $verbose = false) {
+ $this->command = $command;
+ $this->verbose = $verbose;
+
+ /* interrupt output stream */
+ if ($verbose) {
+ printf("\n");
+ }
+ }
+
+ /**
+ * (Re-)set sudo command
+ * @param string $sudo
+ */
+ public function setSu($sudo = false) {
+ $this->sudo = $sudo;
+ }
+
+ /**
+ * Execute a program with escalated privileges handling interactive password prompt
+ * @param string $command
+ * @param string $output
+ * @param int $status
+ */
+ private function suExec($command, &$output, &$status) {
+ if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) {
+ $status = -1;
+ throw new \Exception("Failed to run {$command}");
+ }
+ $stdout = $pipes[1];
+ $passwd = 0;
+ while (!feof($stdout)) {
+ $R = [$stdout]; $W = []; $E = [];
+ if (!stream_select($R, $W, $E, null)) {
+ continue;
+ }
+ $data = fread($stdout, 0x1000);
+ /* only check a few times */
+ if ($passwd++ < 10) {
+ if (stristr($data, "password")) {
+ printf("\n%s", $data);
+ }
+ }
+ $output .= $data;
+ }
+ $status = proc_close($proc);
+ }
+
+ /**
+ * Run the command
+ * @param array $args
+ * @throws \Exception
+ */
+ public function run(array $args = null) {
+ $exec = escapeshellcmd($this->command);
+ if ($args) {
+ $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args));
+ }
+
+ if ($this->sudo) {
+ $this->suExec(sprintf($this->sudo." 2>&1", $exec), $this->output, $this->status);
+ } elseif ($this->verbose) {
+ ob_start(function($s) {
+ $this->output .= $s;
+ return $s;
+ }, 1);
+ passthru($exec, $this->status);
+ ob_end_flush();
+ } else {
+ exec($exec ." 2>&1", $output, $this->status);
+ $this->output = implode("\n", $output);
+ }
+
+ if ($this->status) {
+ throw new \Exception("Command {$this->command} failed ({$this->status})");
+ }
+ }
+
+ /**
+ * Retrieve exit code of cmd run
+ * @return int
+ */
+ public function getStatus() {
+ return $status;
+ }
+
+ /**
+ * Retrieve output of cmd run
+ * @return string
+ */
+ public function getOutput() {
+ return $this->output;
+ }
+}
$phar = new Phar(Phar::running(false));
foreach ($phar as $entry) {
if (fnmatch("*.ext.phar*", $entry->getBaseName())) {
- $temp = $this->newtemp($entry->getBaseName());
+ $temp = new Tempdir($entry->getBaseName());
$phar->extractTo($temp, $entry->getFilename(), true);
$phars[$temp] = new Phar($temp."/".$entry->getFilename());
}
$this->error(null);
exit(4);
}
-
- // phpize
- $this->exec("phpize", $this->php("ize"));
-
- // configure
- $args = ["--with-php-config=". $this->php("-config")];
- if ($this->args->configure) {
- $args = array_merge($args, $this->args->configure);
- }
- $this->exec("configure", "./configure", $args);
-
- // make
- if ($this->args->verbose) {
- $this->exec("make", "make", ["-j3"]);
- } else {
- $this->exec("make", "make", ["-j3", "-s"]);
- }
-
- // install
- if ($this->args->verbose) {
- $this->exec("install", "make", ["install"], true);
- } else {
- $this->exec("install", "make", ["install", "-s"], true);
- }
-
- // activate
+
+ $this->build();
$this->activate();
-
- // cleanup
$this->cleanup($temp);
}
+
+ /**
+ * Phpize + trinity
+ */
+ private function build() {
+ try {
+ // phpize
+ $this->info("Runnin phpize ... ");
+ $cmd = new ExecCmd($this->php("ize"), $this->args->verbose);
+ $cmd->run();
+ $this->info("OK\n");
+
+ // configure
+ $this->info("Running configure ... ");
+ $args = ["--with-php-config=". $this->php("-config")];
+ if ($this->args->configure) {
+ $args = array_merge($args, $this->args->configure);
+ }
+ $cmd = new ExecCmd("./configure", $this->args->verbose);
+ $cmd->run($args);
+ $this->info("OK\n");
+
+ // make
+ $this->info("Running make ... ");
+ $cmd = new ExecCmd("make", $this->args->verbose);
+ if ($this->args->verbose) {
+ $cmd->run(["-j3"]);
+ } else {
+ $cmd->run(["-j3", "-s"]);
+ }
+ $this->info("OK\n");
+
+ // install
+ $this->info("Running make install ... ");
+ $cmd->setSu($this->args->sudo);
+ if ($this->args->verbose) {
+ $cmd->run(["install"]);
+ } else {
+ $cmd->run(["install", "-s"]);
+ }
+ $this->info("OK\n");
+
+ } catch (\Exception $e) {
+ $this->error("%s\n", $e->getMessage());
+ $this->error("%s\n", $cmd->getOutput());
+ }
+ }
/**
* Perform any cleanups
$path = $temp->getPathname();
$stat = stat($file);
- $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]);
- $this->exec("INI owner transfer", "chown", [$ugid, $path], true);
-
- $perm = decoct($stat["mode"] & 0777);
- $this->exec("INI permission transfer", "chmod", [$perm, $path], true);
-
- $this->exec("INI activation", "mv", [$path, $file], true);
+ try {
+ $this->info("Running INI owner transfer ... ");
+ $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]);
+ $cmd = new ExecCmd("chown", $this->args->verbose);
+ $cmd->setSu($this->args->sudo);
+ $cmd->run([$ugid, $path]);
+ $this->info("OK\n");
+
+ $this->info("Running INI permission transfer ... ");
+ $perm = decoct($stat["mode"] & 0777);
+ $cmd = new ExecCmd("chmod", $this->args->verbose);
+ $cmd->setSu($this->args->sudo);
+ $cmd->run([$perm, $path]);
+ $this->info("OK\n");
+
+ $this->info("Running INI activation ... ");
+ $cmd = new ExecCmd("mv", $this->args->verbose);
+ $cmd->setSu($this->args->sudo);
+ $cmd->run([$path, $file]);
+ $this->info("OK\n");
+ } catch (\Exception $e) {
+ $this->error("%s\n", $e->getMessage());
+ $this->error("%s\n", $cmd->getOutput());
+ exit(5);
+ }
}
}
}
}
try {
+ /* source needs to be evaluated before CliArgs validation,
+ * so e.g. name and version can be overriden and CliArgs
+ * does not complain about missing arguments
+ */
if ($this->args["source"]) {
$source = $this->localize($this->args["source"]);
if ($this->args["pecl"]) {
if ($errs) {
if (!$this->args["quiet"]) {
- $this->header();
+ if (!headers_sent()) {
+ /* only display header, if we didn't generate any output yet */
+ $this->header();
+ }
}
foreach ($errs as $err) {
$this->error("%s\n", $err);
*/
private function download($source) {
if ($this->args["git"]) {
- $local = $this->newtemp("gitclone");
- $this->exec("git clone", "git", ["clone", $source, $local]);
- $source = $local;
+ $this->info("Cloning %s ... ", $source);
+ $local = new Tempdir("gitclone");
+ $cmd = new ExecCmd("git", $this->args->verbose);
+ $cmd->run(["clone", $source, $local]);
+ if (!$this->args->verbose) {
+ $this->info("OK\n");
+ }
} else {
$this->info("Fetching remote source %s ... ", $source);
if (!$remote = fopen($source, "r")) {
exit(2);
}
$local->closeStream();
- $source = $local->getPathname();
$this->info("OK\n");
}
$this->cleanup[] = $local;
- return $source;
+ return $local->getPathname();
}
/**
* @return string extracted directory
*/
private function extract($source) {
- $dest = $this->newtemp("local");
- $this->info("Extracting to %s ... ", $dest);
+ $dest = new Tempdir("local");
+ if ($this->args->verbose) {
+ $this->info("Extracting to %s ... ", $dest);
+ }
$archive = new PharData($source);
$archive->extractTo($dest);
$this->info("OK\n");
--- /dev/null
+<?php
+
+namespace pharext;
+
+class Tempdir extends \SplFileInfo
+{
+ private $dir;
+
+ public function __construct($prefix) {
+ $temp = sprintf("%s/%s.%s", sys_get_temp_dir(), $prefix, uniqid());
+ if (!is_dir($temp)) {
+ if (!mkdir($temp, 0700, true)) {
+ throw new Exception("Could not create tempdir: ".error_get_last()["message"]);
+ }
+ }
+ parent::__construct($temp);
+ }
+}