[Build] Make ThirdPartyAudit task uptodate more effective (#118723)

We should basically be able to skip the third party dependency audit
tasks if no third party dependency has changed.

Filtering out the project dependencies allows way better uptodate and
caching behaviour. We are only interested in thirdparty libs anyhow in
this context.

A positive side effect of the reduced class path is a quicker execution
of the task
This commit is contained in:
Rene Groeschke 2024-12-18 09:22:08 +01:00 committed by GitHub
parent 3f1fed0f09
commit d118cbf529
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 41 additions and 19 deletions

View file

@ -16,12 +16,14 @@ import org.gradle.api.Project;
import org.gradle.api.Task; import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier; import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.TaskProvider;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import static org.elasticsearch.gradle.internal.util.DependenciesUtils.createFileCollectionFromNonTransitiveArtifactsView; import static org.elasticsearch.gradle.internal.util.DependenciesUtils.createFileCollectionFromNonTransitiveArtifactsView;
import static org.elasticsearch.gradle.internal.util.DependenciesUtils.thirdPartyDependenciesView;
import static org.elasticsearch.gradle.internal.util.ParamsUtils.loadBuildParams; import static org.elasticsearch.gradle.internal.util.ParamsUtils.loadBuildParams;
public class ThirdPartyAuditPrecommitPlugin extends PrecommitPlugin { public class ThirdPartyAuditPrecommitPlugin extends PrecommitPlugin {
@ -47,7 +49,6 @@ public class ThirdPartyAuditPrecommitPlugin extends PrecommitPlugin {
project.getDependencies().add(JDK_JAR_HELL_CONFIG_NAME, elasticsearchCoreProject); project.getDependencies().add(JDK_JAR_HELL_CONFIG_NAME, elasticsearchCoreProject);
} }
} }
TaskProvider<ExportElasticsearchBuildResourcesTask> resourcesTask = project.getTasks() TaskProvider<ExportElasticsearchBuildResourcesTask> resourcesTask = project.getTasks()
.register("thirdPartyAuditResources", ExportElasticsearchBuildResourcesTask.class); .register("thirdPartyAuditResources", ExportElasticsearchBuildResourcesTask.class);
Path resourcesDir = project.getBuildDir().toPath().resolve("third-party-audit-config"); Path resourcesDir = project.getBuildDir().toPath().resolve("third-party-audit-config");
@ -59,9 +60,11 @@ public class ThirdPartyAuditPrecommitPlugin extends PrecommitPlugin {
// usually only one task is created. but this construct makes our integTests easier to setup // usually only one task is created. but this construct makes our integTests easier to setup
project.getTasks().withType(ThirdPartyAuditTask.class).configureEach(t -> { project.getTasks().withType(ThirdPartyAuditTask.class).configureEach(t -> {
Configuration runtimeConfiguration = project.getConfigurations().getByName("runtimeClasspath"); Configuration runtimeConfiguration = project.getConfigurations().getByName("runtimeClasspath");
FileCollection runtimeThirdParty = thirdPartyDependenciesView(runtimeConfiguration);
Configuration compileOnly = project.getConfigurations() Configuration compileOnly = project.getConfigurations()
.getByName(CompileOnlyResolvePlugin.RESOLVEABLE_COMPILE_ONLY_CONFIGURATION_NAME); .getByName(CompileOnlyResolvePlugin.RESOLVEABLE_COMPILE_ONLY_CONFIGURATION_NAME);
t.setClasspath(runtimeConfiguration.plus(compileOnly)); FileCollection compileOnlyThirdParty = thirdPartyDependenciesView(compileOnly);
t.getThirdPartyClasspath().from(runtimeThirdParty, compileOnlyThirdParty);
t.getJarsToScan() t.getJarsToScan()
.from( .from(
createFileCollectionFromNonTransitiveArtifactsView( createFileCollectionFromNonTransitiveArtifactsView(
@ -78,7 +81,7 @@ public class ThirdPartyAuditPrecommitPlugin extends PrecommitPlugin {
t.getJavaHome().set(buildParams.flatMap(params -> params.getRuntimeJavaHome()).map(File::getPath)); t.getJavaHome().set(buildParams.flatMap(params -> params.getRuntimeJavaHome()).map(File::getPath));
t.setSignatureFile(resourcesDir.resolve("forbidden/third-party-audit.txt").toFile()); t.setSignatureFile(resourcesDir.resolve("forbidden/third-party-audit.txt").toFile());
t.getJdkJarHellClasspath().from(jdkJarHellConfig); t.getJdkJarHellClasspath().from(jdkJarHellConfig);
t.getForbiddenAPIsClasspath().from(project.getConfigurations().getByName("forbiddenApisCliJar").plus(compileOnly)); t.getForbiddenAPIsClasspath().from(project.getConfigurations().getByName("forbiddenApisCliJar").plus(compileOnlyThirdParty));
}); });
return audit; return audit;
} }

View file

@ -17,7 +17,6 @@ import org.gradle.api.DefaultTask;
import org.gradle.api.JavaVersion; import org.gradle.api.JavaVersion;
import org.gradle.api.file.ArchiveOperations; import org.gradle.api.file.ArchiveOperations;
import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileSystemOperations; import org.gradle.api.file.FileSystemOperations;
import org.gradle.api.file.FileTree; import org.gradle.api.file.FileTree;
import org.gradle.api.file.ProjectLayout; import org.gradle.api.file.ProjectLayout;
@ -96,8 +95,6 @@ public abstract class ThirdPartyAuditTask extends DefaultTask {
private final ProjectLayout projectLayout; private final ProjectLayout projectLayout;
private FileCollection classpath;
@Inject @Inject
public ThirdPartyAuditTask( public ThirdPartyAuditTask(
ArchiveOperations archiveOperations, ArchiveOperations archiveOperations,
@ -198,9 +195,7 @@ public abstract class ThirdPartyAuditTask extends DefaultTask {
public abstract Property<JavaVersion> getRuntimeJavaVersion(); public abstract Property<JavaVersion> getRuntimeJavaVersion();
@Classpath @Classpath
public FileCollection getClasspath() { public abstract ConfigurableFileCollection getThirdPartyClasspath();
return classpath;
}
@TaskAction @TaskAction
public void runThirdPartyAudit() throws IOException { public void runThirdPartyAudit() throws IOException {
@ -345,7 +340,7 @@ public abstract class ThirdPartyAuditTask extends DefaultTask {
if (javaHome.isPresent()) { if (javaHome.isPresent()) {
spec.setExecutable(javaHome.get() + "/bin/java"); spec.setExecutable(javaHome.get() + "/bin/java");
} }
spec.classpath(getForbiddenAPIsClasspath(), classpath); spec.classpath(getForbiddenAPIsClasspath(), getThirdPartyClasspath());
// Enable explicitly for each release as appropriate. Just JDK 20/21/22/23 for now, and just the vector module. // Enable explicitly for each release as appropriate. Just JDK 20/21/22/23 for now, and just the vector module.
if (isJavaVersion(VERSION_20) || isJavaVersion(VERSION_21) || isJavaVersion(VERSION_22) || isJavaVersion(VERSION_23)) { if (isJavaVersion(VERSION_20) || isJavaVersion(VERSION_21) || isJavaVersion(VERSION_22) || isJavaVersion(VERSION_23)) {
spec.jvmArgs("--add-modules", "jdk.incubator.vector"); spec.jvmArgs("--add-modules", "jdk.incubator.vector");
@ -383,7 +378,7 @@ public abstract class ThirdPartyAuditTask extends DefaultTask {
private Set<String> runJdkJarHellCheck() throws IOException { private Set<String> runJdkJarHellCheck() throws IOException {
ByteArrayOutputStream standardOut = new ByteArrayOutputStream(); ByteArrayOutputStream standardOut = new ByteArrayOutputStream();
ExecResult execResult = execOperations.javaexec(spec -> { ExecResult execResult = execOperations.javaexec(spec -> {
spec.classpath(getJdkJarHellClasspath(), classpath); spec.classpath(getJdkJarHellClasspath(), getThirdPartyClasspath());
spec.getMainClass().set(JDK_JAR_HELL_MAIN_CLASS); spec.getMainClass().set(JDK_JAR_HELL_MAIN_CLASS);
spec.args(getJarExpandDir()); spec.args(getJarExpandDir());
spec.setIgnoreExitValue(true); spec.setIgnoreExitValue(true);
@ -402,8 +397,4 @@ public abstract class ThirdPartyAuditTask extends DefaultTask {
return new TreeSet<>(Arrays.asList(jdkJarHellCheckList.split("\\r?\\n"))); return new TreeSet<>(Arrays.asList(jdkJarHellCheckList.split("\\r?\\n")));
} }
public void setClasspath(FileCollection classpath) {
this.classpath = classpath;
}
} }

View file

@ -9,12 +9,16 @@
package org.elasticsearch.gradle.internal.util; package org.elasticsearch.gradle.internal.util;
import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin;
import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ResolvableDependencies; import org.gradle.api.artifacts.ResolvableDependencies;
import org.gradle.api.artifacts.component.ComponentIdentifier; import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.artifacts.result.ResolvedComponentResult; import org.gradle.api.artifacts.result.ResolvedComponentResult;
import org.gradle.api.artifacts.result.ResolvedDependencyResult; import org.gradle.api.artifacts.result.ResolvedDependencyResult;
import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.Provider;
import org.gradle.api.specs.AndSpec; import org.gradle.api.specs.AndSpec;
import org.gradle.api.specs.Spec; import org.gradle.api.specs.Spec;
@ -29,7 +33,7 @@ public class DependenciesUtils {
) { ) {
ResolvableDependencies incoming = configuration.getIncoming(); ResolvableDependencies incoming = configuration.getIncoming();
return incoming.artifactView(viewConfiguration -> { return incoming.artifactView(viewConfiguration -> {
Set<ComponentIdentifier> firstLevelDependencyComponents = incoming.getResolutionResult() Provider<Set<ComponentIdentifier>> firstLevelDependencyComponents = incoming.getResolutionResult()
.getRootComponent() .getRootComponent()
.map( .map(
rootComponent -> rootComponent.getDependencies() rootComponent -> rootComponent.getDependencies()
@ -39,12 +43,36 @@ public class DependenciesUtils {
.filter(dependency -> dependency.getSelected() instanceof ResolvedComponentResult) .filter(dependency -> dependency.getSelected() instanceof ResolvedComponentResult)
.map(dependency -> dependency.getSelected().getId()) .map(dependency -> dependency.getSelected().getId())
.collect(Collectors.toSet()) .collect(Collectors.toSet())
) );
.get();
viewConfiguration.componentFilter( viewConfiguration.componentFilter(
new AndSpec<>(identifier -> firstLevelDependencyComponents.contains(identifier), componentFilter) new AndSpec<>(identifier -> firstLevelDependencyComponents.get().contains(identifier), componentFilter)
); );
}).getFiles(); }).getFiles();
} }
/**
* This method gives us an artifact view of a configuration that filters out all
* project dependencies that are not shadowed jars.
* Basically a thirdparty only view of the dependency tree.
*/
public static FileCollection thirdPartyDependenciesView(Configuration configuration) {
ResolvableDependencies incoming = configuration.getIncoming();
return incoming.artifactView(v -> {
// resolve componentIdentifier for all shadowed project dependencies
Provider<Set<ComponentIdentifier>> shadowedDependencies = incoming.getResolutionResult()
.getRootComponent()
.map(
root -> root.getDependencies()
.stream()
.filter(dep -> dep instanceof ResolvedDependencyResult)
.map(dep -> (ResolvedDependencyResult) dep)
.filter(dep -> dep.getResolvedVariant().getDisplayName() == ShadowBasePlugin.COMPONENT_NAME)
.filter(dep -> dep.getSelected() instanceof ResolvedComponentResult)
.map(dep -> dep.getSelected().getId())
.collect(Collectors.toSet())
);
// filter out project dependencies if they are not a shadowed dependency
v.componentFilter(i -> (i instanceof ProjectComponentIdentifier == false || shadowedDependencies.get().contains(i)));
}).getFiles();
}
} }