Skip to content

Add utility for mapping classifiers to feature variants#344

Open
lukebemish wants to merge 3 commits into
neoforged:mainfrom
lukebemish:utility-classifier-remap
Open

Add utility for mapping classifiers to feature variants#344
lukebemish wants to merge 3 commits into
neoforged:mainfrom
lukebemish:utility-classifier-remap

Conversation

@lukebemish

Copy link
Copy Markdown
Contributor

Addresses #306

Currently, jarJar-ing artifacts with classifiers does not work. There are two issues with making something like this work: first is that recovering the resolved artifact classifier is difficult-to-impossible (there are hacky ways, such as what I implemented in loom for backwards compat). The second is that it can actually lead to incorrect behavior, because Gradle artifact classifiers are not considered at all unless manually overridden during selection. That is: it is possible to not explicitly ask for a classifier, but get an artifact with one (see: how IJ resolves sources, nowadays, using an artifactView). This means that an attempt to recover classifiers from resolved artifacts could actually result in writing incorrect jarJar IDs if the artifact is not part of the dependency at all, but an incidental detail of the publishing of that particular version.

Gradle has a built-in alternative to artifact classifiers, with many advantages, that covers many of the same use cases: feature variants. I would highly encourage modders wishing to publish artifacts that may be jarJar-ed to use these instead of classifiers. Obviously, however, this may not be a workable solution for non-mod dependencies that publish only maven metadata, or publish Gradle metadata but for some reason insist on using classifiers anyways.

Gradle provides a tool that is meant for this type of thing: component metadata rules, which modify the metadata of a component during resolution. These are not things modders should need to care about. This PR provides a convenient DSL for setting up these mappings:

dependencies {
    jarJar(neoForge.dependencyTools.mapClassifierToFeature("org.example.group:module-name:0.1.0", "my-classifier"))
    // Or, to specify version ranges more explicitly:
    jarJar(neoForge.dependencyTools.mapClassifierToFeature("org.example.group:module-name", "my-classifier")) {
        version { /* ... */ }
    }
}

Internally, this makes use of a component rule that modifies the metadata of your dependency during resolution. An effect of this is that if you publish a runtime dependency on that module, consumers will also need to apply the same mapping in their own buildscripts. If this is not desired, you can jarJar the mapped dependency but otherwise depend on the unmapped one (in implementation/api, etc.)

The rule works by creating variants otherwise identical to runtime/compile (or runtimeElements/apiElements), except with the classifier-ed jar as an artifact instead of the normal artifacts, and with a capability matching a feature with the same name as the classifier.

jarJar-ing a dependency with a classifier currently leads to incorrect behavior (see: the linked issue) where it is jarJar-ed with nothing in its ID reflecting the identifier, meaning that it conflicts with an non-classifier-ed jar that is also jarJar-ed by this mod or another. This PR makes this case an explicit error; note that this is "breaking" in the sense that any buildscript doing this will now error, but I would not consider this a "breaking change" because the only affected cases already had an "error" (that is, they created incorrect jarJar metadata that could conflict with other mods), this change just shifts that failure to build time instead of run time.

This utility is also exposed in an extension added to Settings if the repositories plugin is applied there; this is necessary because otherwise the utility would not be usable in a setup with a rules mode of FAIL_ON_PROJECT_RULES. Since there is nothing special about the returned dependency -- it's just a dependency on the module, requesting a feature with the name of the classifier -- it's quite possible to declare the mappings in your settings.gradle using this, then depend on the mapped features from within your build.gradle.

@neoforged-pr-publishing

Copy link
Copy Markdown
  • Publish PR to GitHub Packages

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant