I have looked at the build scan and either I am looking at the wrong place or no other plugin applies it (as expected).
Scan of the plugin build, not the consumer.
Same for dependencies
. Doesn’t make any sense at all to look at the dependencies
task of the consumer if you are interested in the build logic.
Once again, I can not see any plugin which adds it.
You will not see any build logic at all, you are looking at your consumer’s build production dependencies.
I tried the dependencyInsight
task but I do not know what a configuration is or which dependency or configuration to enter when I get this error:
There is pretty much documentation that explains it, for example Dependency Management Terminology for a short definition and further links. Did you actually try to research this, or are you just asking along? I’m not an interactive manual, you know? 
I need the Android plugin for one subproject and the library plugin for another. I need to independently be able to apply the plugin with a specific version.
Well, as I said, you cannot. There is not “the Android plugin” and “the library plugin”. Even if you meant to say “the Android application plugin” and the “the Android library plugin”. There is only one bunch of android plugins call “the Android Gradle plugin” which comes with the “Android application” plugin, the “Android library” plugin, …, you cannot have either independently, this is simply impossible. But I’m not an Android developer, if you need more in-depth help with that AGP mess, I’m not the one able to help.
Without the settings plugin I would be able to independently add the application plugin to one project and the library plugin to another, any version I want.
If you say so, I’m not Android developer enough to know that.
But as I said, if your settings plugin depends on the Android Gradle plugin, it is in the settings classpath and thus in a parent class loader that is in the ancestry of all build scripts and you will not be able to use different AGP version in different subprojects. I guess this is anyway a bad idea, but you have to clarify that with someone savvy in Android quirks.
If you really want different AGP versions, you probably have to just have it as compileOnly
dependency in your settings plugin and reacting to the respective plugins being applied instead of applying them, using pluginManager.withPlugin(...) { ... }
and making sure you are compatible with all version you need to be compatible with.
So what can I do to prevent users from having to declare the repositories all the time?
Nothing good.
Maybe do not depend on dependencies from strange repositories.
If it is for some corporate in-house thing, I’d say put up a NXRM, not having one is very bad idea anyway, that could mirror / proxy all the other repositiories, so you only need one to get them all.
If it is for something openly available, you cannot do much in a good way.
The repositories needed to resolve settings plugins and their dependencies have to be defined by the consuming build, full-stop.
But most typically you should only use dependencies available in Gradle Plugin Portal or Maven Central and by that it just works out-of-the-box.
If you really need dependencies from non-canonical repositories, either convince their maintainers to publish to the canonical repositories, find an alternative to use, provide a proxy to all repositories you need to your consumers, or have your consumers declare those repositories.
So why was I never asked to declare repositories apart from the ones to get the certain plugins from? These plugins surely depended on other plugins which came from other repos I did not add myself.
Unliekly, but my crystal ball is broken. If you want to talk about any concrete one, name them. Everything else would just be speculation. Most probably those plugins only depend on things found in the same repository or in any other you have declared.
How would I do that for the publication?
private fun Project.configureSigning() {
apply("signing")
extensions.configure<SigningExtension>("signing") {
it.useGpgCmd()
extensions.getByType(PublishingExtension::class.java).publications.named { it == "revanced-patches-publication" }.configureEach(it::sign)
}
}
Generally for domain object collections, if the entry you want might not exist yet, react to it being added in the past or future by properly using lazy reactive methods.
without an afterEvaluate
i can not get the publication.
Yes you can.
And by using afterEvaluate
you might get it, but you also might not get it.
Or you might get it today but not tomorrow.
Again, the main effect earned with using afterEvaluate
is timing problems, ordering problems, and race conditions.
In the other example I do use get() on the providers but how would else would I do it?
Just do not use the property value during configuration time:
private fun Project.configureArtifactSharing(extension: ExtensionExtension) {
val syncExtensionTask = tasks.register("syncExtension", Sync::class.java) {
it.apply {
dependsOn("minifyReleaseWithR8")
from(layout.buildDirectory.file("intermediates/dex/release/minifyReleaseWithR8/classes.dex"))
into(layout.buildDirectory.zip(extension.name) { buildDirectory, extensionName -> buildDirectory.dir("revanced/${Path(extensionName).parent.pathString}") })
rename { "${Path(extension.name.get()).fileName}" }
}
}
configurations.consumable("extensionConfiguration").also { configuration ->
artifacts.add(
configuration.name,
layout.buildDirectory.dir("revanced"),
) { artifact -> artifact.builtBy(syncExtensionTask) }
}
}
Alternatively, especially if you really need the value at configuration time - unlike this example - as I said, do not have a property in the extension, but instead have a function configureArtifactSharing(extensionName: String)
in your ExtensionExtension
and do the necessary configuration in its body.
I removed get() and it still worked without an
What are those fields in About
?
If they are Property<String>
it will technically work, but not have the result you might expect.
Those manifest attributes are following the old lazyfying approach of calling toString()
late.
Hopefully with Gradle 9 and the Property
fication this changes.
Assuming those fields are Property<String>
, you would in this case probably do something like
private fun Project.configureJarTask(patchesExtension: PatchesExtension) {
tasks.withType(Jar::class.java).configureEach {
with(patchesExtension.about) {
it.manifest.apply {
attributes["Name"] = object { override fun toString() = name.get() }
attributes["Description"] = object { override fun toString() = description.get() }
attributes["Version"] = object { override fun toString() = version.get() }
attributes["Timestamp"] = object { override fun toString() = timestamp.get() }
attributes["Source"] = object { override fun toString() = source.get() }
attributes["Author"] = object { override fun toString() = author.get() }
attributes["Contact"] = object { override fun toString() = contact.get() }
attributes["Website"] = object { override fun toString() = website.get() }
attributes["License"] = object { override fun toString() = license.get() }
}
}
it.archiveExtension.set("rvp")
}
}
Imagine the case where the values would be strictly strings. How would I deal with that here?
As you have seen, highly depends on the actual case.
But in what you indicate, probably again the “use a method, not properties” approach.
Or actually in this specific case you could actually let those values be Property
if you like for consistency but have PatchesExtension#about
be a method that takes an Action<About>
, give an instance to the supplied action and then configure the values from the set values.
But as I said, how to best do it depends from case to case.
I’d like to provide an MCVE but the project is still closed source
Sharing a big fat project would not meet the M
of MCVE
anyway.
Without wrapping that codeblock into afterEvaluate I get the error:
Ah, see, there is the context you left out.
It does not complain about anything with the version catalog, the version catalog is available just fine.
It complains that implementation
configuration is not found.
This happens because you call this before any plugin like java
or java-library
or whatever is applied that adds this configuration.
So you try to use something before it exists.
So here the solution is simply to react to the plugin that adds this configuration being applied, for example like
private fun Project.configureDependencies() {
pluginManager.withPlugin("java") {
val catalog = extensions.getByType(VersionCatalogsExtension::class.java).named("libs")
operator fun String.invoke(versionAlias: String) = dependencies.add(
"implementation",
"$this:" + catalog.findVersion(versionAlias).orElseThrow {
IllegalArgumentException("Version with alias $versionAlias not found in version catalog")
},
)
"app.revanced:revanced-patcher"("revanced-patcher")
"com.android.tools.smali:smali"("smali")
}
}
}
so that this configuration is done as soon as that plugin was applied and did add the configuration where you try to add dependencies to.
others were also using afterEvaluate
And if those people are jumping from a skyscraper, you jump along?
Don’t do stupid things, just because others do stupid things, especially if you are asking experts in physics and health that tell you “do not jump from skyscrapers or you will hurt yourself”.
I would intuitively use thatProject.subprojects
and filter for the parent == thatProject
like this:
And your intuition leads you back to the roof of the skyscraper as I already told you multiple times. How often do I have to repeat it? Do not use that!
Besides that it is non-sense to get all subprojects and then filter for the parent when you could as well just get the child projects.
But that would not be any better.
Do not do that!
So given that thatProject.subProjects
is a subset of gradle.allprojects
why is the latter suggested over the former?
I already told you multiple times, that it is evil and bad to configure one project from another project.
And that is exactly what you do with things like allprojects { ... }
, subproject { ... }
, childProjects.values.forEach { ... }
, project("...") { ... }
and similar approaches from the context of configuring one project. Using gradle.allprojects { ... }
from a settings script is not configuring one project from the scope of another, but configuring projects from the settings script which is also questionable, but at least better than cross-project configuration which is a big fat no-go as I explained already multiple times.
Btw, here I also use get() on extension properties, but how else would I be able to use the provider?
Here this is fine, because the configuration phase “is” the execution phase from the PoV of the settings phase. When the content of rootProject { ... }
is evaluated, the settings script evaluation is already finalized and cannot be changed any further, so it is ok to query the value by then.
The reason I am doing it from the outside is because otherwise, one would have to apply the plugin every time they create a new subproject.
Yes, and that is exactly the idiomatic, and highly recommended way that makes builds much more readable, much more understandable, and much more maintainable.
Adding one line is not really that much boilerplate, I think people will survive that.
If it really is too much for them to type, then provide a task that creates the build script for a new subproject, so that it is generated automatically for them by calling a task like ./gradlew newExtension --name foo
that sets up the structure and the build script with that one line (ok, 3 lines if you format it nicely).
Instead I was imagining that I can setup a specific directory in which projects would be automatically configured.
Yes, I fully understood your intention long ago. Doesn’t change that it is highly discouraged bad practice and that you have to live with some drawbacks for doing so like not having the type-safe accessors generated for example.
Do whatever works best for you, sprinkle afterEvaluate
s everywhere where it sometimes makes things work, do heavy cross configuration, do whatever you like, but you have to live with the consequences. But then please just decide to do so and do not ask the same over and over, ignoring my responses and explanations, requiring me to repeat over and over in desperation to help you when you don’t really want it but always say “why it works how I do it” when I multiple times say that it does not and at best is flaky.
I am able to use type-safe accessors in my patches and extension projects without needing to add the plugins manually.
Ah, ok, was not aware that injecting from the settings script into the projects generates accessors. It was always said that only the extensions added by plugins within the plugins { ... }
block within the same script generate accessors. Gradle Kotlin DSL Primer even explicitly states that any form of cross-project configuration will not make the accessors available. So depending on the accessors you got might be just an unintended side-effect and not supported behavior.
.
The issue starts to occur only once I merge the plugins.
I don’t think the pure merge should really be relevant. You probably changed anything else that then causes the accessors to vanish. Or you are not tripping the bug anymore that actually made the accessors available when they shouldn’t be. Hard to say without seeing the situation. But whether you have the same code in three projects or in one project really shouldn’t make a difference here.
They are slightly different, so would that still work merging them all to a settings plugin?
Probably, if you do it right I guess.
I prefer doing things right if possible, so I did not do much project logic in settings plugins so far, because that is simply out-of-scope and not the right way to go.
But if you want to follow the evil road and have all logic in the settings plugin, injecting it to the projects from outside, I guess it should work just fine.
Also merging the plugins to one project does not mean you have to merge it to just the settings plugin, you can still have the three plugin classes all in one project.
Or you can also make a plugin that is applicable to both, settings and project by not having Plugin<Settings>
or Plugin<Project
but Plugin<PluginAware>
. But then check the type either way, because it could then also be applied to an init script.
PS: Please reasearch the term “Help Vampire”