manifest issue for shadow plugin with gradle - gradle

I am using gradle v3.4 & shadow plugin v1.2.4. I am publishing a jar file to my local maven repo using the following inside my build.gradle file
mainClassName = 'some.thing.SomeClient'
jar {
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
// 'Main-Class': 'some.thing.SomeClient'
)
}
}
shadowJar {
baseName = 'commons-java'
classifier = null
version = '0.0.1-SNAPSHOT'
}
artifacts {
archives shadowJar
}
jar.dependsOn shadowJar
After publishing, I try to use this dependency inside another project as follows but get the error copied below when I run gradle build
/**
* jar/shadow jar (shadow jar extends jar task to create fat jar)
*/
jar {
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
//'Main-Class': 'some.thing.SomeClient'
)
}
}
shadowJar {
baseName = 'something-java-client'
classifier = null
version = '0.0.1-SNAPSHOT'
}
artifacts {
archives shadowJar
}
jar.dependsOn shadowJar
error
The value of a manifest attribute must not be null (Key=Main-Class).

The issue was caused by the mainClassName attribute in gradle.properties leading to the exception. Removing it from gradle.properties fixed the issue.

It also could be that the mainClassName is set after the plugin is applied, which seems to be a known issue. The quick and dirty solution is to set the property before applying the plugin, like this:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
}
}
plugins {
id "application"
}
mainClassName = 'some.thing.SomeClient'
apply plugin: 'com.github.johnrengelman.shadow'
// ...

Related

Gradle - uploadArchives generated snapshot is different from createJar

With my build.gradle file I am creating a jar file using a createJar task, which I later upload using an upload using an uploadArchives task.
The project is in groovy, structured like:
/src/main/groovy/package.name
/src/test/groovy
It's a test jar file, where the main class is in /src/test/groovy and it uses for processing classes from /src/main/groovy/package.name
The createJar task works and creates the jar that I need correctly.
So far so good.
The problem is when I run uploadArchives, which generates a snapshot.jar and it only contains the classes from /src/main/groovy/package.name
Why are the 2 jar files different ?
How can I make uploadArchives to upload the jar from createJar (or at least include the test classes as well & run it using the main class specified in createJar) ?
In build.gradle I have something like :
apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'maven'
sourceCompatibility = 1.5
repositories {
mavenCentral()
}
artifacts {
archives file: file('./build/libs/name-'+version+'.jar'), name: 'name', type: 'jar'
}
task createJar(type: Jar) {
classifier 'test'
from sourceSets.test.output
manifest {
attributes 'Implementation-Title': 'Gradle Jar File',
'Implementation-Version': version,
'Main-Class': 'MainClassName'
}
}
uploadArchives {
repositories {
mavenDeployer {
repository(url: "http://url/snapshotsFolder/") {
authentication(userName: "user", password: "pass")
}
}
}
}
To extend my comment, let me add an example:
jar {
classifier 'test'
from sourceSets.test.output
manifest {
attributes 'Implementation-Title': 'Gradle Jar File', 'Implementation-Version': version, 'Main-Class': 'MainClassName'
}
}
This will configure the task jar of type Jar, which is created by the java plugin.

Trying to import PersistentCookieJar library on Gradle project

I'm doing a project with Gradle and I'm currently using okhttp3 library in order to make requests to a webpage. A need a cookie handler so I'm trying to import this library on GitHub. I don't know why the dependency is not recognized on IntelliJ.
This is my build.gradle:
apply plugin: 'java'
apply plugin: 'application'
compileJava.options.encoding = 'UTF-8'
mainClassName = 'com.Main'
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = '1.0'
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'com.Main'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
allprojects {
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
}
}
dependencies {
compile 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
compile 'com.squareup.okhttp3:okhttp:3.10.0'
}
EDIT: On Netbeans I got the same thing, the library isn't recognized.
This library is an Android Library (see the library's gradle file and the aar extension), you cannot use it as a Pure Java Library because it refers to several Android classpaths.

Gradle : Assembling jar and getting "annot change dependencies of configuration ':compile' after it has been included in dependency resolution"

I have simple gradle project
apply plugin: 'java'
apply plugin: 'application'
repositories {
mavenCentral()
jcenter()
}
task copyToLib(type: Copy) {
into "$buildDir/libs/lib"
from configurations.runtime
}
jar {
dependsOn copyToLib
manifest {
attributes (
"Class-Path": configurations.compile.collect { it.getName() }.join(' ')
)
}
}
dependencies {
compile 'org.slf4j:slf4j-api:1.7.21'
compile 'org.antlr:antlr4:4.0'
}
I am getting an error telling me that
Cannot change dependencies of configuration ':compile' after it has been included in dependency resolution
whenever I try gradle jar command.
What's wrong? How do I fix this?
I have meet same problem,after some research i find that you need put code in dofirst
jar {
doFirst
{
dependsOn copyToLib
manifest {
attributes (
"Class-Path": configurations.compile.collect { it.getName() }.join(' ')
)
}
}
}

Publish Java artifact to Maven Local with Gradle

I am facing a problem when trying to install a generated jar into my local Maven Repository. The message error just show me 'task 'publish' is not found'
I am using this Gradle Script:
buildscript {
ext {
springBootVersion = '1.3.2.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
apply plugin: 'maven-publish'
jar {
baseName = 'mongofoundry'
version = '1.0.0'
}
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-data-mongodb')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
}
eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.9'
}
Do you have some idea Why I am reading that error message?
Thanks.
UPDATED
Running the command as #RaGe mentioned, solved the problem:
gradle publishToMavenLocal
The correct task to publish artifacts to local maven is
gradle publishToMavenLocal
Check Maven locally
For developing and testing it is useful to check library locally
gradle settings for apply plugin: 'com.android.library' not apply plugin: 'java-library'(where you can use it by default)
apply plugin: 'maven-publish'
//simple settings
project.afterEvaluate {
publishing {
publications {
library(MavenPublication) {
//setGroupId groupId
setGroupId "com.company"
//setArtifactId artifactId
setArtifactId "HelloWorld"
version "1.1"
artifact bundleDebugAar
/* add a dependency into generated .pom file
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', 'com.company')
dependencyNode.appendNode('artifactId', 'HelloWorld-core')
dependencyNode.appendNode('version', '1.1')
}
*/
}
}
}
}
to run it using command line or find this command in Gradle tab
./gradlew publishToMavenLocal
Location
artefact will be added into .m2 folder
//Unix
~/.m2
//Windows
C:\Users\<username>\.m2
//For example
/Users/alex/.m2/repository/<library_path>/<version>/<name>.<extension>
build folder
<project_path>/build/outputs/<extension>
other repositories location
~/.gradle/caches/modules-2/files-2.1/<group_id>/<artifact_id>/<version>/<id>
//For example
/Users/alex/.gradle/caches/modules-2/files-2.1/com.company/HelloWorld/1.1/c84ac8bc425dcae087c8abbc9ecdc27fafbb664a
To use it add mavenLocal(). It is important to place it as a first item for prioritise it, which is useful for internal dependencies
buildscript {
repositories {
mavenLocal()
}
allprojects {
repositories {
mavenLocal()
}
}
and
dependencies {
implementation 'com.company:HelloWorld:+'
}
*Also remember if you use a kind of shared.gradle file (via apply from) you should set path which is relevant to project.gradle (not shared.gradle)
[iOS CocoaPod local]
This is how I did it with Kotlin DSL (build.gradle.kts) for my Android library:
plugins {
id("maven-publish")
// OR simply
// `maven-publish`
// ...
}
publishing {
repositories {
// Local repository which we can first publish in it to check artifacts
maven {
name = "LocalTestRepo"
url = uri("file://${buildDir}/local-repository")
}
}
publications {
// ...
}
}
You can create all the publications with the following command:
./gradlew publishAllPublicationsToLocalTestRepoRepository
Or just a single publication with this command:
./gradlew publishReleasePublicationToLocalTestRepoRepository
See Gradle documentations: Maven Publish Plugin for more information.
Add maven plugin to your project and then:
gradle clean install
Here is an alternative skeleton for Gradle 7.5.1 with Java 17
build.gradle
plugins {
id 'org.gradle.java'
id 'org.gradle.maven-publish'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
mavenJava(MavenPublication) {
groupId = 'your-group'
artifactId = 'your-artifact'
version = "0.0.1"
from components.java
}
}
repositories {
mavenLocal()
}
}
Publishing
You can see more details on the publishing steps with --info
./gradlew --info publishToMavenLocal
Output Directory
Linux/macOS
/Users/<username>/.m2/repository/your-group/your-artifact/0.0.1
Windows
C:\Users\<username>\.m2\repository\your-group\your-artifact\0.0.1

Using Gradle to build a JAR with dependencies

I have a multiproject build and I put a task to build a fat JAR in one of the subprojects. I created the task similar to the one described in this cookbook.
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
manifest { attributes 'Main-Class': 'com.benmccann.gradle.test.WebServer' }
}
Running it results in the following error:
Cause: You can't change a
configuration which is not in
unresolved state!
I'm not sure what this error means. I also reported this on the Gradle JIRA in case it is a bug.
I posted a solution in JIRA against Gradle:
// Include dependent libraries in archive.
mainClassName = "com.company.application.Main"
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
Note that mainClassName must appear BEFORE jar {.
The answer by #felix almost brought me there. I had two issues:
With Gradle 1.5, the manifest tag was not recognised inside the fatJar task, so the Main-Class attribute could not directly be set
the jar had conflicting external META-INF files.
The following setup resolves this
jar {
manifest {
attributes(
'Main-Class': 'my.project.main',
)
}
}
task fatJar(type: Jar) {
manifest.from jar.manifest
classifier = 'all'
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
} {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
with jar
}
To add this to the standard assemble or build task, add:
artifacts {
archives fatJar
}
Edit: thanks to #mjaggard: in recent versions of Gradle, change configurations.runtime to configurations.runtimeClasspath
If you want the jar task to behave normally and also have an additional fatJar task, use the following:
task fatJar(type: Jar) {
classifier = 'all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
The important part is with jar. Without it, the classes of this project are not included.
Since use of compile to list dependencies is now deprecated and all should switch to implementation the solution to build a Jar with all dependencies should use the example from this website.
https://docs.gradle.org/current/userguide/working_with_files.html#sec:creating_uber_jar_example
Specifically this command:
configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it)
Here is full gradle section:
[1]: https://docs.gradle.org/current/userguide/working_with_files.html#sec:creating_uber_jar_example
task uberJar(type: Jar) {
archiveClassifier = 'uber'
from sourceSets.main.output
dependsOn configurations.runtimeClasspath
from {
configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
}}
This works fine for me.
My Main class:
package com.curso.online.gradle;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
public class Main {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class);
logger.debug("Starting demo");
String s = "Some Value";
if (!StringUtils.isEmpty(s)) {
System.out.println("Welcome ");
}
logger.debug("End of demo");
}
}
And it is the content of my file build.gradle:
apply plugin: 'java'
apply plugin: 'eclipse'
repositories {
mavenCentral()
}
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
compile 'org.apache.commons:commons-lang3:3.0'
compile 'log4j:log4j:1.2.16'
}
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'com.curso.online.gradle.Main'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
And I write the following in my console:
java -jar ProyectoEclipseTest-all.jar
And the output is great:
log4j:WARN No appenders could be found for logger (com.curso.online.gradle.Main)
.
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more in
fo.
Welcome
The answer from #ben almost works for me except that my dependencies are too big and I got the following error
Execution failed for task ':jar'.
> archive contains more than 65535 entries.
To build this archive, please enable the zip64 extension.
To fix this problem, I have to use the following code
mainClassName = "com.company.application.Main"
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
zip64 = true
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
To generate a fat JAR with a main executable class, avoiding problems with signed JARs, I suggest gradle-one-jar plugin. A simple plugin that uses the One-JAR project.
Easy to use:
apply plugin: 'gradle-one-jar'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.github.rholder:gradle-one-jar:1.0.4'
}
}
task myjar(type: OneJar) {
mainClass = 'com.benmccann.gradle.test.WebServer'
}
Based on the proposed solution by #blootsvoets, I edited my jar target this way :
jar {
manifest {
attributes('Main-Class': 'eu.tib.sre.Main')
}
// Include the classpath from the dependencies
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
// This help solve the issue with jar lunch
{
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
}
Simple sulution
jar {
manifest {
attributes 'Main-Class': 'cova2.Main'
}
doFirst {
from { configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } }
}
}
I use next script for Gradle 7.3.3. It resolves errors and exceptions that I was faced with when I was trying to implement solutions from this question.
jar {
manifest {
attributes(
"Main-Class": "path.to.main.Application",
)
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
For those who need to build more than one jar from the project.
Create a function in gradle:
void jarFactory(Jar jarTask, jarName, mainClass) {
jarTask.doFirst {
println 'Build jar ' + jarTask.name + + ' started'
}
jarTask.manifest {
attributes(
'Main-Class': mainClass
)
}
jarTask.classifier = 'all'
jarTask.baseName = jarName
jarTask.from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
{
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
jarTask.with jar
jarTask.doFirst {
println 'Build jar ' + jarTask.name + ' ended'
}
}
then call:
task makeMyJar(type: Jar) {
jarFactory(it, 'MyJar', 'org.company.MainClass')
}
Works on gradle 5.
Jar will be placed at ./build/libs.
I use task shadowJar by plugin .
com.github.jengelman.gradle.plugins:shadow:5.2.0
Usage just run ./gradlew app::shadowJar
result file will be at MyProject/app/build/libs/shadow.jar
top level build.gradle file :
apply plugin: 'kotlin'
buildscript {
ext.kotlin_version = '1.3.61'
repositories {
mavenLocal()
mavenCentral()
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
}
}
app module level build.gradle file
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
sourceCompatibility = 1.8
kapt {
generateStubs = true
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.seleniumhq.selenium:selenium-java:4.0.0-alpha-4"
shadow "org.seleniumhq.selenium:selenium-java:4.0.0-alpha-4"
implementation project(":module_remote")
shadow project(":module_remote")
}
jar {
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
manifest {
attributes(
'Main-Class': 'com.github.kolyall.TheApplication',
'Class-Path': configurations.compile.files.collect { "lib/$it.name" }.join(' ')
)
}
}
shadowJar {
baseName = 'shadow'
classifier = ''
archiveVersion = ''
mainClassName = 'com.github.kolyall.TheApplication'
mergeServiceFiles()
}
Excluding unwanted Manifest entries fixed the MainClass file not found error in a Gradle build jar file.
jar{
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
from {
-----
}
}
There is gradle plugin shadow jar with seamless setup.
plugins {
id "com.github.johnrengelman.shadow" version "5.0.0"
}
shadowJar {
mergeServiceFiles()
}
Please check about version compatibilities with your gradle version here:
https://github.com/johnrengelman/shadow#latest-test-compatibility
Gradle 6.3, Java library. The code from "jar task" adds the dependencies to the "build/libs/xyz.jar" when running "gradle build" task.
plugins {
id 'java-library'
}
jar {
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
There's something to keep in mind about this type of solution:
task fatJar(type: Jar) {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
It works so long as you're using "compile" dependencies. It doesn't work if you're using "implementation" dependencies.
Try "runtimeClasspath" if "compile" and "implementation" not working.
jar {
manifest {
attributes "Main-Class": "com.example.app"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
This is for Kotlin DSL (build.gradle.kts).
Method 1 (no need for application or other plugins)
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
// OR another notation
// manifest {
// attributes["Main-Class"] = "com.example.MyMainClass"
// }
}
If you use any external libraries, use below code. Copy library JARs in libs sub-directory of where you put your result JAR. Make sure your library JAR files do not contain space in their file name.
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
manifest.attributes["Class-Path"] = configurations
.runtimeClasspath
.get()
.joinToString(separator = " ") { file ->
"libs/${file.name}"
}
}
Note that Java requires us to use relative URLs for the Class-Path attribute. So, we cannot use the absolute path of Gradle dependencies (which is also prone to being changed and not available on other systems). If you want to use absolute paths, maybe this workaround will work.
Create the JAR with the following command:
./gradlew jar
The result JAR will be created in build/libs/ directory by default.
Method 2: Embedding libraries (if any) in the result JAR (fat or uber JAR)
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree) // OR .map { zipTree(it) }
from(dependencies)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
Creating the JAR is exactly the same as the previous method.
Method 3: Using the Shadow plugin (to create a fat or uber JAR)
plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these will be reflected for Shadow as well
tasks.jar {
manifest.attributes["Main-Class"] = "org.example.MainKt"
}
Create the JAR with this command:
./gradlew shadowJar
See Shadow documentations for more information about configuring the plugin.
Running the created JAR
java -jar my-artifact.jar
The above solutions were tested with:
Java 17
Gradle 7.1 (which uses Kotlin 1.4.31 for .kts build scripts)
See the official Gradle documentation for creating uber (fat) JARs.
For more information about manifests, see Oracle Java Documentation: Working with Manifest files.
Note that your resource files will be included in the JAR file automatically (assuming they were placed in /src/main/resources/ directory or any custom directory set as resources root in the build file). To access a resource file in your application, use this code (note the / at the start of names):
Kotlin
val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
// Alternative ways:
// val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
// val vegetables = MyClass::class.java.getResourceAsStream("/vegetables.txt").reader().readText()
// val vegetables = object{}.javaClass.getResourceAsStream("/vegetables.txt").reader().readText()
Java
var stream = MyClass.class.getResource("/vegetables.txt").openStream();
// OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
var reader = new BufferedReader(new InputStreamReader(stream));
var vegetables = reader.lines().collect(Collectors.joining("\n"));
If you're used to ant then you could try the same with Gradle too:
task bundlemyjava{
ant.jar(destfile: "build/cookmyjar.jar"){
fileset(dir:"path to your source", includes:'**/*.class,*.class', excludes:'if any')
}
}

Resources