Ship your dependencies as phars inside the phar
authorMichael Wallner <mike@php.net>
Fri, 6 Mar 2015 15:51:57 +0000 (16:51 +0100)
committerMichael Wallner <mike@php.net>
Fri, 6 Mar 2015 15:51:57 +0000 (16:51 +0100)
Madness didn't stop, just have a look:

$ cd pecl_http.git
$ pharext -qps ../propro.git
$ pharext -qps ../raphf.git
$ pharext -qps .
$ ./pecl_http-2.4.0dev.ext.phar --sudo

Output:

Installing propro-1.0.1.ext.phar ...
Running phpize ... OK
Running configure ... OK
Running make ... OK
Running install ... OK
Cleaning up /tmp/propro-1.0.1.ext.phar.54f9cbc1488a0 ...
Don't forget to activiate the extension in your php.ini!

Installing raphf-1.0.5.ext.phar ...
Running phpize ... OK
Running configure ... OK
Running make ... OK
Running install ... OK
Cleaning up /tmp/raphf-1.0.5.ext.phar.54f9cbc148a50 ...
Don't forget to activiate the extension in your php.ini!

Installing pecl_http-2.4.0dev.ext.phar ...
Running phpize ... OK
Running configure ... OK
Running make ... OK
Running install ... OK
Cleaning up /tmp/pecl_http-2.4.0dev.ext.phar.54f9cbc14869f ...
Don't forget to activiate the extension in your php.ini!

bin/pharext
src/pharext/Installer.php
src/pharext/Packager.php
src/pharext/PeclSourceDir.php
src/pharext_installer.php
src/pharext_packager.php

index 30306c5d052ca0d58d83ceaa3ade2553823a96cd..8de8858ad01c58da9154fa238aa4a3275df75b23 100755 (executable)
Binary files a/bin/pharext and b/bin/pharext differ
index 57bbf06d23664f0a37395f0d5c1e0917a16fab0b..19aad72c67f531bee4b77361769115483a2262d0 100644 (file)
@@ -59,10 +59,25 @@ class Installer implements Command
         * @see \pharext\Command::run()
         */
        public function run($argc, array $argv) {
-               if (($hook = stream_resolve_include_path("pharext_install.php"))) {
-                       $callable = include $hook;
-                       if (is_callable($callable)) {
-                               $recv = $callable($this);
+               $this->cwd = getcwd();
+               $this->tmp = $this->tempname(basename(Phar::running(false)));
+
+               $phar = new Phar(Phar::running(false));
+               foreach ($phar as $entry) {
+                       if (fnmatch("*.ext.phar*", $entry->getBaseName())) {
+                               $temp = $this->newtemp($entry->getBaseName());
+                               $phar->extractTo($temp, $entry->getFilename(), true);
+                               $phars[$temp] = new Phar($temp."/".$entry->getFilename());
+                       }
+               }
+               $phars[$this->tmp] = $phar;
+
+               foreach ($phars as $phar) {
+                       if (($hook = $phar["pharext_install.php"])) {
+                               $callable = include $phar["pharext_install.php"];
+                               if (is_callable($callable)) {
+                                       $recv[] = $callable($this);
+                               }
                        }
                }
                
@@ -96,19 +111,39 @@ class Installer implements Command
                }
                
                if (isset($recv)) {
-                       $recv($this);
+                       foreach ($recv as $r) {
+                               $r($this);
+                       }
+               }
+               foreach ($phars as $temp => $phar) {
+                       $this->installPackage($phar, $temp);
                }
-               
-               $this->installPackage();
+       }
+
+       private function newtemp($prefix) {
+               $temp = $this->tempname($prefix);
+               if (!is_dir($temp)) {
+                       if (!mkdir($temp, 0750, true)) {
+                               $this->error(null);
+                               exit(3);
+                       }
+               }
+               return $temp;
        }
        
        /**
         * Prepares, configures, builds and installs the extension
         */
-       private function installPackage() {
-               $this->extract();
+       private function installPackage(Phar $phar, $temp) {
+               $this->info("Installing %s ... \n", basename($phar->getAlias()));
+               try {
+                       $phar->extractTo($temp, null, true);
+               } catch (\Exception $e) {
+                       $this->error("%s\n", $e->getMessage());
+                       exit(3);
+               }
 
-               if (!chdir($this->tmp)) {
+               if (!chdir($temp)) {
                        $this->error(null);
                        exit(4);
                }
@@ -119,50 +154,25 @@ class Installer implements Command
                $this->exec("make", $this->args->verbose ? "make -j3" : "make -sj3");
                $this->exec("install", $this->args->verbose ? "make install" : "make -s install", true);
 
-               $this->cleanup();
+               $this->cleanup($temp);
 
-               $this->info("\nDon't forget to activiate the extension in your php.ini!\n");
+               $this->info("Don't forget to activiate the extension in your php.ini!\n\n");
        }
 
        /**
         * Perform any cleanups
         */
-       private function cleanup() {
-               if (is_dir($this->tmp)) {
+       private function cleanup($temp = null) {
+               if (!isset($temp)) {
+                       $temp = $this->tmp;
+               }
+               if (is_dir($temp)) {
                        chdir($this->cwd);
-                       $this->info("Cleaning up %s ...\n", $this->tmp);
-                       $this->rm($this->tmp);
+                       $this->info("Cleaning up %s ...\n", $temp);
+                       $this->rm($temp);
                }
        }
 
-       /**
-        * Extract the phar to a temporary directory
-        */
-       private function extract() {
-               if (!$file = Phar::running(false)) {
-                       $this->error("Did your run the ext.phar?\n");
-                       exit(3);
-               }
-
-               $temp = $this->tempname(basename($file));
-               if (!is_dir($temp)) {
-                       if (!mkdir($temp, 0750, true)) {
-                               $this->error(null);
-                               exit(3);
-                       }
-               }
-               $this->tmp = $temp;
-               $this->cwd = getcwd();
-
-               try {
-                       $phar = new Phar($file);
-                       $phar->extractTo($temp, null, true);
-               } catch (\Exception $e) {
-                       $this->error("%s\n", $e->getMessage());
-                       exit(3);
-               }
-       }
-       
        /**
         * rm -r
         * @param string $dir
index 0b133e357909f937b75d8244f8d3899ac4df7d45..f27c38a0a8b0f7e623b71cf514dc1f9b0b6850e5 100644 (file)
@@ -64,17 +64,21 @@ class Packager implements Command
                        $this->help($prog);
                        exit;
                }
-               
-               if ($this->args["source"]) {
-                       if ($this->args["pecl"]) {
-                               $this->source = new PeclSourceDir($this, $this->args["source"]);
-                       } elseif ($this->args["git"]) {
-                               $this->source = new GitSourceDir($this, $this->args["source"]);
-                       } elseif (realpath($this->args["source"]."/pharext_package.php")) {
-                               $this->source = new PharextSourceDir($this, $this->args["source"]);
-                       } else {
-                               $this->source = new FilteredSourceDir($this, $this->args["source"]);
+
+               try {
+                       if ($this->args["source"]) {
+                               if ($this->args["pecl"]) {
+                                       $this->source = new PeclSourceDir($this, $this->args["source"]);
+                               } elseif ($this->args["git"]) {
+                                       $this->source = new GitSourceDir($this, $this->args["source"]);
+                               } elseif (realpath($this->args["source"]."/pharext_package.php")) {
+                                       $this->source = new PharextSourceDir($this, $this->args["source"]);
+                               } else {
+                                       $this->source = new FilteredSourceDir($this, $this->args["source"]);
+                               }
                        }
+               } catch (\Exception $e) {
+                       $errs[] = $e->getMessage();
                }
                
                foreach ($this->args->validate() as $error) {
@@ -88,6 +92,7 @@ class Packager implements Command
                        foreach ($errs as $err) {
                                $this->error("%s\n", $err);
                        }
+                       printf("\n");
                        if (!$this->args["quiet"]) {
                                $this->help($prog);
                        }
@@ -119,7 +124,7 @@ class Packager implements Command
        
                $this->info("Creating phar %s ...%s", $pkgtemp, $this->args->verbose ? "\n" : " ");
                try {
-                       $package = new Phar($pkgtemp, 0, "ext.phar");
+                       $package = new Phar($pkgtemp);
                        $package->startBuffering();
                        $package->buildFromIterator($this->source, $this->source->getBaseDir());
                        $package->buildFromIterator($this->bundle());
index ca065ad21c4e142e50b521d07271513354d7a2f0..1e6d86ec7c8360d568136025e48eb0ba96725f4f 100644 (file)
@@ -36,6 +36,9 @@ class PeclSourceDir implements \IteratorAggregate, SourceDir
         * @see \pharext\SourceDir::__construct()
         */
        public function __construct(Command $cmd, $path) {
+               if (!realpath("$path/package.xml")) {
+                       throw new \Exception("Missing package.xml in $path");
+               }
                $sxe = simplexml_load_file("$path/package.xml");
                $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]);
                
@@ -54,18 +57,6 @@ class PeclSourceDir implements \IteratorAggregate, SourceDir
                        }
                }
                
-               if (($configure = $sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"))) {
-                       $this->hook = tmpfile();
-                       ob_start(function($s) {
-                               fwrite($this->hook, $s);
-                               return null;
-                       });
-                       call_user_func(function() use ($configure) {
-                               include __DIR__."/../pharext_install.tpl.php";
-                       });
-                       ob_end_flush();
-               }
-               
                $this->cmd = $cmd;
                $this->sxe = $sxe;
                $this->path = $path;
@@ -91,15 +82,56 @@ class PeclSourceDir implements \IteratorAggregate, SourceDir
                }
                return trim($path, "/");
        }
+
+       /**
+        * Render installer hook
+        * @param array $configure
+        * @return string
+        */
+       private static function loadHook($configure, $dependencies) {
+               return include __DIR__."/../pharext_install.tpl.php";
+       }
+
+       /**
+        * Create installer hook
+        * @return resource
+        */
+       private function generateHooks() {
+               $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package");
+               foreach ($dependencies as $key => $dep) {
+                       if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) {
+                               usort($glob, function($a, $b) {
+                                       return version_compare(
+                                               substr($a, strpos(".ext.phar", $a)),
+                                               substr($b, strpos(".ext.phar", $b))
+                                       );
+                               });
+                               yield realpath($this->path."/".end($glob));
+                       } else {
+                               unset($dependencies[$key]);
+                       }
+               }
+               $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption");
+               if ($configure) {
+                       $fd = tmpfile();
+                       ob_start(function($s) use($fd){
+                               fwrite($fd, $s);
+                               return null;
+                       });
+                       self::loadHook($configure, $dependencies);
+                       ob_end_flush();
+                       rewind($fd);
+                       yield "pharext_install.php" =>  $fd;
+               }
+       }
        
        /**
         * Generate a list of files from the package.xml
         * @return Generator
         */
        private function generateFiles() {
-               if ($this->hook) {
-                       rewind($this->hook);
-                       yield "pharext_install.php" => $this->hook;
+               foreach ($this->generateHooks() as $file => $hook) {
+                       yield $file => $hook;
                }
                foreach ($this->sxe->xpath("//pecl:file") as $file) {
                        $path = $this->path ."/". $this->dirOf($file) ."/". $file["name"];
index 90c9f508688528bc4b5ceb28c24ea42c9fcd5af7..89e44a3ec62073acea09f0386900e66ee3008bb6 100644 (file)
@@ -3,9 +3,9 @@
  * The installer sub-stub for extension phars
  */
 
-function __autoload($c) {
+spl_autoload_register(function($c) {
        return include strtr($c, "\\_", "//") . ".php";
-}
+});
 
 $installer = new pharext\Installer();
 $installer->run($argc, $argv);
index b72da8a123671462421992f50a09007563e07002..125db61bdea9bdda248c463e4e8780aa3327067e 100644 (file)
@@ -3,9 +3,9 @@
  * The packager sub-stub for bin/pharext
  */
 
-function __autoload($c) {
+spl_autoload_register(function($c) {
        return include strtr($c, "\\_", "//") . ".php";
-}
+});
 
 $packager = new pharext\Packager();
 $packager->run($argc, $argv);