Differences in ivy/maven publishing for sbt plugin - maven

I have two SBT plugins: PluginA and PluginB. PluginA depends on tasks in PluginB. Whenever I publish PluginB locally to "~/.ivy2" using "publishLocal", then PluginA works. Though the dependency still resolves when I publish PluginB using "publishM2" to my local "~/.m2" the compile task for PluginA fails:
"object xxx is not a member of package yyy".
I have tried setting "publishMavenStyle" to both true and false and adjusting the resolver, but neither work.
Why does this happen and is there a way to get this to work when publishing in a maven style?

This was a mistake on my part. I added plugin incorrectly by using from
addSbtPlugin("com.xxyy" %% "PluginA" % "0.0.2" from "http://internal.repo.com")
Though the POM was found, so the resource was found, the correspoinding the jar was not found so the build would fail.
To fix this I added a resolver before adding the plugin
resolvers += "xxyy" at "http://internal.repo.com"
addSbtPlugin("com.xxyy" %% "PluginA" % "0.0.2")

Related

Gradle composite build throwing "Failed to get Gradle name for :module"

We've a collection of libraries developed by different teams and individually pushed to different Git repositories. To minimize the hassle of publishing to local Maven repositories or publishing snapshot versions, we've put together a root project to include all those libraries with Gradle's dependencies substitutions.
The structure is as follows:
- root (Git-root)
...
- modules
- module-a (Git-module-a)
- a-core
- build.gradle.kts
- a-data
- build.gradle.kts
- settings.gradle.kts
- module-b (Git-module-b)
- b-core
- build.gradle.kts
- b-data
- build.gradle.kts
- settings.gradle.kts
- settings.gradle.kts
- settings.gradle.kts
It's a bit confusing but module-a and module-b are included as git submodules inside a modules folder of the root project.
The Gradle files are as follow:
settings.gradle.kts
rootProject.name = "project-root"
includeBuild("modules")
modules/settings.gradle.kts
rootProject.name = "modules"
includeBuild("module-a")
includeBuild("module-b")
modules/module-a/settings.gradle.kts
rootProject.name = "module-a"
include("a-core")
include("a-data")
modules/module-b/settings.gradle.kts
rootProject.name = "module-b"
include("b-core")
include("b-data")
Just to illustrate the reason for this, lets say that the module-b:b-core depends on the module-a:a-core library.
The problem is that when I run this build it comes back with the following message:
Multiple build operations failed.
Failed to get Gradle name for :a-core
Failed to get Gradle name for :a-data
Failed to get Gradle name for :b-core
Failed to get Gradle name for :b-data
I don't know if what I'm trying to achieve is possible, but I couldn't find anything in the documentation. It looks like we're always very close to what we desire, but it's almost impossible to get there.
Do you guys know what I'm missing?
Error messsages come from AGP ModelBuilder.getBuildeName()
The reason is that most "bottom" projects in composite build ("a-core", "a-data", "b-core" and "b-data") have gradle.rootProject.parent set to "topmost" project ("project-root"), instead of their immediate parents ("module-a" and "module-b").
You can check it putting
println("${project.name} parent is ${project.gradle.parent?.rootProject?.name}")
in build.gradle.kts of all the projects and running sync
It whether Gradle or AGP problem, depending on is such a root behavior correct or a gradle bug.
I have created the issue about this case in gradle project. Will look what gradle team answer :)
As temporary solution add includeBuild("a-core") and other bottom level projects in modules/settings.gradle.kts. If still will got error, try add it to the topmost "project-root" settings.gradle.kts instead.

How to list all usages of a gradle module

I have a project with multiple modules (gradle modules) and some are depend on some others, for example :modules:backend:core has a project dependency on :modules:libraries:util:core and some others.
In my gitlab CI job I am able to tell when there are changes within some module (e.g. :modules:libraries:util:core) by listening to something like modules/libraries/util/core/**/*, and then triggering a build of that changed module.
Now the issue I have is how to figure out where this module is used, so that I can build the other side also (in this example I would need to build :modules:backend:core once :modules:libraries:util:core is changed).
Is there some way to list all usages of given module ?
https://github.com/vanniktech/gradle-dependency-graph-generator-plugin
You can use this plugin to create "your project module dependency graph"
./gradlew generateProjectDependencyGraph
or "whole dependency graph".
./gradlew generateDependencyGraph
You can find this file from app/build/reports/dependency-graph and app/build/reports/project-dependency-graph directory.
The folder includes three files: png, svg and dot.
In the dot file, you can get the module dependency.
":app" -> ":base" ["style"="dotted"]
":app" -> ":moduleA" ["style"="dotted"]
":moduleA" -> ":base" ["style"="dotted"]

How can I use a maven dependency package that has been published to GitLab with Gradle Kotlin?

(for reference I am using: IntelliJ as my IDE, Kotlin as the language, and Gradle Kotlin for build)
The package is successfully published to the GitLab Project's Package Registry, it is listed as a Maven. I can download and access the .pom/.jar/.module file in the GitLab Package Registry, additionally the link to the gitlab repo .pom file (as in the error message below) when clicked will download the .pom for the package (this has me super confused). However when I want to use it as a dependency in a different project it cannot be resolved. I am using an Access Token for authentication of this project. This is the error I am getting when I attempt to build / sync my gradle project:
Execution failed for task ':compileKotlin'.
> Error while evaluating property 'filteredArgumentsMap' of task ':compileKotlin'
> Could not resolve all files for configuration ':compileClasspath'.
> Could not find <group>:<projectName>:<version>.
Searched in the following locations:
- https://repo.maven.apache.org/maven2/com/example/<projectName>/<version>/<projectName>-<version>.pom
- https://gitlab.com/api/v4/projects/<packageID>/packages/maven/com/example/<projectName>/<version>/<projectName>-<version>.pom
Required by:
project :
Possible solution:
- Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html
(The group, projectName and version are all filled in with the those things for the project)
I did read the documentation provided as a possible solution as well as a large number of other sources with potential fixes or ways of setting it up, however I cannot seem to find how to solve or correctly import and use my maven repo. Other sources that I have already checked out / tried to use are:
https://www.jetbrains.com/help/space/publish-artifacts-from-a-gradle-project.html#publish-maven-artifacts-using-the-gradle-command-line-tool
https://gitlab-docs.creationline.com/ee/user/packages/maven_repository/
This is what I have in my gradle.build.kts of the package which is attempting to import / use the dependency (again of course packageName, group, version etc are filled in)
val gitLabAccessToken: String by project
repositories {
mavenCentral()
maven {
url = uri("https://gitlab.com/api/v4/projects/<projectID>/packages/maven")
name = "GitLab"
credentials(HttpHeaderCredentials::class) {
name = "Deploy Token"
value = gitLabAccessToken
}
authentication {
create<HttpHeaderAuthentication>("header")
}
}
}
I'm certain I am missing something silly and small, if there is anything more that is needed to get this working so that I can import and use it I will be happy to provide.
I knew it was something stupid, thank you #aSemy, It was that I had missed the dash between Deploy and Token (Deploy Token -> Deploy-Token) and that fixed it. I cannot believe I wasted hours over a dash.
The Solution to this Problem is adding the dash between Deploy and Token.
User #aSemy noticed this when I did not.

Git repository with Maven project as a Gradle source dependency

This article describes an interesting feature of Gradle 4.10+ called a source dependency:
https://blog.gradle.org/introducing-source-dependencies
It allows to use a Git (for example a GitHub) source code repository to build a dependency from it. However it seems like it supports only Gradle projects as source dependencies. Is it possible to use a Maven project as well and if it's possible, please show an example.
When I try to use this feature with Maven project Gradle tries to find the build.gradle file there anyway (I see it when run Gradle with the --info option) and fails with an error message like:
Git repository at https://github.com/something/something.git did not contain a project publishing the specified dependency.
The short answer
... is: "no".
Under the hood, source dependencies are composite builds. These needs to be Gradle projects as the external projects are sort of merged with the main project.
The long answer
... is: "yes but it is hard".
It is actually mentioned in the same blog post you linked to (emphasis mine):
Source dependencies make these use cases simpler to implement. Gradle takes care of automatically checking out the correct versions of dependencies, making sure the binaries are built when required. It does this everywhere that the build is run. The checked out project doesn’t even need to have an existing Gradle build. This example shows a Gradle build consuming two source dependencies that have no build system by injecting a Gradle build via plugins. The injected configuration could do anything a regular Gradle plugin can do, such as wrapping an existing CMake or Maven build.
Because it sounded like it wasn't the biggest thing in the world to create bridge between a Maven and a Gradle project in source dependencies, I gave it a shot. And I have it working except for transitive dependencies. You will basically need to do what is shown in the examples linked to above, but instead of building native libraries, you make a call-out to Maven (e.g. using a Maven plugin for Gradle).
However, the scripts I ended up with are complex enough that I would suggest you instead build the Maven project yourself, deploy it to a local Maven repository and then add that repository to the Gradle project.
<edit>
The loooooooong answer
Alright, so here is how to actually do it. The feature is poorly documented, and appears to be mostly targeted towards native projects (like C++ or Swift).
Root project setup
Take a normal Gradle project with the Java plugin applied. I did a "gradle init" in an empty folder. Assume that in this project, you are depending on a library called `` that you later want to include as a source dependency:
// [root]/build.gradle
dependencies {
implementation 'org.example:my-maven-project:1.1'
}
Note that the version number defined here must match a Git tag in the repository. This is the code revision that will be checkout out.
Then in the settings file, we define a source dependency mapping for it:
// [root]/settings.gradle
rootProject.name = 'my-project'
includeBuild('plugins') // [1]
sourceControl {
gitRepository("https://github.com/jitpack/maven-simple") { // [2]
producesModule("org.example:my-maven-project") // [3]
plugins {
id "external-maven-build" // [4]
}
}
}
[1]: This includes a Gradle project called plugins that will be explained later.
[2]: This is just an arbitrary Maven project that I found, which was relatively simple. Substitute with the actual repository you have.
[3]: This is the name of the Maven module (the same as in the dependency block) that we are defining a source build for
[4]: This defines a custom settings plugin called external-maven-build that is defined in the plugins project, which will be explained later.
Plugins project structure
Inside the root project, we define a new Gradle project. Again, you can use gradle init to initialize it as a Groovy (or whatever you like) project. Delete all generated sources and tests.
// [root]/plugins/settings.gradle
// Empty, but used to mark this as a stand-alone project (and not part of a multi-build)
// [root]/plugins/build.gradle
plugins {
id 'groovy'
id 'java-gradle-plugin' // [1]
}
repositories {
gradlePluginPortal() // [2]
}
dependencies {
implementation "gradle.plugin.com.github.dkorotych.gradle.maven.exec:gradle-maven-exec-plugin:2.2.1" // [3]
}
gradlePlugin {
plugins {
"external-maven-build" { // [4]
id = "external-maven-build"
implementationClass = "org.example.ExternalMavenBuilder"
}
}
}
[1]: In this project, we are defining a new Gradle plugin. This is a standard way to do that.
[2]: To invoke Maven, I am using another 3rd party plugin, so we need to add the Gradle plugin portal as a repository.
[3]: This is the plugin used to invoke Maven. I am not too familiar with it, and I don't know how production ready it is. One thing I noticed is that it does not model inputs and outputs, so there are no built-in support for up-to-date checking. But this can be added retrospectively.
[4]: This defines the custom plugin. Notice that it has the same ID as used in the settings file in the root project.
Plugin implementation class
Now comes the fun stuff. I chose to do it in Groovy, but it can be done in any supported JVM languages of cause.
The plugin structure is just like any other Gradle plugin. One thing to note is that it is a Settings plugin, whereas you normally do Project plugins. This is needed as it we are basically defining a Gradle project at run-time, which needs to be done as part of the initialization phase.
// [root]/plugins/src/main/groovy/org/example/ExternalMavenBuilder.groovy
package org.example
import com.github.dkorotych.gradle.maven.exec.MavenExec
import org.gradle.api.Plugin
import org.gradle.api.artifacts.ConfigurablePublishArtifact
import org.gradle.api.initialization.Settings
class ExternalMavenBuilder implements Plugin<Settings> {
void apply(Settings settings) {
settings.with {
rootProject.name = 'my-maven-project' // [1]
gradle.rootProject {
group = "org.example" //[2]
pluginManager.apply("base") // [3]
pluginManager.apply("com.github.dkorotych.gradle-maven-exec") // [4]
def mavenBuild = tasks.register("mavenBuild", MavenExec) {
goals('clean', 'package') // [5]
}
artifacts.add("default", file("$projectDir/target/maven-simple-0.2-SNAPSHOT.jar")) { ConfigurablePublishArtifact a ->
a.builtBy(mavenBuild) // [6]
}
}
}
}
}
[1]: Must match the Maven module name
[2]: Must match the Maven module group
[3]: Defines tasks like "build" and "clean"
[4]: The 3rd party plugin that makes it more easy to invoke Maven
[5]: For options, see https://github.com/dkorotych/gradle-maven-exec-plugin
[6]: Adds the Maven output as an artifact in the "default" configuration
Be aware that it does not model transitive dependencies, and it is never up-to-date due to missing inputs and outputs.
This is as far as I got with a few hours of playing around with it. I think it can be generalized into a generic plugin published to the Gradle portal. But I think I have too much on my plate as it is already. If anyone would like to continue on from here, you have my blessing :)

Gradle: how does one modify a dynamically created task?

I am building an Android Library project using the Android Gradle plugin (version 0.9.2) and it appears to have a bug (reported) in that while a "provided" dependency is correctly handled (not included) in the generated aar artifact, that dependency is incorrectly included in the generated debug test apk file.
It strikes me that a reasonable workaround is to remove the dependency jar file that is added by the :preDexDebugTest task as the last step for that task. But this task is dynamically generated so getting a handle to it is eluding me at the moment, hence the question.
In your app's build.gradle file, add
afterEvaluate {
def preDexDebugTest = tasks['preDexDebugTest']
// Do something with preDexDebugTest ...
}
That way it should be possible to operate on the preDexDebugTest task.

Resources