home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / tmp / PEAR-1.7.1 / PEAR / Builder.php < prev    next >
Encoding:
PHP Script  |  2008-02-15  |  15.6 KB  |  487 lines

  1. <?php
  2. /**
  3.  * PEAR_Builder for building PHP extensions (PECL packages)
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category   pear
  14.  * @package    PEAR
  15.  * @author     Stig Bakken <ssb@php.net>
  16.  * @author     Greg Beaver <cellog@php.net>
  17.  * @copyright  1997-2008 The PHP Group
  18.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  19.  * @version    CVS: $Id: Builder.php,v 1.33 2008/01/17 05:48:37 cellog Exp $
  20.  * @link       http://pear.php.net/package/PEAR
  21.  * @since      File available since Release 0.1
  22.  * 
  23.  * TODO: log output parameters in PECL command line
  24.  * TODO: msdev path in configuration
  25.  */
  26.  
  27. /**
  28.  * Needed for extending PEAR_Builder
  29.  */
  30. require_once 'PEAR/Common.php';
  31. require_once 'PEAR/PackageFile.php';
  32. /**
  33.  * Class to handle building (compiling) extensions.
  34.  *
  35.  * @category   pear
  36.  * @package    PEAR
  37.  * @author     Stig Bakken <ssb@php.net>
  38.  * @author     Greg Beaver <cellog@php.net>
  39.  * @copyright  1997-2008 The PHP Group
  40.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  41.  * @version    Release: 1.7.1
  42.  * @link       http://pear.php.net/package/PEAR
  43.  * @since      Class available since PHP 4.0.2
  44.  * @see        http://pear.php.net/manual/en/core.ppm.pear-builder.php
  45.  */
  46. class PEAR_Builder extends PEAR_Common
  47. {
  48.     // {{{ properties
  49.  
  50.     var $php_api_version = 0;
  51.     var $zend_module_api_no = 0;
  52.     var $zend_extension_api_no = 0;
  53.  
  54.     var $extensions_built = array();
  55.  
  56.     /**
  57.      * @var string Used for reporting when it is not possible to pass function
  58.      *             via extra parameter, e.g. log, msdevCallback
  59.      */
  60.     var $current_callback = null;
  61.  
  62.     // used for msdev builds
  63.     var $_lastline = null;
  64.     var $_firstline = null;
  65.     // }}}
  66.     // {{{ constructor
  67.  
  68.     /**
  69.      * PEAR_Builder constructor.
  70.      *
  71.      * @param object $ui user interface object (instance of PEAR_Frontend_*)
  72.      *
  73.      * @access public
  74.      */
  75.     function PEAR_Builder(&$ui)
  76.     {
  77.         parent::PEAR_Common();
  78.         $this->setFrontendObject($ui);
  79.     }
  80.  
  81.     // }}}
  82.  
  83.     // {{{ _build_win32()
  84.  
  85.     /**
  86.      * Build an extension from source on windows.
  87.      * requires msdev
  88.      */
  89.     function _build_win32($descfile, $callback = null)
  90.     {
  91.         if (is_object($descfile)) {
  92.             $pkg = $descfile;
  93.             $descfile = $pkg->getPackageFile();
  94.         } else {
  95.             $pf = &new PEAR_PackageFile($this->config, $this->debug);
  96.             $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  97.             if (PEAR::isError($pkg)) {
  98.                 return $pkg;
  99.             }
  100.         }
  101.         $dir = dirname($descfile);
  102.         $old_cwd = getcwd();
  103.  
  104.         if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
  105.             return $this->raiseError("could not chdir to $dir");
  106.         }
  107.         // packages that were in a .tar have the packagefile in this directory
  108.         $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
  109.         if (file_exists($dir) && is_dir($vdir)) {
  110.             if (chdir($vdir)) {
  111.                 $dir = getcwd();
  112.             } else {
  113.                 return $this->raiseError("could not chdir to " . realpath($vdir));
  114.             }
  115.         }
  116.  
  117.         $this->log(2, "building in $dir");
  118.  
  119.         $dsp = $pkg->getPackage().'.dsp';
  120.         if (!file_exists("$dir/$dsp")) {
  121.             return $this->raiseError("The DSP $dsp does not exist.");
  122.         }
  123.         // XXX TODO: make release build type configurable
  124.         $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"';
  125.  
  126.         $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
  127.         if (PEAR::isError($err)) {
  128.             return $err;
  129.         }
  130.  
  131.         // figure out the build platform and type
  132.         $platform = 'Win32';
  133.         $buildtype = 'Release';
  134.         if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
  135.             $platform = $matches[1];
  136.             $buildtype = $matches[2];
  137.         }
  138.  
  139.         if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) {
  140.             if ($matches[2]) {
  141.                 // there were errors in the build
  142.                 return $this->raiseError("There were errors during compilation.");
  143.             }
  144.             $out = $matches[1];
  145.         } else {
  146.             return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
  147.         }
  148.  
  149.         // msdev doesn't tell us the output directory :/
  150.         // open the dsp, find /out and use that directory
  151.         $dsptext = join(file($dsp),'');
  152.  
  153.         // this regex depends on the build platform and type having been
  154.         // correctly identified above.
  155.         $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
  156.                     $pkg->getPackage().'\s-\s'.
  157.                     $platform.'\s'.
  158.                     $buildtype.'").*?'.
  159.                     '\/out:"(.*?)"/is';
  160.  
  161.         if ($dsptext && preg_match($regex,$dsptext,$matches)) {
  162.             // what we get back is a relative path to the output file itself.
  163.             $outfile = realpath($matches[2]);
  164.         } else {
  165.             return $this->raiseError("Could not retrieve output information from $dsp.");
  166.         }
  167.         // realpath returns false if the file doesn't exist
  168.         if ($outfile && copy($outfile, "$dir/$out")) {
  169.             $outfile = "$dir/$out";
  170.         }
  171.  
  172.         $built_files[] = array(
  173.             'file' => "$outfile",
  174.             'php_api' => $this->php_api_version,
  175.             'zend_mod_api' => $this->zend_module_api_no,
  176.             'zend_ext_api' => $this->zend_extension_api_no,
  177.             );
  178.  
  179.         return $built_files;
  180.     }
  181.     // }}}
  182.  
  183.     // {{{ msdevCallback()
  184.     function msdevCallback($what, $data)
  185.     {
  186.         if (!$this->_firstline)
  187.             $this->_firstline = $data;
  188.         $this->_lastline = $data;
  189.         call_user_func($this->current_callback, $what, $data);
  190.     }
  191.     // }}}
  192.  
  193.     // {{{ _harventInstDir
  194.     /**
  195.      * @param string
  196.      * @param string
  197.      * @param array
  198.      * @access private
  199.      */
  200.     function _harvestInstDir($dest_prefix, $dirname, &$built_files)
  201.     {
  202.         $d = opendir($dirname);
  203.         if (!$d)
  204.             return false;
  205.  
  206.         $ret = true;
  207.         while (($ent = readdir($d)) !== false) {
  208.             if ($ent{0} == '.')
  209.                 continue;
  210.  
  211.             $full = $dirname . DIRECTORY_SEPARATOR . $ent;
  212.             if (is_dir($full)) {
  213.                 if (!$this->_harvestInstDir(
  214.                         $dest_prefix . DIRECTORY_SEPARATOR . $ent,
  215.                         $full, $built_files)) {
  216.                     $ret = false;
  217.                     break;
  218.                 }
  219.             } else {
  220.                 $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
  221.                 $built_files[] = array(
  222.                         'file' => $full,
  223.                         'dest' => $dest,
  224.                         'php_api' => $this->php_api_version,
  225.                         'zend_mod_api' => $this->zend_module_api_no,
  226.                         'zend_ext_api' => $this->zend_extension_api_no,
  227.                         );
  228.             }
  229.         }
  230.         closedir($d);
  231.         return $ret;
  232.     }
  233.  
  234.     // }}}
  235.  
  236.     // {{{ build()
  237.  
  238.     /**
  239.      * Build an extension from source.  Runs "phpize" in the source
  240.      * directory, but compiles in a temporary directory
  241.      * (/var/tmp/pear-build-USER/PACKAGE-VERSION).
  242.      *
  243.      * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or
  244.      *               a PEAR_PackageFile object
  245.      *
  246.      * @param mixed $callback callback function used to report output,
  247.      * see PEAR_Builder::_runCommand for details
  248.      *
  249.      * @return array an array of associative arrays with built files,
  250.      * format:
  251.      * array( array( 'file' => '/path/to/ext.so',
  252.      *               'php_api' => YYYYMMDD,
  253.      *               'zend_mod_api' => YYYYMMDD,
  254.      *               'zend_ext_api' => YYYYMMDD ),
  255.      *        ... )
  256.      *
  257.      * @access public
  258.      *
  259.      * @see PEAR_Builder::_runCommand
  260.      */
  261.     function build($descfile, $callback = null)
  262.     {
  263.         $this->current_callback = $callback;
  264.         if (PEAR_OS == "Windows") {
  265.             return $this->_build_win32($descfile,$callback);
  266.         }
  267.         if (PEAR_OS != 'Unix') {
  268.             return $this->raiseError("building extensions not supported on this platform");
  269.         }
  270.         if (is_object($descfile)) {
  271.             $pkg = $descfile;
  272.             $descfile = $pkg->getPackageFile();
  273.             if (is_a($pkg, 'PEAR_PackageFile_v1')) {
  274.                 $dir = dirname($descfile);
  275.             } else {
  276.                 $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName();
  277.                 // automatically delete at session end
  278.                 $this->addTempFile($dir);
  279.             }
  280.         } else {
  281.             $pf = &new PEAR_PackageFile($this->config);
  282.             $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  283.             if (PEAR::isError($pkg)) {
  284.                 return $pkg;
  285.             }
  286.             $dir = dirname($descfile);
  287.         }
  288.         $old_cwd = getcwd();
  289.         if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
  290.             return $this->raiseError("could not chdir to $dir");
  291.         }
  292.         $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
  293.         if (is_dir($vdir)) {
  294.             chdir($vdir);
  295.         }
  296.         $dir = getcwd();
  297.         $this->log(2, "building in $dir");
  298.         putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
  299.         $err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback'));
  300.         if (PEAR::isError($err)) {
  301.             return $err;
  302.         }
  303.         if (!$err) {
  304.             return $this->raiseError("`phpize' failed");
  305.         }
  306.  
  307.         // {{{ start of interactive part
  308.         $configure_command = "$dir/configure";
  309.         $configure_options = $pkg->getConfigureOptions();
  310.         if ($configure_options) {
  311.             foreach ($configure_options as $o) {
  312.                 $default = array_key_exists('default', $o) ? $o['default'] : null;
  313.                 list($r) = $this->ui->userDialog('build',
  314.                                                  array($o['prompt']),
  315.                                                  array('text'),
  316.                                                  array($default));
  317.                 if (substr($o['name'], 0, 5) == 'with-' &&
  318.                     ($r == 'yes' || $r == 'autodetect')) {
  319.                     $configure_command .= " --$o[name]";
  320.                 } else {
  321.                     $configure_command .= " --$o[name]=".trim($r);
  322.                 }
  323.             }
  324.         }
  325.         // }}} end of interactive part
  326.  
  327.         // FIXME make configurable
  328.         if(!$user=getenv('USER')){
  329.             $user='defaultuser';
  330.         }
  331.         $build_basedir = "/var/tmp/pear-build-$user";
  332.         $build_dir = "$build_basedir/$vdir";
  333.         $inst_dir = "$build_basedir/install-$vdir";
  334.         $this->log(1, "building in $build_dir");
  335.         if (is_dir($build_dir)) {
  336.             System::rm(array('-rf', $build_dir));
  337.         }
  338.         if (!System::mkDir(array('-p', $build_dir))) {
  339.             return $this->raiseError("could not create build dir: $build_dir");
  340.         }
  341.         $this->addTempFile($build_dir);
  342.         if (!System::mkDir(array('-p', $inst_dir))) {
  343.             return $this->raiseError("could not create temporary install dir: $inst_dir");
  344.         }
  345.         $this->addTempFile($inst_dir);
  346.  
  347.         if (getenv('MAKE')) {
  348.             $make_command = getenv('MAKE');
  349.         } else {
  350.             $make_command = 'make';
  351.         }
  352.         $to_run = array(
  353.             $configure_command,
  354.             $make_command,
  355.             "$make_command INSTALL_ROOT=\"$inst_dir\" install",
  356.             "find \"$inst_dir\" -ls"
  357.             );
  358.         if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) {
  359.             return $this->raiseError("could not chdir to $build_dir");
  360.         }
  361.         putenv('PHP_PEAR_VERSION=1.7.1');
  362.         foreach ($to_run as $cmd) {
  363.             $err = $this->_runCommand($cmd, $callback);
  364.             if (PEAR::isError($err)) {
  365.                 chdir($old_cwd);
  366.                 return $err;
  367.             }
  368.             if (!$err) {
  369.                 chdir($old_cwd);
  370.                 return $this->raiseError("`$cmd' failed");
  371.             }
  372.         }
  373.         if (!($dp = opendir("modules"))) {
  374.             chdir($old_cwd);
  375.             return $this->raiseError("no `modules' directory found");
  376.         }
  377.         $built_files = array();
  378.         $prefix = exec("php-config --prefix");
  379.         $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
  380.         chdir($old_cwd);
  381.         return $built_files;
  382.     }
  383.  
  384.     // }}}
  385.     // {{{ phpizeCallback()
  386.  
  387.     /**
  388.      * Message callback function used when running the "phpize"
  389.      * program.  Extracts the API numbers used.  Ignores other message
  390.      * types than "cmdoutput".
  391.      *
  392.      * @param string $what the type of message
  393.      * @param mixed $data the message
  394.      *
  395.      * @return void
  396.      *
  397.      * @access public
  398.      */
  399.     function phpizeCallback($what, $data)
  400.     {
  401.         if ($what != 'cmdoutput') {
  402.             return;
  403.         }
  404.         $this->log(1, rtrim($data));
  405.         if (preg_match('/You should update your .aclocal.m4/', $data)) {
  406.             return;
  407.         }
  408.         $matches = array();
  409.         if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
  410.             $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
  411.             $apino = (int)$matches[2];
  412.             if (isset($this->$member)) {
  413.                 $this->$member = $apino;
  414.                 //$msg = sprintf("%-22s : %d", $matches[1], $apino);
  415.                 //$this->log(1, $msg);
  416.             }
  417.         }
  418.     }
  419.  
  420.     // }}}
  421.     // {{{ _runCommand()
  422.  
  423.     /**
  424.      * Run an external command, using a message callback to report
  425.      * output.  The command will be run through popen and output is
  426.      * reported for every line with a "cmdoutput" message with the
  427.      * line string, including newlines, as payload.
  428.      *
  429.      * @param string $command the command to run
  430.      *
  431.      * @param mixed $callback (optional) function to use as message
  432.      * callback
  433.      *
  434.      * @return bool whether the command was successful (exit code 0
  435.      * means success, any other means failure)
  436.      *
  437.      * @access private
  438.      */
  439.     function _runCommand($command, $callback = null)
  440.     {
  441.         $this->log(1, "running: $command");
  442.         $pp = popen("$command 2>&1", "r");
  443.         if (!$pp) {
  444.             return $this->raiseError("failed to run `$command'");
  445.         }
  446.         if ($callback && $callback[0]->debug == 1) {
  447.             $olddbg = $callback[0]->debug;
  448.             $callback[0]->debug = 2;
  449.         }
  450.  
  451.         while ($line = fgets($pp, 1024)) {
  452.             if ($callback) {
  453.                 call_user_func($callback, 'cmdoutput', $line);
  454.             } else {
  455.                 $this->log(2, rtrim($line));
  456.             }
  457.         }
  458.         if ($callback && isset($olddbg)) {
  459.             $callback[0]->debug = $olddbg;
  460.         }
  461.         if (is_resource($pp)) {
  462.             $exitcode = pclose($pp);
  463.         } else {
  464.             $exitcode = -1;
  465.         }
  466.         return ($exitcode == 0);
  467.     }
  468.  
  469.     // }}}
  470.     // {{{ log()
  471.  
  472.     function log($level, $msg)
  473.     {
  474.         if ($this->current_callback) {
  475.             if ($this->debug >= $level) {
  476.                 call_user_func($this->current_callback, 'output', $msg);
  477.             }
  478.             return;
  479.         }
  480.         return PEAR_Common::log($level, $msg);
  481.     }
  482.  
  483.     // }}}
  484. }
  485.  
  486. ?>
  487.