Converting Maven plugin to Gradle plugin

Hi,

I’m converting a Maven plugin to Gradle where the Maven plugin is doing:

    for (Object a : mavenProject.getArtifacts()) {
        Artifact artifact = (Artifact) a;
        File file = artifact.getFile();
        // do something with the file
    }

What is the equivalent to get these dependency file references in a Gradle task?

I believe that by default the artifacts (eg jars) are added to the “archives” Configuration.

So you could do something like

// closure will be executed live (as archives are added) 
configurations.archives.all { File archive -> ... } 

Or

 // need to be careful with this closure, it might execute before the archives are added
configurations.archives.each { File archive -> ... } 

I’m now using this:

Don’t know if it is the optimal implementation, but it works.

Your task now has a dependency on the artifacts (or maybe the configurations) but I can’t see that dependency being expressed.

See Task.dependsOn(Object …) and the supported object types. I’m guessing you’ll use a Provider to delay the execution until after the configuration phase

The code I posted earlier does not what I want after all.

Basically I want to have a custom taks which could loop over all Artifacts in the Configurations.
I need the actual File of the artifact as I need to index the file contents.

I already found out that e.g. the “implementation” configuration is not resolvabe.

Now based on the previous code we want to offer that users using the custom task can even define which configurations should be used to index it’s artifacts.

So if someone has:

dependencies {
    implementation 'io.smallrye:smallrye-graphql-servlet:1.0.2-SNAPSHOT'
    implementation 'com.github.javafaker:javafaker:1.0.2'
    runtimeOnly 'org.microprofile-ext.openapi-ext:openapi-ui:1.1.4'
    providedCompile 'org.eclipse.microprofile:microprofile:3.3'
    compileOnly 'org.projectlombok:lombok:1.18.12'
    annotationProcessor 'org.projectlombok:lombok:1.18.12'
}

then in my task I want to be configurable to only index jars from implementation configuration.

But as the implementation configuration is not resolvable this won’t work.

In the Maven plugin this is quite straightforward like:

        for (Object a : mavenProject.getArtifacts()) {
            Artifact artifact = (Artifact) a;
            if (includeDependenciesScopes.contains(artifact.getScope())
                    && includeDependenciesTypes.contains(artifact.getType())) {
                getLog().debug("Indexing file " + artifact.getFile());
                try {
                    Result result = JarIndexer.createJarIndex(artifact.getFile(), new Indexer(), false, false, false);
                    indexes.add(result.getIndex());
                } catch (Exception e) {
                    getLog().error("Can't compute index of " + artifact.getFile().getAbsolutePath() + ", skipping", e);
                }
            }
        }

I’m a bit stuck how one should do something similar in a Gradle custom task.

So you want the dependency jars? Not the artifacts produced by the project? I think you’d do something like:

   @InputFiles
   public FileCollection getImplementationConfiguration() {
      return getProject().getConfigurations().getByName("implementation");
   }

   @TaskAction
   public void doIt() {
      Set<File> files = getImplementationConfiguration().getFiles();
      for (File file : files) {...}
   }

Yes, I want the dependency jars. And thanks for your patience :slight_smile:

E.g. when I do the getFiles() like:

        Set<Configuration> configurations = getProject().getConfigurations();
        for (Configuration configuration : configurations) {
            if (includeDependenciesConfigurations.contains(configuration.getName())) {
                Set<File> files = configuration.getFiles();
                .. do other stuff
            }
        } 

then it gives me:

Caused by: java.lang.IllegalStateException: Resolving dependency configuration 'implementation' is not allowed as it is defined as 'canBeResolved=false'.
Instead, a resolvable ('canBeResolved=true') dependency configuration that extends 'implementation' should be resolved.

So I can’t seem to be able to get the files for the implementation configuration.

Btw besides the File object I also need the extension of the dependency.

Thanks for your help so far,
Marcel

Hmm, I’ve seen this “canBeResolved=false” before and never took the time to understand why.

I wonder if you can do

ConfigurationContainer cc = project.getConfigurations();
Configuration resolvable = cc.detachedConfiguration();
Configuration impl = cc.getByName("implementation");
resolvable.extendsFrom(impl);
Set<File> files = resolvable.getFiles();

Or perhaps

Configuration resolvable = impl.copyRecursive();
Set<File> files = resolvable.getFiles();

I tried.

The first option does not contain any files unfortunately…
The second option gives Resolving dependency configuration 'implementationCopy' is not allowed as it is defined as 'canBeResolved=false'.

But I think you put me in the right direction with copying the configuration.

I now have this:

        ConfigurationContainer configurations = getProject().getConfigurations();

        for (String s : includeDependenciesConfigurations) {

            Configuration configuration = configurations.getByName(s);

            Configuration copiedConfiguration = configuration.copyRecursive();
            copiedConfiguration.setCanBeResolved(true); // THIS IS IMPORTANT; AS IT IS A COPIED CONFIG I GUESS IT IS OK TO DO.

            ResolvedConfiguration resolvedConfiguration = copiedConfiguration.getResolvedConfiguration();

            Set<ResolvedArtifact> artifacts = resolvedConfiguration.getResolvedArtifacts();
            for (ResolvedArtifact artifact : artifacts) {
                getLogger().lifecycle("INCLUDED CONFIG = " + s +
                        "\n    INCLUDED ARTIFACT = " + artifact.getName() +
                        "\n    INCLUDED CLASSIFIER = " + artifact.getClassifier() +
                        "\n    INCLUDED EXTENSION = " + artifact.getExtension() +
                        "\n    INCLUDED TYPE = " + artifact.getType() +
                        "\n    INCLUDED FILE = " + artifact.getFile().getPath());
            }
        }

So I think I’m almost there.

The artifacts printed also include transitive dependencies.
I wonder if it would be possible to include only the top dependencies defined directly in the configuration.

So e.g.

dependencies {
    implementation 'com.github.javafaker:javafaker:1.0.2' // just a random dep
}

would only include this artifact, but not the transitive dependency artifacts.

I think you put me in the right direction

Glad to hear

I wonder if it would be possible to include only the top dependencies defined directly in the configuration.

You’re in luck! Just do impl.copy() instead of impl.copyRecursive()

Thanks, I will give this a try this evening but I think that will do indeed!

Thanks for your help and I will let you know later if it worked.

configuration.copyRecursive() vs configuration.copy() doesn’t seem to matter.

From the apidocs I see for copyRecursive:

Creates a copy of this configuration that contains the dependencies directly in this configuration and those derived from superconfigurations.

and for copy():

Creates a copy of this configuration that only contains the dependencies directly in this configuration (without contributions from superconfigurations).

I don’t think that is related to transitive dependencies, only derived from other configurations?

It can be solved with:

copiedConfiguration.setTransitive(true|false);

So in my case:

Configuration copiedConfiguration = configuration.copyRecursive();
copiedConfiguration.setCanBeResolved(true);
copiedConfiguration.setTransitive(includeDependenciesTransitive);
ResolvedConfiguration resolvedConfiguration = copiedConfiguration.getResolvedConfiguration();

The resolved configuration in this case contain the transitive dependencies or not depending on the flag.

This covers it :slight_smile:

Thanks for your help Lance, much appreciated.

For a bit of further reading you might also be interested in Configuration. resolvedConfiguration.firstLevelModuleDependencies which I make usage of here