Access generatePomFileFor*Publication from plugin code

I am writing a plugin that ultimately needs access to the generated POM file for a MavenPublication. As far as I know, that information is currently only available via GenerateMavenPom#destination. However I have tried many ways to be able to access the various generatePomFileFor*Publication with no success. I have tried:

  1. project.getTasks().findByName( name ) - and getByName
  2. the same as above, but done in an project.afterEvaluation block
  3. via ModelMap as a Rule(Source)

None of them worked. Any pointers?

project.getTasks().findByName(name) should work, it’s likely just a matter of timing.

What I think you really want is access to:

You could try reflectively hacking access to that to see if that would work for you. If so, we could at least promote that to the internal interface as an interim step so you wouldn’t have to hack at private fields.

Thanks @luke_daley. I had seen that one but wanted to stay away from reflection if possible. But that seems like the best/only option. The Task lookup is very perplexing.

Another reason I did not really think to use this is I had no idea why it is a FileCollection. Any insight into that? As far as I understand, the MavenPublication allows just a single POM be associated with it, so why not just a single File?

Maybe just to be Buildable?

Also, access the field just returns null :frowning:


	private static final String PUBLICATION_IMPL_CLASS_NAME = "org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication";
	private static final Class PUBLICATION_IMPL_CLASS;
	private static final String POM_FILE_COLLECTION_FIELD_NAME = "pomFile";
	private static final Field POM_FILE_COLLECTION_FIELD;

	static {
		try {
			PUBLICATION_IMPL_CLASS = Utils.class.getClassLoader().loadClass( PUBLICATION_IMPL_CLASS_NAME );
		}
		catch (ClassNotFoundException e) {
			throw new GradleException( "Could not locate DefaultMavenPublication class : " + PUBLICATION_IMPL_CLASS_NAME, e );
		}

		try {
			POM_FILE_COLLECTION_FIELD = PUBLICATION_IMPL_CLASS.getDeclaredField( POM_FILE_COLLECTION_FIELD_NAME );
			POM_FILE_COLLECTION_FIELD.setAccessible( true );
		}
		catch (NoSuchFieldException e) {
			throw new GradleException( "Could not locate DefaultMavenPublication#pomFile field", e );
		}
	}

	public static FileCollection getPomFileCollection(MavenPublication mavenPublication) {
		try {
			return (FileCollection) POM_FILE_COLLECTION_FIELD.get( mavenPublication );
		}
		catch (IllegalAccessException e) {
			throw new GradleException( "Could not access DefaultMavenPublication#pomFile field [" + mavenPublication + "]", e );
		}
	}

Calls to this #getPomFileCollection method return null

Yes, just so that it can encode the task dependency.

Timing is critical. When are you trying to access? When do you need to have it?

The background is that I am working on a solution to sign all of the artifacts associated with a MavenDeployment[1] in preparation for uploading to OSSRH[2].

I have tried numerous timings. I’ve tried applying my plugin at different points in the script. I’ve tried delaying this specific work until afterEvaluation.

The basic idea I was going for was to add a MavenArtifact to the publictaion for each artifact (including the pom file) in the publication. So I need to be able to access all of the artifacts at a time I can still add to the collection of artifacts. Maybe you could suggest a better “timing”?

[1] https://github.com/gradle/gradle/issues/1165
[2] I wanted to use Bintray but then noticed the 10G storage limit of the OSS plan which Hibernate would hit in no time.

afterEvaluate is not going to be late enough. The pomFile property is set as the tasks are created, which is later. The only practical option is to reference this property (i.e. lazily) and not try to copy it.

The feasibility of that depends on how eagerly Gradle tries to access org.gradle.api.publish.maven.MavenArtifact#getFile for the artifacts I am adding to the MavenPublication.

Or how lately I can add my MavenArtifact objects to the publication.

Any ideas about these?

I see what you’re saying. That’s not going to work as you can’t practically have a lazy File.

Next, I’d try doing your own impl of MavenArtifact with a getFile() that is lazy. You’d have to add this directly via publication.artifacts.add() instead of the publication.artifact {} factory.

MavenArtifact is unfortunately a org.gradle.api.Buildable - I don’t really know how to implement Buildable#getBuildDependencies here since the pomFile is part of its dependencies

You can just return a DefaultTaskDependency, populated with a Callable that returns DefaultMavenPublication.pomFile.

I’ve already been down this path. DefaultTaskDependency is unfortunately internal and implementing the TaskDependency contract is beyond my understanding - I’ve looked through the code in DefaultTaskDependency and it sadly makes zero sense to me.

You aren’t going to be able to implement what you want without using some internals.

You could try just delegating to DefaultMavenPublication.pomFile.getTaskDependency() (i.e. that thing is a Buildable) when it is not null.

I generally do not have a problem with that. However, whenever I do rely on Gradle internal classes I end up getting runtime errors during the build saying that Gradle internal dependencies cannot be found

Well that comes back to the original thing I am asking… when does MavenPublication.pomFile become non-null so that I can do this, and is that point in time too late to still add a MavenArtifact to that MavenPublication?

So the crux here seems to be: When is the latest I can add the MavenArtifact to that MavenPublication? This would facilitate delaying the creation/addition of the MavenArtifact until the #pomFile information is ready.

I guess the easiest route (really the only workable route atm) is to plan on saving the signature files into a dedicated “build dir”. The trouble here is ultimately coming from the fact that I was trying to have a “nice” feature of saving the signature files to the same dir as the artifact file it is verifying. For that to work for the POM I have to know its output directory which is why I need access to the pomFile. If I just plan on saving all signature files to the one dir then its not relevant where the POM is generated.

FWIW I went ahead and forced naming a “signature directory” and this code is now working.

The non-working code was trying to access MavenPublication#pomFile in order to determine a base name for the POM signature file; roughly it was trying to name it mavenPublication.getPomFile().getSingleFile().getName + ".asc". However, this is actually not correct since the build can name this generated POM file anything it wants locally - so this is not necessarily a “hack” change. I am happy with this.

Thanks for your help (as always) Luke.

Success!

Great stuff Steve. I am sure that many will be eagerly awaiting your work.

I may end up using Bintray after all, in which case all of this work was not necessary for me :smile:

But in the meantime, here is the working code: https://github.com/sebersole/gradle-clean-room2/tree/master/buildSrc