How to combining multiple dependencies into a maven artifact? - maven

I have a dependency in a project of mine that does not declare its own required dependencies. How may I combine the dep and its deps into a single maven artifact, so that I can depend on this single artifact instead?
The first (and maybe? second) implementation dependency is the actual public API.
repositories {
maven {
url 'http://devsite.ctr-electronics.com/maven/release/'
}
}
dependencies {
implementation "com.ctre.phoenix:api-java:5.14.1"
implementation "com.ctre.phoenix:wpiapi-java:5.14.1"
implementation "com.ctre.phoenix:cci:5.14.1"
implementation "com.ctre.phoenix:canutils:5.14.1"
implementation "com.ctre.phoenix:platform-stub:5.14.1"
}

Use the java-library plugin (emphasis mine):
https://docs.gradle.org/current/userguide/java_library_plugin.html
The api configuration should be used to declare dependencies which are exported by the library API, whereas the implementation configuration should be used to declare dependencies which are internal to the component.
So change implementation to api for the dependencies you want exposed on the classpath.

Related

Is it possible to set a dependencySet entry's classifier using Spring's Gradle dependency management plugin

I have a project that uses the Spring Dependency Management Plugin in a multi-project Gradle build to specify consistent dependency versions for the projects.
Per the documentation, when multiple dependencies share the same group & version, a dependency set should be used:
When you want to provide dependency management for multiple modules with the same group and version you should use a dependency set. Using a dependency set removes the need to specify the same group and version multiple times, as shown in the following example:
dependencyManagement {
dependencies {
dependencySet(group:'org.slf4j', version: '1.7.7') {
entry 'slf4j-api'
entry 'slf4j-simple'
}
}
}
The project uses two dependencies that share a group and version, but one of the items also specifies a classifier:
dependencies {
annotationProcessor 'com.querydsl:querydsl-apt:4.2.2:general'
implementation 'com.querydsl:querydsl-mongodb:4.2.2'
}
If there's a way to use a dependencySet in this case, I haven't found it in the documentation or an online search:
dependencyManagement {
dependencies {
dependencySet(group:'com.querydsl', version: '4.2.2') {
entry 'querydsl-apt' // Can I specify that this uses the "general" classifier?
entry 'querydsl-mongodb'
}
}
}
So in short, is there a way to use a dependencySet when one of the dependencies requires a classifier, and what is the syntax for doing so?
Issue #67 for the Dependency Management Plugin requests this feature. The issue was closed by the maintainers with a status of "declined" by the plugin maintainers:
It's impossible to implement this while classifiers remain a second class citizen in Gradle. I don't expect that will change any time soon (things haven't improved in Gradle 3.0) so, with some regret, I'm going to close this.
Follow-up conversation in that issue suggest that the underlying Gradle issue is still not resolved in Gradle 6.
This means that not only can classifiers not be specified using dependencySet, they cannot be specified using dependency entries within the dependencyManagement section either:
dependencyManagement {
dependencies {
dependency 'com.querydsl:querydsl-apt:4.2.2:general' // INVALID
dependency 'com.querydsl:querydsl-mongodb:4.2.2'
}
}
What's worse, there is no warning or failure emitted, so the fact that it's not using the specified version with a classifier is not made clear to the user.
So to answer the question, the usage of classifiers in the dependencyManagement plugin is not supported since Gradle does not provide an API for the plugin to access the classifier. Therefore, dependency versions with classifiers cannot be specified in the dependencyManagement section either directly using dependency or as a group using dependencySet.

How can I access plugin-specific properties of a Gradle project using the Kotlin DSL?

Many Gradle plugins define project properties. For instance, the Base Plugin defines the properties archivesBaseName, distsDirName, and libsDirName.
It is my understanding that using Groovy, I'd simply access them as project.archivesBaseName and so on. But how can I access these properties using the typesafe Kotlin DSL?
Many Gradle plugins define project properties
This isn't entirely true. When you do project.someProperty, Gradle will do an exhaustive lookup as noted here.
Now let's assume a very basic Java project using the Kotlin DSL:
plugins {
java
}
repositories {
jcenter()
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.4.2")
}
Applying the java plugin applies the following key plugins:
Applies the JavaBasePlugin here
The JavaBasePlugin applies the Base plugin here
As noted in the docs here:
The Base Plugin only adds conventions related to the creation of archives, such as ZIPs, TARs and JARs
In order words, the Base plugin set defaults for all tasks that are of type AbstractArchiveTask as seen here.
As of Gradle 5.5.1, those subclasses (tasks) are:
So back to your original question:
how can I access these properties using the typesafe Kotlin DSL?
Simply retrieve a reference to the task you are trying to configure or reference:
val jar by tasks.getting(Jar::class)
println(jar.archiveBaseName.get())
val baseConvention = convention.getPlugin(BasePluginConvention::class)
println(baseConvention.libsDirName)
println(baseConvention.distsDirName)
The reason for the extra call for .get() is due to Lazy Configuration.
For any other third party plugin, you would need to either:
Grab a reference to their extension
Grab a reference to the tasks they create

Which Maven BOM determines a dependency's version in Gradle 5?

I am currently developing a Gradle 5 project that imports two different Maven BOMs. Therefore, I use the native Gradle syntax without the dependency management plugin. However, both BOMs may define different versions for the same dependency.
dependencies {
implementation platform ("org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}")
implementation platform ("com.organisation:xyz:${otherBomVersion}")
}
As far as I know in Maven the first BOM which defines a version for a given dependency determines it. In contrast, in the Gradle dependency management plugin the last BOM which defines a version for a given dependency determines it.
How is the order of imported BOMs handled in pure Gradle 5?
The order of BOMs, or wherever the dependency is declared, doesn't matter at all in Gradle. Unlike Maven, which uses a nearest first approach, Gradle takes all dependency information in consideration and selects the highest. The documentation states
Gradle will consider all requested versions, wherever they appear in the dependency graph. Out of these versions, it will select the highest one.
A practical example. The following declaration will always select 2.2.5.RELEASE of Spring Cloud Gateway defined by spring-cloud-dependencies BOM version Hoxton.SR8 no matter which platform() declaration is listed first:
dependencies {
implementation platform('org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8')
implementation platform('org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR6')
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
}
The dependency insight report may look like this:
> gradlew -q dependencyInsight --dependency spring-cloud-starter-gateway
org.springframework.cloud:spring-cloud-starter-gateway:2.2.5.RELEASE (by constraint)
variant "compile" [
org.gradle.status = release (not requested)
org.gradle.usage = java-api
org.gradle.libraryelements = jar (compatible with: classes)
org.gradle.category = library (not requested)
Requested attributes not found in the selected variant:
org.gradle.dependency.bundling = external
org.gradle.jvm.version = 8
]
org.springframework.cloud:spring-cloud-starter-gateway:2.2.5.RELEASE
\--- org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8
\--- compileClasspath
org.springframework.cloud:spring-cloud-starter-gateway -> 2.2.5.RELEASE
\--- compileClasspath
You may use enforcedPlatform() should you prefer versions from a specific BOM.
A simpler suggestion would be to use this mechanism described in the Spring Boot Gradle Plugin docs, whereby you can explicitly override versions picked by setting properties.
The example they give:
ext['slf4j.version'] = '1.7.20'
The full list of properties you can set can be found in the Spring Boot reference documentation.
In the end you would have an explicit way to override the defaults given by Spring Boot.
Ah, and now I see you are in fact not using that plugin, so this mechanism doesn't apply to you. Well, if you start using it, the above should work as intended and be predictable.

Gradle get what repository a dependency was resolved from (smart fat jar)

I have a use case where I need to create a smart fat jar. What, exactly does that mean?
Essentially I have different repositories which our application dependencies can be resolved from, some of them exist in a globally replicated file share (which is served as an ivy repository), while others exist in a private maven repository that is not productionized, and should not be used directly by applications in production.
I would like to create a fat jar in a smart way, such that:
If the dependency came from the private maven repository, it should be added to the fat jar.
If the dependency came from the file share, it points to the jar'directly in that file share (i.e. that fat jar's manifest adds that jar to the classpath.
Do any facilities exist for me to be able to distinguish where a dependency is resolved from?
For example:
repositories {
stableIvy()
unreliableMaven()
}
dependencies {
...
}
As of Gradle 5.2 there is no API that allows you to acces that information. However this is internally known by Gradle. You could file a feature request against the project.

The transitive reference don't works in gradle when use implementation project

I have a Core project where it has dependencies to other library.
Core build.gradle
dependencies {
implementation 'com.jakewharton:butterknife:9.0.0-rc2'
annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc2'
}
And I have other User project that reference to Core.
When the User project, does reference to Core library:
User build.gradle
dependencies {
implementation 'com.example: core: 1.0'
}
In User project I can acces at butterknife classes but when I reference at Core project
User build.gradle
dependecies{
implementation project(":core")
}
I can't access to butterknife classes (Compilation failed; see the compiler error output for details.)
Is there any way to reference core and be able to use the dependencies referenced by core?
This is the way implementation configuration works.
The plugin exposes two configurations that can be used to declare dependencies: api and implementation. The api configuration should be used to declare dependencies which are exported by the library API, whereas the implementation configuration should be used to declare dependencies which are internal to the component.
Dependencies appearing in the api configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers. Dependencies found in the implementation configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath.
Either declare Butter Knife as an api dependency, or depend on it explicitly in user/build.gradle.

Resources