Using build artifacts from one project in another project

Hi,
I have two projects A and B and want to use one build artifact of A as dependency in B.
The Buildscript looks like this

subprojects {

	configurations {
		feature {
    		canBeConsumed = true
        	canBeResolved = true
        	attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
        	attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.NATIVE_LINK))
		}
		template {
    		canBeConsumed = true
        	canBeResolved = true
        	attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
        	attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.NATIVE_RUNTIME))
		}
	}
	
	task extractDeps(type: Copy) {
    	from {
        	configurations.template.collect {
            	zipTree(it).matching{include '**/*.xml'}.files
        	}
    	}
    	into ('deps')
	}
	
	task deleteOut(type: Delete) {
		delete('out', 'deps')
	}

	task buildNative(Type: Exec) {
		outputs.dir('out')
		outputs.upToDateWhen { false }
		commandLine create.sh
	}
	
	task buildFeature(type: Zip) {
		from buildNative
		exclude '**/*project*.zip'
	}
	
	task buildTemplate(type: Zip) {
		dependsOn buildNative
		classifier = 'template'
    	from {
    		buildNative.outputs.files.asFileTree.filter {file->file.name.endsWith('_template.zip')}.collect{zipTree(it)}
    	}
    	outputs.upToDateWhen { false }
	}
    			
	clean.dependsOn deleteOut

	artifacts {
    	template(buildTemplate)
    	feature(buildFeature)
	}
	
	publishing {
        publications {
            maven(MavenPublication) {
	              artifact source: buildTemplate
	              artifact source: buildFeature
            }
        }
    }
}

Gradle file for project B with dependency declaration looks like this:

dependencies {
	template project(path:':A',configuration:'template')
}

If I start this with i.e. gradle publish, I get

Could not determine the dependencies of task ':B:extractDeps'.
> Cannot expand ZIP '/home/jenkins/workspace/A/build/distributions/A-13.0.0-SNAPSHOT-template.zip' as it does not exist.

But of cause it does not exist as it hasn’t been built yet.

If I start build on A manually, everything is fine. And If I then start the build again, all is working too, because now the missing artifact is created. But it should also work after a clean.

Is there anything I’m missing?

Gradle does not know that the extractDeps task depends on the template configuration and therefore cannot infer the required task dependencies. The following should do two things for you; 1. Let Gradle know what the input files are for up-to-date checking. 2. The required task dependencies will be inferred.

task extractDeps( type: Copy ) {
    inputs.files configurations.template
    ...
}

Thanks, but this does not change a thing.
Maybe the problem is somehow that gradle checks for the zip already in the configuration phase, but it is first created in the execution phase. Is it somehow possible to postpone the check?

I tried to squeeze in DoLasts every now and then, but best I could get was a “NO-SOURCE” for extractDeps or syntax errors. And honoestly I thought using a closure in the from statement should be enough to get to the execution phase.
There was a similar issue in the buildTemplate task and this was solved by using the closure in the from statement.

Is it easy for you to share an example build that demonstrates what you’re seeing?

Example.zip (64.0 KB)
I hope it’s complete

OK, I see what you’re running into. extractDeps is configured using from( Closure ). This closure will be called multiple times during Gradle’s lifecycle (I’m seeing 5 calls). One of the calls happens right after the projects are configured and before the task graph is generated. This is part of Gradle gathering all the task inputs in order to discover any inferred task dependencies. Since the dependent task hasn’t run yet zipTree fails.

The closure will need to handle the case where the file does not exist yet and calling inputs.files will allow Gradle to discover the required task dependencies.

task extractDeps( type: Copy ) {
    inputs.files configurations.template
    from {
        configurations.template.collect {
            if( it.exists() ) {
                zipTree( it ).matching{ include '**/*.xml' }.files
            }
        }
    }
    into( 'deps' )
}

Here’s an alternative way of implementing the extractDeps task that avoids having a closure called so many times:

task extractDeps {
    ext {
        inFiles = configurations.template
        outDir = file( 'deps' )
    }
    inputs.files( inFiles )
    outputs.dir( outDir )
    doLast {
        project.copy {
            inFiles.each {
                // No longer need to check if the file exists because we're
                // guaranteed to be in task execution and this task's dependencies
                // will have been executed.
                from( zipTree( it ).matching{ include '**/*.xml' } )
            }
            into( outDir )
        }
    }
}
1 Like

This response very much relates to my age old request for an unzip task

2 Likes

Thanks a lot. There will be the time when also I can undestand all the miracle gradle is.