Merge branch '2.0'
commit
7b77161b7e
|
@ -16,6 +16,7 @@ use Composer\Composer;
|
||||||
use Composer\DependencyResolver\Request;
|
use Composer\DependencyResolver\Request;
|
||||||
use Composer\Installer;
|
use Composer\Installer;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Package\Loader\RootPackageLoader;
|
||||||
use Composer\Plugin\CommandEvent;
|
use Composer\Plugin\CommandEvent;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Package\Version\VersionParser;
|
||||||
|
@ -140,8 +141,9 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$rootRequires = $composer->getPackage()->getRequires();
|
$rootPackage = $composer->getPackage();
|
||||||
$rootDevRequires = $composer->getPackage()->getDevRequires();
|
$rootRequires = $rootPackage->getRequires();
|
||||||
|
$rootDevRequires = $rootPackage->getDevRequires();
|
||||||
foreach ($reqs as $package => $constraint) {
|
foreach ($reqs as $package => $constraint) {
|
||||||
if (isset($rootRequires[$package])) {
|
if (isset($rootRequires[$package])) {
|
||||||
$rootRequires[$package] = $this->appendConstraintToLink($rootRequires[$package], $constraint);
|
$rootRequires[$package] = $this->appendConstraintToLink($rootRequires[$package], $constraint);
|
||||||
|
@ -151,8 +153,10 @@ EOT
|
||||||
throw new \UnexpectedValueException('Only root package requirements can receive temporary constraints and '.$package.' is not one');
|
throw new \UnexpectedValueException('Only root package requirements can receive temporary constraints and '.$package.' is not one');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$composer->getPackage()->setRequires($rootRequires);
|
$rootPackage->setRequires($rootRequires);
|
||||||
$composer->getPackage()->setDevRequires($rootDevRequires);
|
$rootPackage->setDevRequires($rootDevRequires);
|
||||||
|
$rootPackage->setReferences(RootPackageLoader::extractReferences($reqs, $rootPackage->getReferences()));
|
||||||
|
$rootPackage->setStabilityFlags(RootPackageLoader::extractStabilityFlags($reqs, $rootPackage->getMinimumStability(), $rootPackage->getStabilityFlags()));
|
||||||
|
|
||||||
if ($input->getOption('interactive')) {
|
if ($input->getOption('interactive')) {
|
||||||
$packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages);
|
$packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages);
|
||||||
|
|
|
@ -158,151 +158,157 @@ class EventDispatcher
|
||||||
|
|
||||||
$this->pushEvent($event);
|
$this->pushEvent($event);
|
||||||
|
|
||||||
$returnMax = 0;
|
try {
|
||||||
foreach ($listeners as $callable) {
|
$returnMax = 0;
|
||||||
$return = 0;
|
foreach ($listeners as $callable) {
|
||||||
$this->ensureBinDirIsInPath();
|
$return = 0;
|
||||||
|
$this->ensureBinDirIsInPath();
|
||||||
|
|
||||||
if (!is_string($callable)) {
|
if (!is_string($callable)) {
|
||||||
if (!is_callable($callable)) {
|
if (!is_callable($callable)) {
|
||||||
$className = is_object($callable[0]) ? get_class($callable[0]) : $callable[0];
|
$className = is_object($callable[0]) ? get_class($callable[0]) : $callable[0];
|
||||||
|
|
||||||
throw new \RuntimeException('Subscriber '.$className.'::'.$callable[1].' for event '.$event->getName().' is not callable, make sure the function is defined and public');
|
throw new \RuntimeException('Subscriber '.$className.'::'.$callable[1].' for event '.$event->getName().' is not callable, make sure the function is defined and public');
|
||||||
}
|
}
|
||||||
if (is_array($callable) && (is_string($callable[0]) || is_object($callable[0])) && is_string($callable[1])) {
|
if (is_array($callable) && (is_string($callable[0]) || is_object($callable[0])) && is_string($callable[1])) {
|
||||||
$this->io->writeError(sprintf('> %s: %s', $event->getName(), (is_object($callable[0]) ? get_class($callable[0]) : $callable[0]).'->'.$callable[1]), true, IOInterface::VERBOSE);
|
$this->io->writeError(sprintf('> %s: %s', $event->getName(), (is_object($callable[0]) ? get_class($callable[0]) : $callable[0]).'->'.$callable[1]), true, IOInterface::VERBOSE);
|
||||||
}
|
}
|
||||||
$return = false === call_user_func($callable, $event) ? 1 : 0;
|
$return = false === call_user_func($callable, $event) ? 1 : 0;
|
||||||
} elseif ($this->isComposerScript($callable)) {
|
} elseif ($this->isComposerScript($callable)) {
|
||||||
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable), true, IOInterface::VERBOSE);
|
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $callable), true, IOInterface::VERBOSE);
|
||||||
|
|
||||||
$script = explode(' ', substr($callable, 1));
|
$script = explode(' ', substr($callable, 1));
|
||||||
$scriptName = $script[0];
|
$scriptName = $script[0];
|
||||||
unset($script[0]);
|
unset($script[0]);
|
||||||
|
|
||||||
|
$args = array_merge($script, $event->getArguments());
|
||||||
|
$flags = $event->getFlags();
|
||||||
|
if (strpos($callable, '@composer ') === 0) {
|
||||||
|
$exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . ' ' . implode(' ', $args);
|
||||||
|
if (0 !== ($exitCode = $this->executeTty($exec))) {
|
||||||
|
$this->io->writeError(sprintf('<error>Script %s handling the %s event returned with error code '.$exitCode.'</error>', $callable, $event->getName()), true, IOInterface::QUIET);
|
||||||
|
|
||||||
|
throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!$this->getListeners(new Event($scriptName))) {
|
||||||
|
$this->io->writeError(sprintf('<warning>You made a reference to a non-existent script %s</warning>', $callable), true, IOInterface::QUIET);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
/** @var InstallerEvent $event */
|
||||||
|
$scriptEvent = new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags);
|
||||||
|
$scriptEvent->setOriginatingEvent($event);
|
||||||
|
$return = $this->dispatch($scriptName, $scriptEvent);
|
||||||
|
} catch (ScriptExecutionException $e) {
|
||||||
|
$this->io->writeError(sprintf('<error>Script %s was called via %s</error>', $callable, $event->getName()), true, IOInterface::QUIET);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($this->isPhpScript($callable)) {
|
||||||
|
$className = substr($callable, 0, strpos($callable, '::'));
|
||||||
|
$methodName = substr($callable, strpos($callable, '::') + 2);
|
||||||
|
|
||||||
|
if (!class_exists($className)) {
|
||||||
|
$this->io->writeError('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>', true, IOInterface::QUIET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!is_callable($callable)) {
|
||||||
|
$this->io->writeError('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>', true, IOInterface::QUIET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$message = "Script %s handling the %s event terminated with an exception";
|
||||||
|
$this->io->writeError('<error>'.sprintf($message, $callable, $event->getName()).'</error>', true, IOInterface::QUIET);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$args = implode(' ', array_map(array('Composer\Util\ProcessExecutor', 'escape'), $event->getArguments()));
|
||||||
|
$exec = $callable . ($args === '' ? '' : ' '.$args);
|
||||||
|
if ($this->io->isVerbose()) {
|
||||||
|
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $exec));
|
||||||
|
} elseif ($event->getName() !== '__exec_command') {
|
||||||
|
// do not output the command being run when using `composer exec` as it is fairly obvious the user is running it
|
||||||
|
$this->io->writeError(sprintf('> %s', $exec));
|
||||||
|
}
|
||||||
|
|
||||||
|
$possibleLocalBinaries = $this->composer->getPackage()->getBinaries();
|
||||||
|
if ($possibleLocalBinaries) {
|
||||||
|
foreach ($possibleLocalBinaries as $localExec) {
|
||||||
|
if (preg_match('{\b'.preg_quote($callable).'$}', $localExec)) {
|
||||||
|
$caller = BinaryInstaller::determineBinaryCaller($localExec);
|
||||||
|
$exec = preg_replace('{^'.preg_quote($callable).'}', $caller . ' ' . $localExec, $exec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($exec, '@putenv ') === 0) {
|
||||||
|
putenv(substr($exec, 8));
|
||||||
|
list($var, $value) = explode('=', substr($exec, 8), 2);
|
||||||
|
$_SERVER[$var] = $value;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strpos($exec, '@php ') === 0) {
|
||||||
|
$pathAndArgs = substr($exec, 5);
|
||||||
|
if (Platform::isWindows()) {
|
||||||
|
$pathAndArgs = preg_replace_callback('{^\S+}', function ($path) {
|
||||||
|
return str_replace('/', '\\', $path[0]);
|
||||||
|
}, $pathAndArgs);
|
||||||
|
}
|
||||||
|
// match somename (not in quote, and not a qualified path) and if it is not a valid path from CWD then try to find it
|
||||||
|
// in $PATH. This allows support for `@php foo` where foo is a binary name found in PATH but not an actual relative path
|
||||||
|
preg_match('{^[^\'"\s/\\\\]+}', $pathAndArgs, $match);
|
||||||
|
if (!file_exists($match[0])) {
|
||||||
|
$finder = new ExecutableFinder;
|
||||||
|
if ($pathToExec = $finder->find($match[0])) {
|
||||||
|
$pathAndArgs = $pathToExec . substr($pathAndArgs, strlen($match[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exec = $this->getPhpExecCommand() . ' ' . $pathAndArgs;
|
||||||
|
} else {
|
||||||
|
$finder = new PhpExecutableFinder();
|
||||||
|
$phpPath = $finder->find(false);
|
||||||
|
if ($phpPath) {
|
||||||
|
$_SERVER['PHP_BINARY'] = $phpPath;
|
||||||
|
putenv('PHP_BINARY=' . $_SERVER['PHP_BINARY']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Platform::isWindows()) {
|
||||||
|
$exec = preg_replace_callback('{^\S+}', function ($path) {
|
||||||
|
return str_replace('/', '\\', $path[0]);
|
||||||
|
}, $exec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if composer is being executed, make sure it runs the expected composer from current path
|
||||||
|
// resolution, even if bin-dir contains composer too because the project requires composer/composer
|
||||||
|
// see https://github.com/composer/composer/issues/8748
|
||||||
|
if (strpos($exec, 'composer ') === 0) {
|
||||||
|
$exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . substr($exec, 8);
|
||||||
|
}
|
||||||
|
|
||||||
$args = array_merge($script, $event->getArguments());
|
|
||||||
$flags = $event->getFlags();
|
|
||||||
if (strpos($callable, '@composer ') === 0) {
|
|
||||||
$exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . ' ' . implode(' ', $args);
|
|
||||||
if (0 !== ($exitCode = $this->executeTty($exec))) {
|
if (0 !== ($exitCode = $this->executeTty($exec))) {
|
||||||
$this->io->writeError(sprintf('<error>Script %s handling the %s event returned with error code '.$exitCode.'</error>', $callable, $event->getName()), true, IOInterface::QUIET);
|
$this->io->writeError(sprintf('<error>Script %s handling the %s event returned with error code '.$exitCode.'</error>', $callable, $event->getName()), true, IOInterface::QUIET);
|
||||||
|
|
||||||
throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (!$this->getListeners(new Event($scriptName))) {
|
|
||||||
$this->io->writeError(sprintf('<warning>You made a reference to a non-existent script %s</warning>', $callable), true, IOInterface::QUIET);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
/** @var InstallerEvent $event */
|
|
||||||
$scriptEvent = new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags);
|
|
||||||
$scriptEvent->setOriginatingEvent($event);
|
|
||||||
$return = $this->dispatch($scriptName, $scriptEvent);
|
|
||||||
} catch (ScriptExecutionException $e) {
|
|
||||||
$this->io->writeError(sprintf('<error>Script %s was called via %s</error>', $callable, $event->getName()), true, IOInterface::QUIET);
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif ($this->isPhpScript($callable)) {
|
|
||||||
$className = substr($callable, 0, strpos($callable, '::'));
|
|
||||||
$methodName = substr($callable, strpos($callable, '::') + 2);
|
|
||||||
|
|
||||||
if (!class_exists($className)) {
|
|
||||||
$this->io->writeError('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>', true, IOInterface::QUIET);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!is_callable($callable)) {
|
|
||||||
$this->io->writeError('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>', true, IOInterface::QUIET);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$returnMax = max($returnMax, $return);
|
||||||
$return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$message = "Script %s handling the %s event terminated with an exception";
|
|
||||||
$this->io->writeError('<error>'.sprintf($message, $callable, $event->getName()).'</error>', true, IOInterface::QUIET);
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$args = implode(' ', array_map(array('Composer\Util\ProcessExecutor', 'escape'), $event->getArguments()));
|
|
||||||
$exec = $callable . ($args === '' ? '' : ' '.$args);
|
|
||||||
if ($this->io->isVerbose()) {
|
|
||||||
$this->io->writeError(sprintf('> %s: %s', $event->getName(), $exec));
|
|
||||||
} elseif ($event->getName() !== '__exec_command') {
|
|
||||||
// do not output the command being run when using `composer exec` as it is fairly obvious the user is running it
|
|
||||||
$this->io->writeError(sprintf('> %s', $exec));
|
|
||||||
}
|
|
||||||
|
|
||||||
$possibleLocalBinaries = $this->composer->getPackage()->getBinaries();
|
if ($event->isPropagationStopped()) {
|
||||||
if ($possibleLocalBinaries) {
|
break;
|
||||||
foreach ($possibleLocalBinaries as $localExec) {
|
|
||||||
if (preg_match('{\b'.preg_quote($callable).'$}', $localExec)) {
|
|
||||||
$caller = BinaryInstaller::determineBinaryCaller($localExec);
|
|
||||||
$exec = preg_replace('{^'.preg_quote($callable).'}', $caller . ' ' . $localExec, $exec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos($exec, '@putenv ') === 0) {
|
|
||||||
putenv(substr($exec, 8));
|
|
||||||
list($var, $value) = explode('=', substr($exec, 8), 2);
|
|
||||||
$_SERVER[$var] = $value;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (strpos($exec, '@php ') === 0) {
|
|
||||||
$pathAndArgs = substr($exec, 5);
|
|
||||||
if (Platform::isWindows()) {
|
|
||||||
$pathAndArgs = preg_replace_callback('{^\S+}', function ($path) {
|
|
||||||
return str_replace('/', '\\', $path[0]);
|
|
||||||
}, $pathAndArgs);
|
|
||||||
}
|
|
||||||
// match somename (not in quote, and not a qualified path) and if it is not a valid path from CWD then try to find it
|
|
||||||
// in $PATH. This allows support for `@php foo` where foo is a binary name found in PATH but not an actual relative path
|
|
||||||
preg_match('{^[^\'"\s/\\\\]+}', $pathAndArgs, $match);
|
|
||||||
if (!file_exists($match[0])) {
|
|
||||||
$finder = new ExecutableFinder;
|
|
||||||
if ($pathToExec = $finder->find($match[0])) {
|
|
||||||
$pathAndArgs = $pathToExec . substr($pathAndArgs, strlen($match[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$exec = $this->getPhpExecCommand() . ' ' . $pathAndArgs;
|
|
||||||
} else {
|
|
||||||
$finder = new PhpExecutableFinder();
|
|
||||||
$phpPath = $finder->find(false);
|
|
||||||
if ($phpPath) {
|
|
||||||
$_SERVER['PHP_BINARY'] = $phpPath;
|
|
||||||
putenv('PHP_BINARY=' . $_SERVER['PHP_BINARY']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Platform::isWindows()) {
|
|
||||||
$exec = preg_replace_callback('{^\S+}', function ($path) {
|
|
||||||
return str_replace('/', '\\', $path[0]);
|
|
||||||
}, $exec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if composer is being executed, make sure it runs the expected composer from current path
|
|
||||||
// resolution, even if bin-dir contains composer too because the project requires composer/composer
|
|
||||||
// see https://github.com/composer/composer/issues/8748
|
|
||||||
if (strpos($exec, 'composer ') === 0) {
|
|
||||||
$exec = $this->getPhpExecCommand() . ' ' . ProcessExecutor::escape(getenv('COMPOSER_BINARY')) . substr($exec, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 !== ($exitCode = $this->executeTty($exec))) {
|
|
||||||
$this->io->writeError(sprintf('<error>Script %s handling the %s event returned with error code '.$exitCode.'</error>', $callable, $event->getName()), true, IOInterface::QUIET);
|
|
||||||
|
|
||||||
throw new ScriptExecutionException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->popEvent();
|
||||||
|
|
||||||
$returnMax = max($returnMax, $return);
|
throw $e;
|
||||||
|
|
||||||
if ($event->isPropagationStopped()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->popEvent();
|
$this->popEvent();
|
||||||
|
|
|
@ -144,8 +144,8 @@ class RootPackageLoader extends ArrayLoader
|
||||||
$links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
|
$links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
|
||||||
}
|
}
|
||||||
$aliases = $this->extractAliases($links, $aliases);
|
$aliases = $this->extractAliases($links, $aliases);
|
||||||
$stabilityFlags = $this->extractStabilityFlags($links, $stabilityFlags, $realPackage->getMinimumStability());
|
$stabilityFlags = self::extractStabilityFlags($links, $realPackage->getMinimumStability(), $stabilityFlags);
|
||||||
$references = $this->extractReferences($links, $references);
|
$references = self::extractReferences($links, $references);
|
||||||
|
|
||||||
if (isset($links[$config['name']])) {
|
if (isset($links[$config['name']])) {
|
||||||
throw new \RuntimeException(sprintf('Root package \'%s\' cannot require itself in its composer.json' . PHP_EOL .
|
throw new \RuntimeException(sprintf('Root package \'%s\' cannot require itself in its composer.json' . PHP_EOL .
|
||||||
|
@ -203,7 +203,10 @@ class RootPackageLoader extends ArrayLoader
|
||||||
return $aliases;
|
return $aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function extractStabilityFlags(array $requires, array $stabilityFlags, $minimumStability)
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static function extractStabilityFlags(array $requires, $minimumStability, array $stabilityFlags)
|
||||||
{
|
{
|
||||||
$stabilities = BasePackage::$stabilities;
|
$stabilities = BasePackage::$stabilities;
|
||||||
$minimumStability = $stabilities[$minimumStability];
|
$minimumStability = $stabilities[$minimumStability];
|
||||||
|
@ -256,7 +259,10 @@ class RootPackageLoader extends ArrayLoader
|
||||||
return $stabilityFlags;
|
return $stabilityFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function extractReferences(array $requires, array $references)
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static function extractReferences(array $requires, array $references)
|
||||||
{
|
{
|
||||||
foreach ($requires as $reqName => $reqVersion) {
|
foreach ($requires as $reqName => $reqVersion) {
|
||||||
$reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
|
$reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
|
||||||
|
|
Loading…
Reference in New Issue