how to set archiveBaseName for local .m2 repository - maven

I'm trying to upgrade a dependency to a project that will ultimately become a dependency to my project. I've made the upgrade and I want to test it locally before I put it out on the repo to be used. I'm learning Gradle and a few Google searches showed me how to add the project to the settings.gradle file. But the dependency project uses aliases for their dependencies (see build.gradle below).
settings.gradle
include ':TransportImpl'
Changed to:
include ':TransportImpl', ':jeromq'
project(':jeromq').projectDir = new File("../zeromq/jeromq")
build.gradle
//project.ext.set("JEROMQ", 'THIRD-PARTY:jeromq:0.4.2')
project.ext.set("JEROMQ", ':jeromq')
If I uncomment the original line (shown commented above), because that apk is in the repo it gets recognized. I'm guessing that this only works for external libraries.
Other things I have tried:
//project.ext.set("JEROMQ", 'C:/Users/username/.m2/repository/THIRD_PARTY/jeromq/0.5.1-SNAPSHOT/jeromq-0.5.1-SNAPSHOT-jeromq.jar')
//project.ext.set("JEROMQ", 'C:\\Users\\username\\.m2\\repository\\THIRD_PARTY\\jeromq\\0.5.1\\jeromq-0.5.1-jeromq.jar')
//implementation filetree(dir: 'C:\\Users\\username\\.m2\\repository\\THIRD_PARTY\\jeromq\\0.5.1', include:['jeromq-0.5.1-jeromq.jar'])
Can anyone give me a tip on how I can assign a variable that points to the local repository and use that variable to set an archiveBaseName?
New Information:
gradle.build for our jeromq project
apply plugin : 'maven'
apply plugin : 'maven-publish'
// Top-level build file where you can add configuration options common to all sub-projects/modules.
ext {
// Nexus paths
nexusUrl='https://nexus.path'
Releases='/Private_Releases'
nexusUsername = project.findProperty("nexusUsername") ?: (System.getenv("NEXUS_USERNAME") ?: "user_name"
nexusPassword = project.findProperty("nexusPassword") ?: (System.getenv("NEXUS_PASSWORD") ?: "password")
// Project versions
jeromqVersion = "0.5.1-SNAPSHOT"
}
allprojects {
// Read only repositories for dependencies; this should never be used to publish
repositories {
mavenCentral()
jcenter()
}
}
The project that uses it as a dependency finds it using the following from its build.gradle file:
// Create aliases for dependencies
project.ext.set("EASY_MOCK", 'Test:easymock:3.5.1')
project.ext.set("OBJENESIS", 'Test:objenesis:2.6')
// **************** HERE ***************************
// THIRD-PARTY is configured to look on the nexus server
project.ext.set("JEROMQ", 'THIRD-PARTY:jeromq:0.4.2') ... or 0.5.1 or 0.5.1-SNAPSHOT ...
allprojects {
// Read only repositories for dependencies; this should never be used to publish
repositories {
mavenCentral()
mavenLocal()
// maven {
// // trying to add my local repo,
// // BUT this still does not change where THIRD-PARTY is pointing to
// url 'file://C:/Users/me/.m2/repository/THIRD_PARTY/jeromq/0.5.1-SNAPSHOT/jeromq-0.5.1-SNAPSHOT-jeromq.jar'
// }
maven {
name 'ReleasesName'
url "$nexusUrl$ReleasesName
}
}
maven {
name 'ReleasesNameSnapshots'
url "$nexusUrl$ReleasesNameSnapshots"
credentials {
username "${rootProject.ext.nexusReadOnlyUsername}"
password "${rootProject.ext.nexusReadOnlyPassword}"
}
}
jcenter {
url "https://jcenter.bintray.com/"
}
}
The only reason I need the alias for that dependency is because it is used in other places.

I'm not entirely sure what you are asking, but I think what you are trying is completely off.
The build you are trying to include is a Maven build, not a Gradle build, so it is unlikely you can simply treat it as it were a Gradle build.
And even if it were a Gradle build, including it like you did would not be the right way. How you tried it is for including multiple projects of a multi-project build, not including external libraries.
If it were a Gradle build, you would use a composite build, which effectively replaces a declared binary dependency by the build output of a "sub-build". But afair this only works cleanly with a Gradle build.
Why don't you simply mvn install your modified jeromq version, add mavenLocal() to your dependencies and depend on that just installed version? That would be the usual way for locally testing new Maven built dependencies.

Related

How to structure Gradle builds to allow building interdependent libraries

I built a small little test project to see if Gradle would solve this problem we currently have with Maven. We have 200 little libraries, all of them Maven projects, whenever you do a clean checkout, you have to mvn install each of them individually
To simulate such a scenario, i've created 4 modules:
root
- jvaas-gson
- jvaas-jackson
- jvaas-json
- jvaas-provider
Both jvaas-gson and jvaas-jackson depends on jvaas-json and jvaas-provider. jvaas-json only depends on jvaas-provider.
If some external application wants to include JSON capabilities, they should only have to include jvaas-gson or jvaas-jackson which uses an interface in jvaas-json (as i said, this is just experimenting with it, actual use-case would be to switch out email providers, payment providers etc only having to change a line in the Gradle build script)
In jvaas-provider i have a settings.gradle.kts
rootProject.name = "jvaas-provider"
and a build.gradle.kts
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
group = "io.jvaas"
version = "1.3.0"
plugins {
`maven-publish`
kotlin("jvm") version("1.3.10")
id("org.jetbrains.dokka") version "0.9.16"
}
repositories {
jcenter()
}
dependencies {
implementation(kotlin("stdlib", getKotlinPluginVersion()))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
}
publishing {
repositories {
mavenLocal()
}
}
In jvaas-json i'm trying to access one of the classes in jvaas-provider, so i've added it to the settings.gradle.kts
rootProject.name = "jvaas-json"
include("jvaas-provider")
and the build.gradle.kts
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
group = "io.jvaas"
version = "1.3.0"
plugins {
`maven-publish`
kotlin("jvm") version("1.3.10")
id("org.jetbrains.dokka") version "0.9.16"
}
repositories {
jcenter()
}
dependencies {
implementation(kotlin("stdlib", getKotlinPluginVersion()))
implementation(group = "io.jvaas", name = "jvaas-provider", version = "$version")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
}
Inside jvaas-json, i'm trying access one of the classes i created inside jvaas-provider, but it's not resolving.
Without having to manually install jvaas-provider in the local maven repo (mvn install), is it possible to use it as a dependency inside jvaas-json? If so, what should i be changing in my Gradle build scripts and Settings files?
Paul on the Kotlin Slack just answered my question just before i could hit Post on my StackOverflow question, so posting this as QA-style, maybe it'll help somebody else in the future.
Gradle has something called composite builds which allows you to include other projects / libraries inside your project without having to install them to a local maven repo first.
For the above example to work with composite builds, all i had to do was change something in the settings.gradle.kts file:
rootProject.name = "jvaas-json"
includeBuild("../jvaas-provider")

Maven local repository pointing wrong location

In my Android project I've defined mavenLocal() as repository in the root build.gradle file. However, if I publish changes to mavenLocal these will not be recognized by my project. However, if I define the location explicitly the changes ARE found, proving that the changes are correctly published. So it seems like my mavelLocal is not pointing to the default location?
buildscript {
repositories {
maven { url 'https://maven.fabric.io/public' }
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.google.gms:google-services:4.0.1'
classpath 'io.fabric.tools:gradle:1.25.1'
}
}
allprojects {
repositories {
mavenLocal() // picks wrong location?
// maven { "C:/Users/<username>/.m2/repository/" } // mavenLocal fallback
maven { url nexusRepositoryUrl }
maven { url nexusSnapshotsUrl }
maven { url nexusReleasesUrl }
mavenCentral()
google()
maven { url "https://maven.google.com" }
jcenter()
}
}
How can I solve this? I'm working with several people in one project, so the fallback method isn't a solution. Of course I could use a variable user path, but that's not what I'm looking for:
maven { url System.getenv('USER_PATH') + "/.m2/repository/" }
I do not have a settings.xml file in /.m2 directory (which can be used to override the default maven local repository location). I could create one to solve the problem, but it feels unnecessary to change the 'default' location to the 'default' location.
So I'm specifically looking for the reason why the default implementation of mavenLocal() doesn't seem to work.
EDIT
This following might be either related or an unrelated problem / question: I've got some trouble with old caches of repositories not being replaced by new ones, so that might also be an issue in this case. I don't have much knowledge about how gradle caching works, so if someone could provide me a clear solution for this, then it might also solve the problem. I'm able to clear cache sometimes, but the problem keeps returning, and I need a permanent fix for it.
Caches seem to be temporarily updated after a combination of any of the following (not order specific, and haven't been able to find out which are exactly required to fix my problem):
Android Studio > File > Invalidate caches & restart
Remove mavenLocal definition in build.gradle > sync project > add mavenLocal definition > sync project
Run > Clean Project > Rebuild project
Install & run project on device

Boilerplate project configuration in Gradle with Gradle Kotlin DSL

I'm currently trying to improve the way our projects share their configuration. We have lots of different multi-module gradle projects for all of our libraries and microservices (i.e. many git repos).
My main goals are:
To not have my Nexus repository config duplicated in every project (also, I can safely assume that the URL won't change)
To make my custom Gradle plugins (published to Nexus) available to every project with minimal boilerplate / duplication (they should be available to every project, and the only thing the project cares about is the version it's using)
No magic - it should be obvious to developers how everything is configured
My current solution is a custom gradle distribution with an init script that:
adds mavenLocal() and our Nexus repository to the project repos (very similar to the Gradle init script documentation example, except it adds repos as well as validating them)
configures an extension that allows our gradle plugins to be added to the buildscript classpath (using this workaround). It also adds our Nexus repo as a buildscript repo as that's where the plugins are hosted. We have quite a few plugins (built upon Netflix's excellent nebula plugins) for various boilerplate: standard project setup (kotlin setup, test setup, etc), releasing, publishing, documentation, etc and it means our project build.gradle files are pretty much just for dependencies.
Here is the init script (sanitised):
/**
* Gradle extension applied to all projects to allow automatic configuration of Corporate plugins.
*/
class CorporatePlugins {
public static final String NEXUS_URL = "https://example.com/repository/maven-public"
public static final String CORPORATE_PLUGINS = "com.example:corporate-gradle-plugins"
def buildscript
CorporatePlugins(buildscript) {
this.buildscript = buildscript
}
void version(String corporatePluginsVersion) {
buildscript.repositories {
maven {
url NEXUS_URL
}
}
buildscript.dependencies {
classpath "$CORPORATE_PLUGINS:$corporatePluginsVersion"
}
}
}
allprojects {
extensions.create('corporatePlugins', CorporatePlugins, buildscript)
}
apply plugin: CorporateInitPlugin
class CorporateInitPlugin implements Plugin<Gradle> {
void apply(Gradle gradle) {
gradle.allprojects { project ->
project.repositories {
all { ArtifactRepository repo ->
if (!(repo instanceof MavenArtifactRepository)) {
project.logger.warn "Non-maven repository ${repo.name} detected in project ${project.name}. What are you doing???"
} else if(repo.url.toString() == CorporatePlugins.NEXUS_URL || repo.name == "MavenLocal") {
// Nexus and local maven are good!
} else if (repo.name.startsWith("MavenLocal") && repo.url.toString().startsWith("file:")){
// Duplicate local maven - remove it!
project.logger.warn("Duplicate mavenLocal() repo detected in project ${project.name} - the corporate gradle distribution has already configured it, so you should remove this!")
remove repo
} else {
project.logger.warn "External repository ${repo.url} detected in project ${project.name}. You should only be using Nexus!"
}
}
mavenLocal()
// define Nexus repo for downloads
maven {
name "CorporateNexus"
url CorporatePlugins.NEXUS_URL
}
}
}
}
}
Then I configure each new project by adding the following to the root build.gradle file:
buildscript {
// makes our plugins (and any others in Nexus) available to all build scripts in the project
allprojects {
corporatePlugins.version "1.2.3"
}
}
allprojects {
// apply plugins relevant to all projects (other plugins are applied where required)
apply plugin: 'corporate.project'
group = 'com.example'
// allows quickly updating the wrapper for our custom distribution
task wrapper(type: Wrapper) {
distributionUrl = 'https://com.example/repository/maven-public/com/example/corporate-gradle/3.5/corporate-gradle-3.5.zip'
}
}
While this approach works, allows reproducible builds (unlike our previous setup which applied a build script from a URL - which at the time wasn't cacheable), and allows working offline, it does make it a little magical and I was wondering if I could do things better.
This was all triggered by reading a comment on Github by Gradle dev Stefan Oehme stating that a build should work without relying on an init script, i.e. init scripts should just be decorative and do things like the documented example - preventing unauthorised repos, etc.
My idea was to write some extension functions that would allow me to add our Nexus repo and plugins to a build in a way that looked like they were built into gradle (similar to the extension functions gradleScriptKotlin() and kotlin-dsl() provided by the Gradle Kotlin DSL.
So I created my extension functions in a kotlin gradle project:
package com.example
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
fun RepositoryHandler.corporateNexus(): MavenArtifactRepository {
return maven {
with(it) {
name = "Nexus"
setUrl("https://example.com/repository/maven-public")
}
}
}
fun DependencyHandler.corporatePlugins(version: String) : Any {
return "com.example:corporate-gradle-plugins:$version"
}
With the plan to use them in my project's build.gradle.kts as follows:
import com.example.corporateNexus
import com.example.corporatePlugins
buildscript {
repositories {
corporateNexus()
}
dependencies {
classpath(corporatePlugins(version = "1.2.3"))
}
}
However, Gradle was unable to see my functions when used in the buildscript block (unable to compile script). Using them in the normal project repos/dependencies worked fine though (they are visible and work as expected).
If this worked, I was hoping to bundle the jar into my custom distribution , meaning my init script could just do simple validation instead of hiding away the magical plugin and repo configuration. The extension functions wouldn't need to change, so it wouldn't require releasing a new Gradle distribution when plugins change.
What I tried:
adding my jar to the test project's buildscript classpath (i.e. buildscript.dependencies) - doesn't work (maybe this doesn't work by design as it doesn't seem right to be adding a dependency to buildscript that's referred to in the same block)
putting the functions in buildSrc (which works for normal project deps/repos but not buildscript, but is not a real solution as it just moves the boilerplate)
dropping the jar in the lib folder of the distribution
So my question really boils down to:
Is what I'm trying to achieve possible (is it possible to make custom classes/functions visible to the buildScript block)?
Is there a better approach to configuring a corporate Nexus repo and making custom plugins (published to Nexus) available across lots of separate projects (i.e. totally different codebases) with minimal boilerplate configuration?
If you want to benefit from all the Gradle Kotlin DSL goodness you should strive to apply all plugins using the plugins {} block. See https://github.com/gradle/kotlin-dsl/blob/master/doc/getting-started/Configuring-Plugins.md
You can manage plugin repositories and resolution strategies (e.g. their version) in your settings files. Starting with Gradle 4.4 this file can be written using the Kotlin DSL, aka settings.gradle.kts. See https://docs.gradle.org/4.4-rc-1/release-notes.html.
With this in mind you could then have a centralized Settings script plugin that sets things up and apply it in your builds settings.gradle.kts files:
// corporate-settings.gradle.kts
pluginManagement {
repositories {
maven {
name = "Corporate Nexus"
url = uri("https://example.com/repository/maven-public")
}
gradlePluginPortal()
}
}
and:
// settings.gradle.kts
apply(from = "https://url.to/corporate-settings.gradle.kts")
Then in your project build scripts you can simply request plugins from your corporate repository:
// build.gradle.kts
plugins {
id("my-corporate-plugin") version "1.2.3"
}
If you want your project build scripts in a multi-project build to not repeat the plugin version you can do so with Gradle 4.3 by declaring versions in your root project. Note that you also could set the versions in settings.gradle.kts using pluginManagement.resolutionStrategy if having all builds use the same plugins version is what you need.
Also note that for all this to work, your plugins must be published with their plugin marker artifact. This is easily done by using the java-gradle-plugin plugin.
I promised #eskatos that I would come back and give feedback on his answer - so here it is!
My final solution consists of:
Gradle 4.7 wrapper per project (pointed at a mirror of http://services.gradle.org/distributions setup in Nexus as a raw proxy repository, i.e. it's vanilla Gradle but downloaded via Nexus)
Custom Gradle plugins published to our Nexus repo along with plugin markers (generated by the Java Gradle Plugin Development Plugin)
Mirroring the Gradle Plugin Portal in our Nexus repo (i.e. a proxy repo pointing at https://plugins.gradle.org/m2)
A settings.gradle.kts file per project that configures our maven repo and gradle plugin portal mirror (both in Nexus) as plugin management repositories.
The settings.gradle.kts file contains the following:
pluginManagement {
repositories {
// local maven to facilitate easy testing of our plugins
mavenLocal()
// our plugins and their markers are now available via Nexus
maven {
name = "CorporateNexus"
url = uri("https://nexus.example.com/repository/maven-public")
}
// all external gradle plugins are now mirrored via Nexus
maven {
name = "Gradle Plugin Portal"
url = uri("https://nexus.example.com/repository/gradle-plugin-portal")
}
}
}
This means that all plugins and their dependencies are now proxied via Nexus, and Gradle will find our plugins by id as the plugin markers are published to Nexus as well. Having mavenLocal in there as well facilitates easy testing of our plugin changes locally.
Each project's root build.gradle.kts file then applies the plugins as follows:
plugins {
// plugin markers for our custom plugins allow us to apply our
// plugins by id as if they were hosted in gradle plugin portal
val corporatePluginsVersion = "1.2.3"
id("corporate-project") version corporatePluginsVersion
// 'apply false` means this plugin can be applied in a subproject
// without having to specify the version again
id("corporate-publishing") version corporatePluginsVersion apply false
// and so on...
}
And configures the gradle wrapper to use our mirrored distribution, which when combined with the above means that everything (gradle, plugins, dependencies) all come via Nexus):
tasks {
"wrapper"(Wrapper::class) {
distributionUrl = "https://nexus.example.com/repository/gradle-distributions/gradle-4.7-bin.zip"
}
}
I was hoping to avoid the boilerplate in the settings files using #eskatos's suggestion of applying a script from a remote URL in settings.gradle.kts. i.e.
apply { from("https://nexus.example.com/repository/maven-public/com/example/gradle/corporate-settings/1.2.3/corporate-settings-1.2.3.kts" }
I even managed to generate a templated script (published alongside our plugins) that:
configured the plugin repos (as in the above settings script)
used a resolution strategy to apply the version of the plugins associated with the script if the requested plugin id was one of our plugins and the version wasn't supplied (so you can just apply them by id)
However, even though it removed the boilerplate, it meant our builds were reliant on having a connection to our Nexus repo, as it seems that even though scripts applied from a URL are cached, Gradle does a HEAD request anyway to check for changes. It also made it annoying to test plugin changes locally, as I had to point it manually at the script in my local maven directory. With my current config, I can simply publish the plugins to maven local and update the version in my project.
I'm quite happy with the current setup - I think it's far more obvious to developers now how the plugins are applied. And it's made it far easier to upgrade Gradle and our plugins independently now that there's no dependency between the two (and no custom gradle distribution required).
I've been doing something like this in my build
buildscript {
project.apply {
from("${rootProject.projectDir}/sharedValues.gradle.kts")
}
val configureRepository: (Any) -> Unit by extra
configureRepository.invoke(repositories)
}
In my sharedValues.gradle.kts file I have code like this:
/**
* This method configures the repository handler to add all of the maven repos that your company relies upon.
* When trying to pull this method out of the [ExtraPropertiesExtension] use the following code:
*
* For Kotlin:
* ```kotlin
* val configureRepository : (Any) -> Unit by extra
* configureRepository.invoke(repositories)
* ```
* Any other casting will cause a compiler error.
*
* For Groovy:
* ```groovy
* def configureRepository = project.configureRepository
* configureRepository.invoke(repositories)
* ```
*
* #param repoHandler The RepositoryHandler to be configured with the company repositories.
*/
fun repositoryConfigurer(repoHandler : RepositoryHandler) {
repoHandler.apply {
// Do stuff here
}
}
var configureRepository : (RepositoryHandler) -> Unit by extra
configureRepository = this::repositoryConfigurer
I follow a similar patter for configuring the resolution strategy for plugins.
The nice thing about this pattern is that anything you configure in sharedValues.gradle.kts can also be used from your buildSrc project meaning that you can reuse repository declarations.
Updated:
You can apply another script from a URL, for example doing this:
apply {
// This was actually a plugin that I used at one point.
from("http://dl.bintray.com/shemnon/javafx-gradle/8.1.1/javafx.plugin")
}
Simply host your script that you want all your builds to share on some http server (would highly recommend using HTTPS so your build can't be targeted by a man in the middle attack).
The downside of this is that I don't think that scripts applied from urls aren't cached so they will be re-downloaded every time you run your build.
This may have been fixed by now, I'm not certain.
A solution offered to me by Stefan Oehme when I was having a similar problem was to vendor my own custom distribution of Gradle. According to him this is a common thing to do at large companies.
Simply create a custom fork of the gradle repo, add your companies special sauce to every project using this custom version of gradle.
I encountered a similar problem when common config is replicated in each and every project. Solved it by a custom gradle distribution with the common settings defined in init script.
Created a gradle plugin for preparing such custom distributions - custom-gradle-dist. It works perfectly for my projects, e.g. a build.gradle for a library project looks like this (this is a complete file):
dependencies {
compile 'org.springframework.kafka:spring-kafka'
}

Setting POM version for upload archive in Gradle build

I am using Gradle to build my own Android Libraries. I put this libraries into a gradle multi project.
Root Project
Lib A
Lib B
and so on.
I am trying to upload the created aar files into my local maven repo (.m2 directory)
I am using the following commands in my gradle file of the Root Project
uploadArchives {
repositories {
mavenDeployer {
repository(url: mavenLocal().url)
pom.groupId = rootProject.group
pom.artifactId = project.name
pom.version = project.version
}
}
}
everything works fine except the settings for the project.version. Where can I define the value for every single subproject?
Regards
Michael
Try checking out Gradle-Fury at https://github.com/gradle-fury/gradle-fury
Essentially, set the pom.version and various other settings in gradle.properties, add a few apply from throughout the build.gradle files and you're set. Definitely works with direct publication to sonatype oss/central.
These settings can be overridden on a per module basis by adding a gradle.properties file in each module that you need to override the parent's settings from.
declaimer: i'm one of the authors

How to add an additional repository at the top of the list for a particular subproject in Gradle

I have a multi project setup in Gradle, the root of which specifies a bunch of repositories that are shared by all subprojects:
subprojects {
repositories {
maven { url '...' }
mavenLocal()
mavenCentral()
}
}
How can I specify an additional repository for a particular subproject, and have it as the first in the list of repositories?
Why do I need to do this? I need to include an Android library in .aar format. It is present in .aar format in this additional repository, and in .apklib format in Maven Central. If Gradle hits Maven Central first and finds the .apklib it will bail out.
By postponing the addition of the repositories in the root build script, the repositories are added after the ones added in the subproject:
subprojects {
afterEvaluate {
repositories {
maven { url '...' }
mavenLocal()
mavenCentral()
}
}
}
i would recommend to use something for external&internal repository management.
i think that artifactory or nexus can handle remote/virtual repositories, and makes it possible to use only one repository in your project.
note: if repository order is relevant to you, that might mean that the artifact identifier (group:ident:version) can mean different artifacts - i think that violates some maven/etc guideline which can cause invalid caches behind gradle and cause mysterious problems

Resources