I've run into a problem building a CPAN distribution using Module::Build, in an interesting way that raises questions about optional dependencies. Specifically, I'm installing Crypt-MySQL-0.04, which comes with both a Build.PL that uses Module::Build and a Makefile.PL that uses ExtUtils::MakeMaker. Building the EU::MM way works perfectly, and is obviously what the author does himself. Building the M::B way works except for an undeclared dependency.
Crypt-MySQL includes some C code in an XS source file. When M::B comes to compile this, it wants to use ExtUtils::CBuilder. I've only just started using M::B, and didn't have EU::CB installed. The Build.PL script therefore emitted a warning, "Module::Build is not configured with C_support". However, that warning is only human-readable, not machine-readable. I was doing this install via the CPAN module. CPAN went on to (I presume) request a prereq_report, but this report doesn't say anything about EU::CB either. CPAN then attempted to build Crypt-MySQL, and the build immediately failed because of the lack of EU::CB. So, we have a dependency problem: Crypt-MySQL's build process required a certain feature of M::B (compiling XS); that feature depends on EU::CB; but the dependency is not installed. C-MS is unaware of the dependency, as it's only using the M::B interface, and M::B appears to be fully installed. C-MS could declare the dependency itself, as build_requires => { ExtUtils::CBuilder => "0.15" }, and I've submitted a patch to the author to do that. But this seems a dubious way to do things. Other than in this possible dependency declaration, the C-MS distribution doesn't know anything about EU::CB; that name is not mentioned anywhere in the C-MS tarball. It uses EU::CB only indirectly. For the specific case of this EU::CB dependency, there are some workarounds available. For a start, I've installed the module on my system, so unless the version requirement changes no distribution will run into this particular problem again. A more interesting possibility is that M::B could treat this as an implicit build_requires for modules that include XS files, so that the dependency gets declared in META.yml and the prereq_report. This doesn't solve the general case, though, which is what I'm interested in. The general problem is that a distribution may depend on an `optional feature' of another distribution. At the moment it only gets to declare the direct dependency on (the main features of) the other distribution. For example, Crypt-MySQL could declare build_requires => { Module::Build => 0 }. (Actually it doesn't have such a declaration, but arguably all Module::Build-using distributions should, at least in META.yml. There's another thing that M::B could do implicitly, but probably shouldn't because of the complexity around M::B subclasses.) To ensure that the optional feature is available, it needs to make the indirect dependencies available, which at the moment it can only do by making them direct dependencies. That requires knowing what the dependencies of the foreign module are, violating encapsulation. I think there ought to be a way to explicitly represent a dependency on an optional feature of another module. Supposing the META.yml files all contained the "optional_features" entry that the spec documents (though I see M::B itself doesn't use that key, only the vague "recommends"). Crypt-MySQL could then have a dependency declaration along the lines of build_requires => { "Module::Build/C_support" => 0 }, to indicate that it requires the C_support feature of the Module::Build module. (I'm blurring the distinction between module and distribution a bit here, because dependencies are usually on modules but the optional_features data is per-distribution.) In principle, the CPAN module or something similar could trace these feature dependencies through the META.yml files and figure out which actual modules they translate to. Knowledge is all appropriately localised, and the dependency can be handle automatically. What do you think? I have some modules myself that would benefit from a distinction between the dependencies of optional features. For example, Date::ISO8601 currently declares hard prerequisites (using PREREQ_PM in EU::MM) of certain versions of Math::BigInt and Math::BigRat, but it can be used perfectly well without any bignum modules. I don't want to downgrade the requirement to a "recommends", though, if it means the version requirement won't be automatically handled when some other module knows that it *will* be doing bignum date arithmetic. -zefram