From cec1627acf7ea66799ec43116b460fb52cf0ce75 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 20 Jul 2014 21:55:24 +0200 Subject: [PATCH] Add top level aliases for nonstandard scripts and allow parameter passing, refs #2516 --- composer.json | 3 + src/Composer/Command/RunScriptCommand.php | 7 +- src/Composer/Command/ScriptAliasCommand.php | 67 +++++++++++++++++++ src/Composer/Console/Application.php | 9 +++ src/Composer/EventDispatcher/Event.php | 21 +++++- .../EventDispatcher/EventDispatcher.php | 14 ++-- src/Composer/Plugin/CommandEvent.php | 5 +- src/Composer/Script/Event.php | 8 ++- 8 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 src/Composer/Command/ScriptAliasCommand.php diff --git a/composer.json b/composer.json index 961ddf0ec..718b06780 100644 --- a/composer.json +++ b/composer.json @@ -47,5 +47,8 @@ "branch-alias": { "dev-master": "1.0-dev" } + }, + "scripts": { + "test": "phpunit" } } diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 50449b89d..7f94814a7 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -55,6 +55,7 @@ class RunScriptCommand extends Command ->setDescription('Run the scripts defined in composer.json.') ->setDefinition(array( new InputArgument('script', InputArgument::REQUIRED, 'Script name to run.'), + new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'), )) @@ -88,10 +89,12 @@ EOT putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH')); } + $args = $input->getArguments(); + if (in_array($script, $this->commandEvents)) { - return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev')); + return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']); } - return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev')); + return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']); } } diff --git a/src/Composer/Command/ScriptAliasCommand.php b/src/Composer/Command/ScriptAliasCommand.php new file mode 100644 index 000000000..958678068 --- /dev/null +++ b/src/Composer/Command/ScriptAliasCommand.php @@ -0,0 +1,67 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Jordi Boggiano + */ +class ScriptAliasCommand extends Command +{ + private $script; + + public function __construct($script) + { + $this->script = $script; + + parent::__construct(); + } + + protected function configure() + { + $this + ->setName($this->script) + ->setDescription('Run the '.$this->script.' script as defined in composer.json.') + ->setDefinition(array( + new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'), + new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), + )) + ->setHelp(<<run-script command runs scripts defined in composer.json: + +php composer.phar run-script post-update-cmd +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $composer = $this->getComposer(); + + // add the bin dir to the PATH to make local binaries of deps usable in scripts + $binDir = $composer->getConfig()->get('bin-dir'); + if (is_dir($binDir)) { + putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH')); + } + + $args = $input->getArguments(); + + return $composer->getEventDispatcher()->dispatchScript($this->script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']); + } +} diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 926d68719..29615af47 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -104,6 +104,15 @@ class Application extends BaseApplication $input->setInteractive(false); } + // add non-standard scripts as own commands + if ($composer = $this->getComposer(false)) { + foreach ($composer->getPackage()->getScripts() as $script => $dummy) { + if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { + $this->add(new Command\ScriptAliasCommand($script)); + } + } + } + if ($input->hasParameterOption('--profile')) { $startTime = microtime(true); $this->io->enableDebugging($startTime); diff --git a/src/Composer/EventDispatcher/Event.php b/src/Composer/EventDispatcher/Event.php index 0b3c9c951..7aff24f11 100644 --- a/src/Composer/EventDispatcher/Event.php +++ b/src/Composer/EventDispatcher/Event.php @@ -24,6 +24,11 @@ class Event */ protected $name; + /** + * @var array Arguments passed by the user + */ + protected $args; + /** * @var boolean Whether the event should not be passed to more listeners */ @@ -32,11 +37,13 @@ class Event /** * Constructor. * - * @param string $name The event name + * @param string $name The event name + * @param array $events Arguments passed by the user */ - public function __construct($name) + public function __construct($name, array $args = array()) { $this->name = $name; + $this->args = $args; } /** @@ -49,6 +56,16 @@ class Event return $this->name; } + /** + * Returns the event's arguments. + * + * @return array The event arguments + */ + public function getArguments() + { + return $this->args; + } + /** * Checks if stopPropagation has been called * diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index c1d3be064..5a7492610 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -76,12 +76,13 @@ class EventDispatcher * * @param string $eventName The constant in ScriptEvents * @param Script\Event $event + * @param array $additionalArgs * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchScript($eventName, $devMode = false) + public function dispatchScript($eventName, $devMode = false, $additionalArgs = array()) { - return $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode)); + return $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode, $additionalArgs)); } /** @@ -103,18 +104,20 @@ class EventDispatcher * * @param string $eventName The constant in ScriptEvents * @param boolean $devMode Whether or not we are in dev mode + * @param array $additionalArgs * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchCommandEvent($eventName, $devMode) + public function dispatchCommandEvent($eventName, $devMode, $additionalArgs = array()) { - return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode)); + return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs)); } /** * Triggers the listeners of an event. * * @param Event $event The event object to pass to the event handlers/listeners. + * @param string $additionalArgs * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 * @throws \RuntimeException @@ -149,7 +152,8 @@ class EventDispatcher throw $e; } } else { - if (0 !== ($exitCode = $this->process->execute($callable))) { + $args = implode(' ', array_map('escapeshellarg', $event->getArguments())); + if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) { $event->getIO()->write(sprintf('Script %s handling the %s event returned with an error', $callable, $event->getName())); throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode); diff --git a/src/Composer/Plugin/CommandEvent.php b/src/Composer/Plugin/CommandEvent.php index 0f75bed9e..1c83db2b9 100644 --- a/src/Composer/Plugin/CommandEvent.php +++ b/src/Composer/Plugin/CommandEvent.php @@ -45,10 +45,11 @@ class CommandEvent extends Event * @param string $commandName The command name * @param InputInterface $input * @param OutputInterface $output + * @param array $events Arguments passed by the user */ - public function __construct($name, $commandName, $input, $output) + public function __construct($name, $commandName, $input, $output, array $args = array()) { - parent::__construct($name); + parent::__construct($name, $args); $this->commandName = $commandName; $this->input = $input; $this->output = $output; diff --git a/src/Composer/Script/Event.php b/src/Composer/Script/Event.php index 40b109b2d..58fb4788b 100644 --- a/src/Composer/Script/Event.php +++ b/src/Composer/Script/Event.php @@ -14,6 +14,7 @@ namespace Composer\Script; use Composer\Composer; use Composer\IO\IOInterface; +use Composer\EventDispatcher\Event as BaseEvent; /** * The script event class @@ -21,7 +22,7 @@ use Composer\IO\IOInterface; * @author François Pluchino * @author Nils Adermann */ -class Event extends \Composer\EventDispatcher\Event +class Event extends BaseEvent { /** * @var Composer The composer instance @@ -45,10 +46,11 @@ class Event extends \Composer\EventDispatcher\Event * @param Composer $composer The composer object * @param IOInterface $io The IOInterface object * @param boolean $devMode Whether or not we are in dev mode + * @param array $events Arguments passed by the user */ - public function __construct($name, Composer $composer, IOInterface $io, $devMode = false) + public function __construct($name, Composer $composer, IOInterface $io, $devMode = false, array $args = array()) { - parent::__construct($name); + parent::__construct($name, $args); $this->composer = $composer; $this->io = $io; $this->devMode = $devMode;