I am trying to build a gradle plugin, which does the following:
As part of one its tasks, it creates a new configuration
It adds a DefaultExternalModuleDependency to this configuration - more specifically, it constructs a dependency to the application server zip file (available on Nexus). This information can be overridden by the invoking project as well.
Tries to resolve this newly added dependency and then unpacks the file to a local folder
All of this was working well when I had the details hard coded in a build file, but it looks like adding dependencies as part of a task are not treated the same way as having that information available at the parsing time.
So my question is, how do I get the project to reload the configurations / dependencies?
The code looks like the following:
#TaskAction
void installAppserver() {
Dependency dependency = new DefaultExternalModuleDependency(group,name,version)
Configuration configuration = project.configurations.detachedConfiguration(dependency)
configuration.setTransitive(false)
configuration.files.each { file ->
if (file.isFile() && file.name.endsWith('.zip')) {
println 'Attempting to unzip: ' + file + ' into folder: ' + appServerFolder
new Copy().from(project.zipTree(file)).into(appServerFolder).execute()
}
}
}
The problem is that the actual artifacts are not getting resolved!
A task can't configure the build model (that's what plugins do). It's fine to create and resolve a detached configuration in a task. If this doesn't work, there is likely a problem with the task's code, or the dependency it tries to resolve. Note that dependencies can only be resolved if the correct repository(s) are defined.
Instead of new DetaultExternalModuleDependency() (which is an internal class), project.dependencies.create() should be used. Instead of new Copy().execute() (Task#execute must not be called from user code), project.copy should be used.
Related
I am currently working with a project (using gradle) that needs a properties file in the same package to the class that consumes it (I can't change this config). I have added the file in my package, but when I execute the gradle build command omits the file, and only adds the .class file. The app is deployed as a war file.
I have tried editing the war task, but I can't find the way to make the properties file to be added:
war {
from('src/main/com/foo/bar') {
include 'b.properties'
into 'WEB-INF/classes/com/foo/bar'
}
}
The war task does execute (I have added a prinln and it executes). Also, I have tried changing the paths, but no results. Also replacing from('path') to from (file('path')), but still doesn't seem to add the file anywhere.
How should I achieve this? Thanks!
Is there anything stopping you from following Gradle's convention for resources?
Eg put your property file at src/main/resources/com/foo/bar/b.properties
If you do this you won't need any task customisations
We have a huge monolith application which is build by multiple tools (shell scripts, Ant and Maven). The build process is quite complex:
a lot of manually steps
hidden dependencies between Ant targets
different steps must be executed depending on the used Operating System
We decided to simplify it by creating Gradle scripts which wraps all this logic (it is quite impossible to fix it, so we create a wrapper which standardize the way of executing all the logic). We have to download some files from the Maven repository, but we cannot use the dependencies syntax:
we don't need to always download all files
the versions of the downloaded artifacts are dynamic (depends on configuration located in completely different place)
we need a path to the downloaded files (e.g. we have to unpack an artifact distributed as zip)
How we can achieve it?
The easiest way to achieve it is to create a dynamic configuration with dependencies, and next resolve it. The resolve method returns paths to the dependencies on the local disk. It is important to use a unique name for every configuration. If not, executing the logic twice would fail (cannot overwrite the configuration with XYZ name).
Here is an example method which returns a path to an artifact. If the artifact is already available in the Gradle cache it won't be downloaded for the second time, but of course the path will be returned. In this example all artifacts are downloaded from Maven Central.
Method:
ext.resolveArtifact = { CharSequence identifier ->
def configurationName = "resolveArtifact-${UUID.randomUUID()}"
return rootProject.with {
configurations.create(configurationName)
dependencies.add(configurationName, identifier)
return configurations.getByName(configurationName, {
repositories {
mavenCentral()
}
}).resolve()[0]
}
}
Usage:
def jaCoCoZip = resolveArtifact('org.jacoco:jacoco:0.8.6')
def jaCoCoAgent = resolveArtifact('org.jacoco:org.jacoco.agent:0.8.6')
curently I am writing a gradle plugin and I need to add and download a maven dependency programmatically in a given task.
I evaluated DependencyHandler and ArtifactResolutionQuery but I can't figure out where and how to add a Dependency and resolve it in mavenCentral repository
Similarcoding for maven does look rather easy
Artifact artifact = artifactFactory.createArtifactWithClassifier(groupId, artifactId, version, type, classifier);
artifactResolver.resolve(artifact, remoteRepositories, localRepository);
So i guess/hope there is a similiar easy way in gradle and I am just not seeing it
Regards
Mathias
Update 1:
So here is some of the stuff I tried,wildly c&p from different tries. it is worth saying that the dependency I want to download has the classifier ZIP, so normal in my build.gradle I write
compile 'group:artifact:version#zip
to get the file
ComponentIdentifier componentIdentifier = new DefaultModuleComponentIdentifier("com.sap.cloud",
"neo-java-web-sdk", "3.39.10");
System.out.println("CompIdentifier = " + componentIdentifier.getDisplayName());
//getProject().getDependencies().add("compile", componentIdentifier.getDisplayName());
Configuration configuration = getProject().getConfigurations().getByName("compile");
org.gradle.api.artifacts.Dependency dep2 = new DefaultExternalModuleDependency("com.sap.cloud",
"neo-java-web-sdk", "3.39.10");
boolean depList = configuration.getDependencies().add(dep2);
//
configuration.forEach(file -> {
getProject().getLogger().lifecycle("Found project dependency # " + file.getAbsolutePath());
});
Set<File> files = configuration.resolve();
for (File file2 : files) {
System.out.println("Files: " + file2.getName());
}
DependencyHandler dep = getProject().getDependencies();
ComponentModuleMetadataHandler modules = dep.getModules();
ArtifactResolutionQuery a = getProject().getDependencies().createArtifactResolutionQuery()
.forComponents(componentIdentifier).withArtifacts(MavenModule.class, SourcesArtifact.class);
ArtifactResolutionResult r = a.execute();
Set<ComponentArtifactsResult> set = r.getResolvedComponents();
Set<ComponentResult> c = r.getComponents();
I think the simplest way to download a dependency programmatically in a Gradle plugin is the same as doing it in a build script. Just create a new configuration, add your dependency and resolve the configuration. Watch the example below how this works in Java (the preferred language for Gradle plugins):
Configuration config = project.getConfigurations().create("download");
config.setTransitive(false); // if required
project.getDependencies().add(config.getName(), "com.sap.cloud:neo-java-web-sdk:3.39.10#zip");
File file = config.getSingleFile();
For this example, the name of the configuration ("download") can be any string not already used as configuration name (like compile or runtime). Since the configuration will be resolved afterwards, you must use another name whenever you reuse this code snippet (or if you call it multiple times).
In Gradle User Guide Chapter 23. Dependency Management , an example show the difference between Configuration.copy and Configuration.files method :
build.gradle
task copyVsFiles << {
configurations.sealife.copyRecursive { dep -> dep.name == 'orca' }
.each { file -> println file.name }
println()
configurations.sealife.files { dep -> dep.name == 'orca' }
.each { file -> println file.name }
}
Output of gradle -q copyVsFiles
> gradle -q copyVsFiles
orca-1.0.jar
seal-1.0.jar
orca-1.0.jar
seal-2.0.jar
the below explanation confused me. I still don't know the difference. Can anyone help me with this ?
The answer to your question is in the paragraph just after example 23.24 in the Gradle API documentation Chapter 23 Section 24.
Note how the configurations.files method returns version 2 of the seal jar.
In the example above, orca has a dependency on seal-1.0 whereas shark
has a dependency onseal-2.0. The original configuration has therefore
a version conflict which is resolved to the newer seal-2.0 version.
The files method therefore returns seal-2.0 as a transitive dependency
of orca. The copied configuration only has orca as a dependency and
therefore there is no version conflict and seal-1.0 is returned as a
transitive dependency.
To really understand why this happens spend some time looking at the Configuration method in the Gradle docs.
Files does the following:
Resolves this configuration. This locates and downloads the files
which make up this configuration. But only the resulting set of files
belonging to the subset of dependencies specified by the
dependencySpec is returned.
Copy does the following:
Creates a copy of this configuration that only contains the
dependencies directly in this configuration (without contributions
from superconfigurations). The new configuration will be in the
UNRESOLVED state, but will retain all other attributes of this
configuration except superconfigurations. Configuration.getHierarchy()
for the copy will not include any superconfigurations.
How does this apply to the documentation example? Configuration.files resolves the configuration which, as shown by the output in the example, handles the version conflict introduced by shark which depends on seal-2.0.jar. Configuration.copy creates a copy of the configuration and is not yet resolved meaning it does not yet have a version conflict.
There's a bug in a dependency I'm trying to use that makes the build fail. I'm trying to configure a work-around until the dependency's fix is released. The work-around is to remove a class from a jar inside an aar.
The task I need to do this action in is dynamically added, so to complicate things I'm modifying the task in another task.
At this point in time, I'm just trying to find the location of the aar on disk. I'm open to any and all suggestions on how to do this better/easier, as I'm terrible at Gradle. But my explicit question is how can I modify a dependency file after resolution?
// A hack for duplicate BuildConfig in multidex bug.
// remove the duplicate BuildConfig from the multidex test aar
// can't configure before packageAllArmDebugTestClassesForMultiDex task is known, so configure it
// after preBuild.
preBuild.doLast {
packageAllArmDebugTestClassesForMultiDex.doFirst {
project.configurations.getByName("androidTestCompile")
.getDependencies().matching{d -> d.name == "multidex-instrumentation"}
.each{f -> println("Nothing?! " + f.getArtifacts())}
}
}
The packageAllArmDebugTestClassesForMultiDex task is successfully modified to print out the dependency artifacts, but it merely prints Nothing?! [].