Incompatible with composer/installers
grasmash opened this issue · 8 comments
Steps to reproduce
Create composer.json:
{
"require": {
"wikimedia/composer-merge-plugin": "^1.4"
},
"extra": {
"merge-plugin": {
"require": [
"composer.include.json"
],
"merge-extra": true,
"merge-extra-deep": true
}
}
}
composer.include.json:
{
"require": {
"drupal/core": "^8.0",
"composer/installers": "^1.2"
},
"extra": {
"installer-paths": {
"docroot/core": [
"type:drupal-core"
]
}
}
}
Execute composer install
.
Note that at this point, docroot/core
is created as expected.
mkdir subdir
cp composer.* subdir/
cd subdir
composer install
At this point subdir/docroot/core
is not created. subdir/core
is instead.
It seems that composer/installers cannot correctly create the expected paths. I suspect that this is in part because the path of composer.json has changed. This only occurs when composer-merge-plugin is used.
This has something to with autoloading. If I subsequently run composer dump-autoload && composer install
after the initial failed composer install
, then subdir/docroot/core
will be created. However, I then have both subdir/core
and subdir/docroot/core
.
So I'm starting to think that the installer-paths
config is simply merged too late for composer/installers
to recognize it.
The following output is written to screen after drupal/core
is installed:
Generating autoload files
[merge-plugin] Loading composer.include.json...
[merge-plugin] Merging drupal/core
[RuntimeException]
Could not scan for classes inside "docroot/core/lib/Drupal.php" which does not appear to be a file nor a folder
Exception trace:
() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/src/Composer/Autoload/ClassMapGenerator.php:69
Composer\Autoload\ClassMapGenerator::createMap() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/src/Composer/Autoload/AutoloadGenerator.php:336
Composer\Autoload\AutoloadGenerator->generateClassMap() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/src/Composer/Autoload/AutoloadGenerator.php:319
Composer\Autoload\AutoloadGenerator->addClassMapCode() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/src/Composer/Autoload/AutoloadGenerator.php:266
Composer\Autoload\AutoloadGenerator->dump() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/src/Composer/Installer.php:298
Composer\Installer->run() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/src/Composer/Command/InstallCommand.php:119
Composer\Command\InstallCommand->execute() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/vendor/symfony/console/Command/Command.php:267
Symfony\Component\Console\Command\Command->run() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/vendor/symfony/console/Application.php:846
Symfony\Component\Console\Application->doRunCommand() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/vendor/symfony/console/Application.php:191
Symfony\Component\Console\Application->doRun() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/src/Composer/Console/Application.php:227
Composer\Console\Application->doRun() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/vendor/symfony/console/Application.php:122
Symfony\Component\Console\Application->run() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/src/Composer/Console/Application.php:100
Composer\Console\Application->run() at phar:///usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar/bin/composer:54
require() at /usr/local/Cellar/composer/1.2.2_1/libexec/composer.phar:24
So, composer/installers
has already written drupal/core
to core
before composer-merge-plugin
merges in the installer-paths
config... I think. A subsequent composer install
works but you end up with the package in two places.
@bd808 Is there a way to manually call composer-merge-plugin
using a static method in order to force the merge before drupal/core
is installed? Or else a way to call it earlier?
When running under Composer 1.1+, composer-merge-plugin hooks the INIT hook which is the very first plugin signal sent by the platform. There is not any way to initialize sooner and still have access to the full plugin system.
Using the files from the original issue summary, (with no lock file), we do this:
$ composer install
# Composer does its thing...
$ ls docroot/
core
# Let's do a reset...
$ rm -rf docroot/
$ rm -rf vendor/
# This leaves us with only the two json files and a lock file.
$ composer install
# Eventually see this error:
[RuntimeException]
Could not scan for classes inside "docroot/core/lib/Drupal.php" which does
not appear to be a file nor a folder
$ ls
composer.include.json composer.lock vendor
composer.json core
# Note no docroot/, only core/.
Remove the lock file and it works again.
The fact that drupal/core ends up in core/
tells us that composer/installers plugin is working, since that's the default location for a drupal-core package according to composer-installers.
It also tells us that it hasn't gotten the special config from composer.include.json
, or it would be docroot/core/
instead.
However, the autoloader is being told that drupal/core lives in docroot/core/
, which is why we get the error about scanning for classes.
The fact that this changes when you add/remove composer.lock
tells us that this is a race condition between the two plugins: When there's no lock file, composer/installer is always loaded after wikimedia/composer-merge-plugin because it's merged by it. If there is a lock file, then C comes before W (or some other arbitrary ordering problem).
I'd call this a limitation of the plugin system.
Does anyone has solutions for that case?
This is easy to reproduce.
As a workaround, you can remove "installer-paths" from composer.include.json and add it to composer.json, but this is just a quickfix for the issue.
I'm seeing this too and the workaround from @rutiolma is working ok. It's a bit unfortunate because I'd love to outsource those installer paths into a separate file (even into a separate package) in order to make re-use of complex setup possible. So I hope this can be fiexed eventually.