Support composer_manager AND drupal-composer/drupal-project with contrib module Motivation / use case

I maintain the module package https://drupal.org/project/cfr, which currently requires composer_manager to be installed, and where composer_manager is required in the *.info files of some of the submodules.

Now I also want to support sites that install all their modules via composer, e.g. using https://github.com/drupal-composer/drupal-project.

However, the auto-generated composer.json files now require composer_manager, because they are generated from the *.info files.

composer_manager is undesirable in this context and even creates a conflict with composer_autoloader which is required by drupal-composer/drupal-project.

One specialty here is that there is no actual module named 'cfr', all the modules in the package are submodules like 'cfrapi', 'cfrplugin', 'cfrplugindiscovery' etc. This may or may not be a good choice, but it is how it is now.

Technical observations

I found, by looking into ~/.composer/cache/repo/https---packages.drupal.org-7/*, that Composer / packages.drupal.org creates "meta packages" for submodules.

Even if the submodule already has a composer.json file, this is replaced with a generated composer.json file that requires only the module dependencies from the *.info file, not the 3rd party dependencies from the composer.json file.

This means:
- The meta packages do require modules like drupal/xautoload and drupal/composer_manager.
- The meta packages do NOT require vendor libraries like donquixote/annotation-parser anymore.
- The main package 'cfr' seems like a combination of the meta-packages, but still does not require the vendor libraries.

Question

What do I need to do in a contrib module to support both composer_manager AND the full-composer scenario?

How can I avoid the 'composer_manager' dependency in the generated composer.json for metapackages for the submodules?

Especially, with the scenario above?

For my taste it is ok if the root package requires all the 3rd party stuff, so then I only need to prevent to require drupal/composer_manager.
Here are the conclusions I will follow for the CFR module package.

I am writing this as guidelines for module maintainers.

The steps may look different for other module packages with a different structure.

Start a new major version.

If the existing version of the contrib module requires composer_manager to work, supporting an alternative method might come with backwards-compatibility-breaking side effects. If this is the case, a new major version should be introduced.

Use the 'alternatives' module.

The alternatives module allows to replace one dependency with another.

In the *.info file:

dependencies[] = composer_autoloader
alternatives[composer_autoloader][] = composer_manager


This means: If 'alternatives' and 'composer_manager' are enabled, then the module in question can be enabled without enabling 'composer_autoloader'.

Depend on composer_autoloader

Let your modules, or one of them, depend on composer_autoloader, but add the alternatives[composer_autoloader][] = composer_manager as mentioned above.

Doing this in a subdirectory module causes the metapackage to require drupal/composer_autoloader.

Additional composer.json file at the top level

Why?

Composer manager will look at composer.json files per (sub)module. If the root dir of the module package contains a composer.json, but no module exists in the root dir of the module package, this composer.json will be ignored by composer_manager.

On the other hand, the project_composer module responsible for packages.drupal.org will extract metapackage information from the *.info file in a subdirectory module, but ignore composer.json files for modules in subdirectories. 

Unlike composer_manager, it does look at the main composer.json in the package root dir, even if there is no module in this place.

How?

Put one composer.json at the top level directory of the module package. This has to require the 3rd party libraries from every submodule contained in the module package.

Keep the submodule composerjson files in place.

Replacement composer_manager for the transition.

Goal: Transition from composer_manager to composer_autoloader + drupal-composer.

Problem

If the modules were previously installed with composer_manager and now we want to use composer_autoloader, we are in a bit of a pickle:


At least one of the two dependencies must be enabled at any given time, to allow the dependee module to remain enabled and the 3rd party libraries to be autoloaded.
Downloading both with drupal-composer is not possible, because composer_autoloader declares a conflict with composer_manager in its composer.json.
Assuming we would find a way to download both composer_autoloader and composer_manager at the same time, having both enabled would be possible but would perhaps cause weird behavior due to duplicate vendor directory and duplicate autoload.
Updating code and database at the same time is not possible. We need to call drush en and drush dis, or do the switch via hook_update_N(), in the new codebase.


Solution

Let the site builder create a dummy / placeholder version of composer_manager in sites/all/modules/custom/composer_manager.

The only thing this would do:
- a hook_boot(), which would check if composer_autoloader exists, and if not, it replicate the behavior from composer_autoloader.
- a hook_uninstall() to replicate uninstalling the regular composer_manager.

Then use a hook_update_N() to enable composer_autoloader and disable + uninstall the placeholder composer_manager module.

Perhaps such a placeholder module could be provided on packagist, with an obfuscated name to prevent the conflict with composer_autoloader.