I am trying to publish a Java library to maven central repository.
I have never done this before. This project is for testing purposes. When I figure out
how to properly publish a project, I will then publish an actual library.
My goal is to be able to add this project as a dependency for other projects.
I know I could include it as a .jar, but I want to learn about other ways of importing dependencies.
While running the task:
./gradlew publish
in my project root folder, I get the build error:
> Task :signMavenJavaPublication FAILED
Caching disabled for task ':signMavenJavaPublication' because:
Build cache is disabled
Task ':signMavenJavaPublication' is not up-to-date because:
Task has failed previously.
:signMavenJavaPublication (Thread[Execution worker for ':',5,main]) completed. Took 0.004 secs.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':signMavenJavaPublication'.
> Cannot perform signing task ':signMavenJavaPublication' because it has no configured signatory
EDIT:
I have made some progress. I will post this at the bottom of the question.
For the past four days, I have been trying to figure out why. I will post the build code further below, but first I will go through all the steps I followed to be able to publish to the central repository in the first place.
My gradle experience is limited, but I think I know the basics.
I have read various documentation on:
central.sonatype.org
Gradle
for how to publish / sign. I don't know exactly what i am doing or why.
Apply for a GroupID on Sonatype Jira. This issue/ticket is resolved. And I should be able to publish SNAPSHOT and release artifacts to s01.oss.sonatype.org. My GroupID is my github domain. So, as far as I know, this lets me publish my projects / libraries under "io.github.username".
Download and set up GnuPG:
At some point in the set up I was asked to create a GnuPG password (signing.password).
Don't remember when.
gpg --gen-key
Entering my name and email. Now I can type:
gpg -K
And I get the following (not actual values):
sec ed25519 2022-05-25 [SC] [expires: 2024-05-24]
****************************************
uid [ultimate] My Name <my-email#mail.com>
ssb cv25519 2022-05-25 [E] [expires: 2024-05-24]
So, the **************************************** is the password I am using. (the final 8 digits).
Now I export the key.(I think it creates my secret key right?):
gpg --export-secret-keys ******** > C:\users\username\secring.gpg
As far as I know, this could be any folder. As long as the folder corresponds to the:
signing.secretKeyRingFile=\users\username\secring.gpg
in the gradle.properties file.
Also, what would be the correct way to type this?
signing.secretKeyRingFile=\users\username\secring.gpg
signing.secretKeyRingFile=C:\users\username\secring.gpg
signing.secretKeyRingFile=C:\\users\\username\\secring.gpg
signing.secretKeyRingFile="C:\\users\\username\\secring.gpg"
(I think I have tried all the variations)
Then I need to send the public key to some key server. And there are some alternatives:
keyserver.ubuntu.com
keys.openpgp.org
pgp.mit.edu
I have tried to send it to all of them.
gpg --keyserver hkp://keyserver.ubuntu.com --send-keys ****************************************
And to can check if the server received the key:
gpg --keyserver hkp://keyserver.ubuntu.com --search-key 'my-email#mail.com'
And they got it. At least the server responds with the last 16 or so digits of the key.
So at this point I set up a simple java test project named "Storage". Pushing it to my github repo. under the same name.
And now we can get to the gradle files. Keep in mind, I'm not entirely sure if this is correct. If I left out something, or something is unnecessary. Please let me know.
build.gradle
plugins {
id 'java-library'
id 'signing'
id 'maven-publish'
}
group 'io.github.username'
version '0.0.1'
repositories {
mavenCentral()
maven { url "http://repo.maven.apache.org/maven2" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
dependencies {}
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
mavenJava(MavenPublication) {
groupId = 'io.github.username'
artifactId = 'storage'
version = '0.0.1'
from components.java
pom {
name = 'Storage'
description = 'Storage is an open-source Java library test'
url = 'https://github.com/username/Storage'
inceptionYear = '2022'
licenses {
license {
name = 'MIT License'
url = 'http://www.opensource.org/licenses/mit-license.php'
}
}
developers {
developer {
id = 'sonatype-username'
name = 'Full Name'
email = 'my-email#mail.com'
}
}
scm {
connection = 'scm:git:git://github.com/username/Storage.git'
developerConnection = 'scm:git:ssh://github.com/username/Storage.git'
url = 'https://github.com/username/Storage'
}
}
}
}
repositories {
maven {
name = "OSSRH"
url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
credentials {
username = project.properties["ossrhUsername"]
password = project.properties["ossrhPassword"]
}
}
}
}
signing {
sign publishing.publications.mavenJava
}
javadoc {
if(JavaVersion.current().isJava9Compatible()) {
options.addBooleanOption('html5', true)
}
}
gradle-wrapper.properties
# auto-generated
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
# Sonatype variables
ossrhUsername=username
ossrhPassword=password
# GnuPG
signing.keyId=********
singing.password=GnpPassword
signing.secretKeyRingFile=\Users\username\secring.gpg
settings.gradle
rootProject.name = 'Storage'
That should be it. But I can include versions of various software/tools:
OS Windows 10
GnuPG 2.3.6
Gradle 7.4.2
Java 13.0.1
Groovy 3.0.9
PROGRESS:
I got the project published to Nexus repository manager.
So I know for a fact that build.gradle can access my gradle.properties file and can read it's content.
I got the publish task to work by excluding the signing part.
I have sent my key (two keys now) to both:
keyserver.ubuntu.com
pgp.mit.edu
keys.openpgp.org does not seem to work. I get gpg: keyserver send failed: Certificate expired
When i send a key to a server I use the FULL KEY ID.
I can query a server to see if they in fact received the keys: gpg --keyserver hkp://keyserver.ubuntu.com --search-key 'my-email#mail.com'
And both servers have received 2 keys:
(1) My Name <my-email#mail.com>
263 bit EDDSA key ****************, created: 2022-05-28
(2) My Name <my-email#mail.com>
263 bit EDDSA key ****************, created: 2022-05-25
Keys 1-2 of 2 for "my-email#mail.com". Enter number(s), N)ext, or Q)uit >
The **************** is in fact the last numerals of my keys. It has to be right. Should I only send the last 8 numerals of the key to a server?
And the singing.password I use is the same choose when creating a key.
But for some esoteric reason. The signing still does not work. Is there no way to pinpoint the EXACT reason for failure?
Please, take a look at my build.gradle. Is there an alternative way to publishing / signing i could try instead?
Does the name of the secret key file matter? secring.gpg
The error piece:
> Cannot perform signing task ':signMavenJavaPublication' because it has no configured signatory
basically says that your signing task is not able to figure out the signature info it needs to execute. Most likely it cannot find your gradle-wrapper.properties file.
What you can do is to try to put them in the main gradle.properties file and see how it goes.
As for the folders: Gradle relies on java.io.File for its path related operations which means it should be able to handle forward slashes as well.
A good approach, especially when you are setting up a new configuration and you see it failing for path reasons, is to put everything straight into the folder where you're sure Gradle (or any other system/build) is able to see it. In your case that would be either gradle home or same folder where your build.gradle is. Then, after you get everything to work, you can reorganize and put configs however you like.
EDIT:
It is also always a good idea to find a way how to printout something while your build / script / whatever is executing. So, for Gradle you can use println to print a property name:
task printSigning {
println(project.findProperty('signing').secretKeyRingFile)
}
Or you can print a current directory:
task currentDir {
println file('.')
}
I hope that this helps a bit.
Below questions might contain some more details and give you a hint too:
https://stackoverflow.com/a/67115705/177154
https://stackoverflow.com/a/68505768/177154
Related
I can successfully add a generated openapi client to my project via source sets. But then I have to copy dependencies into the main build-gradle, resolve conflicts -> I think it would be a better design to have the client as a subproject with its own build.gradle.
So I add include = 'build:openapi-java-client' to my settings.gradle and compile project(':build:openapi-java-client') to my dependencies. So that I have the following files:
build.gradle:
plugins {
id 'java'
id 'application'
id "org.openapi.generator" version "4.3.1"
}
repositories {
jcenter()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/specs/petstore.yaml".toString()
outputDir = "$buildDir/openapi-java-client".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
configOptions = [
dateLibrary: "java8"
]
}
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
testImplementation 'junit:junit:4.13'
compile project(':build:openapi-java-client')
}
application {
mainClassName = 'a.aa.App'
}
and settings.gradle:
rootProject.name = 'simple-java-app'
include = 'build:openapi-java-client'
I execute openApiGenerate in advance, after adding it as a subproject, I do Gradle -> Refresh Gradle Project and Refresh.
Eclipse then shows me a problem:
Could not run phased build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-6.5.1-bin.zip'.
Settings file 'C:\...\simple-java-app\settings.gradle' line: 11
A problem occurred evaluating settings 'simple-java-app'.
Could not set unknown property 'include' for settings 'simple-java-app' of type org.gradle.initialization.DefaultSettings.
I don't know where to go from here, addressing subprojects in subfolders worked just fine when I worked through https://guides.gradle.org/creating-multi-project-builds/ and put greeting-library in a subfolder.
You are trying to make build/ a project when that directory specifically is not meant to be a project directory. It's Gradle default build directory and likely 99% of other plugins and other Gradle plugins.
Simply change output directory to something else other than build/:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$rootDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
Then include the project in your build with the correct syntax:
// settings.gradle
include("openapi-java-client")
However, using the org.openapi.generator seems to generate an invalid build.gradle since I get the following error:
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle' line: 23
* What went wrong:
Could not compile build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle'.
> startup failed:
build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle': 23: unexpected char: '\' # line 23, column 35.
main.java.srcDirs = ['src/main\java']
This obviously won't work how you wanted it to since it appears to be an issue with the Gradle plugin itself. If you just need to include the generate code in your project, then just include the generated Java code as part of your main Java source:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
But with this approach, you'll run into missing imports/dependencies. It doesn't appear this plugin offers the ability to just generate the models/POJOs only, so updating the library property to native and including some missing dependencies manually, it all works:
plugins {
java
id("org.openapi.generator") version "5.0.0-beta"
}
repositories {
mavenCentral()
}
group = "io.mateo.test"
dependencies {
implementation(platform("com.fasterxml.jackson:jackson-bom:2.11.1"))
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("org.openapitools:jackson-databind-nullable:0.2.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("io.swagger:swagger-core:1.6.2")
}
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
library.set("native")
configOptions.put("dateLibrary", "java8")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
You cannot configure it alike this, because build most certainly is an output directory, which would create a circular reference. Better try to add a new module and add that generator plugin into that module. If you can configure another module as outputDir, this could be referenced.
Even if the plugin resides in the root project, the destination needs to be a module.
The point is, that the root project always executes, opposite to module configutions.
I’ve just answered a very similar question. While my answer there is not perfect, I would personally still prefer the approach suggested there – and kind of repeated here:
Suggested Approach
I would keep the builds of the modules that depend on the generated API completely separate from the build that generates the API. The only connection between such builds should be a dependency declaration. That means, you’ll have to manually make sure to build the API generating project first and only build the dependent projects afterwards.
By default, this would mean to also publish the API module before the dependent projects can be built. An alternative to this default would be Gradle composite builds – for example, to allow you to test a newly generated API locally first before publishing it. However, before creating/running the composite build, you would have to manually run the API generating build each time that the OpenAPI document changes.
Example
Let’s say you have project A depending on the generated API. Its Gradle build would contain something like this:
dependencies {
implementation 'com.example:api:1.0'
}
Of course, the simple-java-app build described in the question would have to be adapted to produce a module with these coordinates:
openApiGenerate {
// …
groupId = "com.example"
id = "api"
version = "1.0"
}
Before running A’s build, you’d first have to run
./gradlew openApiGenerate from your simple-java-app project.
./gradlew publish from the simple-java-app/build/openapi-java-client/ directory.
Then A’s build could fetch the published dependency from the publishing repository.
Alternatively, you could drop step 2 locally and run A’s build with an additional Gradle CLI option:
./gradlew --include-build $path_to/simple-java-app/build/openapi-java-client/ …
I have a Gradle project which creates a zip artifact. I define the artifact via artifacts.add('default', zipTask). I add this project to another project via includeBuild and use the zip as dependency (dependencies { myConfiguration 'org.example:testA:+#zip' }).
So far so good. It works.
The problem starts when I add the plugin java to the first project. For some reason it prevents Gradle from finding the zip artifact.
The error is:
Execution failed for task ':doubleZipTask'.
> Could not resolve all files for configuration ':myConfiguration'.
> Could not find testA.zip (project :testA).
Why? How to fix it?
Complete example:
Project testA
settings.gradle:
rootProject.name = 'testA'
build.gradle:
plugins {
id 'base'
// Uncomment the line below to break the zip artifact
//id 'java'
}
group = 'org.test'
version = '0.0.0.1_test'
task zipTask(type: Zip) {
from './settings.gradle' // just so the zip isn't empty
}
artifacts.add('default', zipTask)
Project testB
settings.gradle:
rootProject.name = 'testB'
// This line may be commented out in some cases and then the artifact should be downloaded from Maven repository.
// For this question it should be always uncommented, though.
includeBuild('../testA')
build.gradle:
plugins {
id 'base'
}
configurations {
myConfiguration
}
dependencies {
myConfiguration 'org.test:testA:0.0.0.+#zip'
}
task doubleZipTask(type: Zip) {
from configurations.myConfiguration
}
Update 1
I've added some diagnostic code at the end of the build.grade:
configurations.default.allArtifacts.each() {
println it.toString() + ' -> name: ' + it.getName() + ', extension: ' + it.getExtension()
}
and in the version with java plugin it prints:
ArchivePublishArtifact_Decorated testA:zip:zip: -> name: testA, extension: zip
org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact#2c6aaa5 -> name: testA, extension: jar
However, I'm not sure if an additional artifact can break something.
It doesn't seem to be a problem when I add a second artifact myself.
Update 2
Maybe the zip file isn't the best representation of my intentions. After all, I could build java related files in one project and zip them in another.
However, the problem also applies to war files. (War plugin internally uses the Java plugin so it cannot be run separately.)
The issue seems to be a bug in Gradle where the composite build and reference to artifacts are broken.
Some discussion is here: https://discuss.gradle.org/t/composite-build-cant-use-included-artifact-in-buildsrc-build-gradle/24978
Bug report: https://github.com/gradle/gradle/issues/3768
A workaround would be to move the artifact dependency to a task dependency:
plugins {
id 'base'
}
configurations {
myConfiguration
}
dependencies {
}
task doubleZipTask(type: Zip) {
dependsOn gradle.includedBuild('testA').task(':zipTask')
from configurations.myConfiguration
}
The following setup should work with Gradle 5.6 (when using another attribute it will likely also work with previous versions). It mostly corresponds to your original setup with the exception of the changes indicated by XXX.
Project testA
settings.gradle:
rootProject.name = 'testA'
build.gradle:
plugins {
id 'base'
// Uncomment the line below to break the zip artifact
//id 'java'
}
group = 'org.test'
version = '0.0.0.1_test'
task zipTask(type: Zip) {
from './settings.gradle' // just so the zip isn't empty
}
// XXX added an attribute to the configuration
configurations.default.attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements, 'my-zipped-lib'))
}
artifacts.add('default', zipTask)
Project testB
settings.gradle:
rootProject.name = 'testB'
// This line may be commented out in some cases and then the artifact should be downloaded from Maven repository.
// For this question it should be always uncommented, though.
includeBuild('../testA')
build.gradle:
plugins {
id 'base'
}
configurations {
// XXX added the same attribute as in the testA project
myConfiguration {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements, 'my-zipped-lib'))
}
}
}
dependencies {
myConfiguration 'org.test:testA:0.0.0.+#zip'
}
task doubleZipTask(type: Zip) {
from configurations.myConfiguration
}
I have tested this setup both with and without the java plugin. I’ve also tested publishing to a Maven repository and letting testB take its dependency from there instead of from the included build of testA. Adding an additional dependency from testB on the JAR artifact of testA (myConfiguration 'org.test:testA:0.0.0.+#jar') worked, too.
Some explanation on what (I believe) is going on: Gradle needs a way of determining automatically which local component/artifact in testA it can use for substituting the external dependency of testB.
Without applying the java plugin, there is only a single component/artifact and my guess is that Gradle then just selects that single one without further ado.
As you saw, by applying the java plugin, another artifact is added to testA. Now which one should Gradle take? One would expect that it would look at the file extension specified on the dependency in testB but that doesn’t seem to be the case. It seems that Gradle isn’t actually working at the artifacts level when substituting dependencies but rather at the module/component level. You might say we only have one component with two artifacts, so selecting the one component should be straightforward. But it seems that we actually have two variants of the same component and Gradle wants to select one of these variants. In your own testB setup, there is no clue given that would tell Gradle which variant to select; so it fails (with an admittedly bad/misleading error message). In my changed testB setup I provide the clue: I tell Gradle that we want the variant that has a specific attribute with the value my-zipped-lib. Since I’ve added the same attribute on the published configuration of testA, Gradle is now able to select the right variant (because there is only one that has the required attribute). The file extension on the dependency is still relevant in a second step: once Gradle has selected the component variant, it still needs to select the right artifact – but only then.
Note that we’re actually working on the rim of what is supported with composite builds today. See also Gradle issue #2529 which states that “projects that publish non-jar artifacts” were not terribly well supported. When I first saw your question I honestly thought we’d be out of luck here … but it seems there’s a way to again step a little closer to the rim ;-)
In the comments, the question came up why adding multiple custom artifacts works while applying the java plugin breaks the build. As I’ve tried to explain above, it’s not a question of multiple artifacts but of multiple component variants. IIUIC, these variants originate from different attributes on configurations. When you don’t add such attributes, then you will not have different components variants. However, the Java plugin does add such attributes which consequently leads to different component variants in your project(/component). If you’re interested, you can see the different attributes by adding something like the following to your build.gradle:
configurations.each { conf ->
println "Attributes of $conf:"
conf.attributes.keySet().each { attr ->
println "\t$attr -> ${conf.attributes.getAttribute(attr)}"
}
}
Now where and when to add which attributes? That depends on your setup. I wouldn’t blindly add attributes to all configurations in the hope that that’ll solve things magically. While it might work (depending on your setup), it’d certainly not be clean. If your project setup is as complex and/or special as your question suggests, then it’ll probably make sense to think more deeply about which configurations you need and what attributes they should carry. The first sections of the Gradle documentation page that I have linked above are probably a good starting point if you are not intricately familiar with these nuances of configurations in Gradle, yet. And yes, I agree that such logic would best live in a Gradle plugin.
I'm using the 'maven-publish' plugin by gGradle and it putting a suffix after actual version, which I want to avoid. because in the next step of my CI it trying to download the .jar and the curl command is downloading nothing.
I can connect to my nexus and upload via ./gradlew publish Optional<VERSION=0.0.1> but the plugin (I think) is added a timestamp, is looking like this:
a/b/c/ARTIFACT-NAME/0.0.1-SNAPSHOT/ARTIFACT-NAME-0.0.1-20190114.134142-8.jar
How can I disable the feature of timestamp in the plugin?
That's my publishing task:
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
repositories {
maven {
if (project.version.endsWith('-SNAPSHOT')) {
url deployNexusSnapshotUrl
} else {
url deployNexusReleaseUrl
}
credentials {
username = deployNexusUsername
password = deployNexusPassword
}
}
}
}
That's because you are publishing a SNAPSHOT version. This timestamp is a feature that allows to distinguish between different snapshot builds e.g. to use them for limited period of time and purge them later. It won't happen when you release a proper artifact version that doesn't use -SNAPSHOT suffix.
For the sake of CI build reproducibility you should never use SNAPSHOT dependencies when you are building software on CI. SNAPSHOT versions can be overridden especially if you disable the timestamp in the version.
What if there were no code changes in your project, the CI build was green but on the next day someone overwrites a SNAPSHOT dependency in repository and the build is now red? What if on the next day you have to release a hotfix to resolve urgent production issue but instead you have to focus on a problem introduced by new SNAPSHOT dependency.
You are trying to solve the wrong problem. SNAPSHOT is intended for local development only.
I would like to be a able to get Eclipse to ignore one Gradle project, and instead use a pre-built version of it.
Background
I have a project "parser" written in Scala, and a dozen others written in Java. The weakest link in my tool-set is Scala IDE. I use this plugin to edit & compile Scala code, but unfortunately it breaks the Java (JDT) tooling quite badly in mixed-language projects*.
Specifically: Call-hierarchy is missing results, searches crash and so on. Also Scala IDE appears to have lost funding and the issues sound fairly fundamental, so I'm not holding my breath for these issues to be fixed.
With Maven (m2e) I had a workaround I was quite happy with:
Build as a .jar put into my local .m2 repository:
cd parser; mvn install
In Eclipse, close the "parser" project
"Like magic", m2e simply picked up the most recent 'installed' .jar and used it in place of the closed project.
An awesome answer would be how to get Gradle to do that!
However all I wish for is any solution that meets these...
Requirements
That I can open Project parser when necessary (which is seldom),
to edit and build changes via the Gradle command-line.
I will close it when done.
Other projects use the built .jar from my local .m2 repo.
(It's fine if they always do so.)
The change must not affect others who don't use Eclipse
(ideally) the change can be used by other Eclipse users
Approaches
A similar question had this good answer by #lance-java with a number of general suggestions. I think I can rule out these ideas:
composite build support / multiple repos. Other team members wouldn't think it makes sense to build this project separately, as it is quite closely integrated with the others.
dependency substitution rules - doesn't appear to meet requirement 3.
Something along the lines of lance-java's idea #4 sounds viable. Paraphrasing...
"use the eclipse plugin [in conjunction with] Buildship, e.g. using the whenMerged hook to tweak the generated .classpath [of all the Java projects]."
UPDATE: [18 Apr]: I had hit a brick wall in this approach. Buildship was not putting the built .jar onto the runtime classpath. (UPDATE 2: Now resolved - see my answer.)
Questions
The main question: How can I structure a solution to this, that will actually work & avoid any major pitfalls?
Note that the project itself has a few dependencies, specifically:
dependencies {
compile 'org.scala-lang:scala-library:2.12.4'
compileOnly 'com.google.code.findbugs:jsr305:1.3.9'
antlr 'org.antlr:antlr4:4.5.3'
}
So a sub-question may be: How to pull these in into the other projects without duplicating the definition? (If that doesn't work automatically.)
So the solution was a bit involved. After adding 'maven-publish' to create the library, I then implemented the following to force Eclipse to use the prebuilt library:
subprojects {
// Additional configuration to manipulate the Eclipse classpaths
configurations {
parserSubstitution
}
dependencies {
parserSubstitution module("com.example:parser:${project.version}")
}
apply plugin: 'eclipse'
eclipse {
classpath {
plusConfigurations += [ configurations.pseLangSubstitution ]
file {
whenMerged { cp ->
// Get Gradle to add the depedency upon
// parser-xxx.jar via 'plusConfigurations' above.
// Then this here if we have a dependency on Project(':parser')
// - If so, remove it (completing the project -> jar substitution).
// - If not, remove the .jar dependency: it wasn't needed.
def usesParser = entries.removeAll {
it instanceof ProjectDependency && it.path.startsWith('/parser')
}
def parserJar =
cp.entries.find { it instanceof Library && it.path.contains('parser-') }
if (usesParser) {
// This trick stops Buildship deleting it from the runtime classpath
parserJar ?. entryAttributes ?. remove("gradle_used_by_scope")
} else {
cp.entries.remove { parserJar }
}
}
}
}
So there are 2 parts to this:
Using 'plusConfigurations' felt a bit round-about. I ended up doing this because I could not see how to construct class Library classpath entries directly. However it could well be that this is required to implement the 'transient dependencies' correctly anyway. (See the end of the question.)
The trick to stop Buildship removing the .jar from the runtime classpath (thus deviating from a Gradle command-line launch) was provided to me by a Gradle developer in this discussion.
Usage
The solution works just as I hoped. Every time some code in this library is modified, I execute the following task of mine on the command line (which also does some other code & resource generation steps, in addition to building the parser jar):
./gradlew generateEclipse
Then in Eclipse I press keyboard shortcuts for "Gradle -> Refresh Gradle Projects", Build.
And harmony is restored. :-)
Navigating to the (prebuilt) source of parser works.
If I need to edit the source, I can open the parser project and edit it. Scala-IDE still does a good job for this.
When I'm done I execute the command, close the project and my Java tools are happy.
In parser project
You shoud use the maven-publish plugin with the publishToMavenLocal task
apply plugin: 'maven-publish'
group = 'your.company'
version = '1.0.0'
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
pom.withXml {
def root = asNode()
root.appendNode('name', 'Your parser project name')
root.appendNode('description', 'Your parser project description')
}
}
}
}
Everytime you make a modification, just change the version number if necessary and go with gradle publishToMavenLocal
In other java project using parser
Just use parser as a regular dependency :
repositories {
mavenLocal()
...
}
compile 'your.company:parser:1.0.0'
If my understanding of your situation is good, it should do the trick.
I'm looking at the webpush-java code. I run into a problem attempting to build the project using gradle. (I'm a gradle newbie).
:signArchives FAILED
FAILURE: Build failed with an exception.
What went wrong: Execution failed for task ':signArchives'.
Cannot perform signing task ':signArchives' because it has no
configured signatory
I guess that I need to configure a signatory. How do I do that?
Quoting the Signing plugin documentation you should be able to resolve the error when you provide the expected GPG variables in the gradle.properties file in your HOME directory:
# File location: ~/.gradle/gradle.properties - see https://docs.gradle.org/current/userguide/directory_layout.html
signing.keyId=24875D73
signing.password=secret
signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg
Gradle resp. the Signing plugin will automatically pick them up in the build process.
Another option that does not require a special command-line option is to add the following in your build.gradle:
signing {
setRequired {
// signing is only required if the artifacts are to be published
gradle.taskGraph.allTasks.any { it.equals( PublishToMavenRepository) }
}
....
See e.g. https://github.com/Vampire/command-framework/blob/master/buildSrc/src/main/kotlin/net/kautler/publishing.gradle.kts#L157
I got the same problem and it was because of several things:
I didn't distribute the gpg key to any server;
I was using inMemory to sign and I shouldn't
the link of the complete answer with build.gradle file and gradle.properties file: https://stackoverflow.com/a/68505768/7937498
Found solution here https://github.com/jaegertracing/jaeger-client-java/issues/202
Use the below command.
./gradlew assemble -x signArchives