Skip to content

Commit

Permalink
fix - Support composite builds using build actions (#154)
Browse files Browse the repository at this point in the history
Co-authored-by: Arthur McGibbon <arthur.mcgibbon@h3im.com>
  • Loading branch information
Tanish-Ranjan and Arthur McGibbon authored Jul 5, 2024
1 parent 544fc2b commit 5af4df6
Show file tree
Hide file tree
Showing 25 changed files with 438 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
* List of all Gradle source set instances.
*/
public interface GradleSourceSets extends Serializable {
public List<GradleSourceSet> getGradleSourceSets();
List<GradleSourceSet> getGradleSourceSets();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.microsoft.java.bs.gradle.model;

import java.io.File;
import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
* Provides necessary information about Gradle source sets,
* enabling mapping of dependencies between them.
*/
public interface GradleSourceSetsMetadata extends Serializable {

/**
* Returns a map that associates each Gradle source set with its corresponding
* classpath files in a gradle project. This typically includes any libraries
* or dependencies required for compilation within that source set.
*
* <p>
* The keys of the map represent instances of the {@link GradleSourceSet} class,
* identifying all the source sets within the project.
* </p>
* <p>
* The values of the map are lists of {@link File} objects, representing the
* classpath files associated with the corresponding source set.
* </p>
*/
Map<GradleSourceSet, List<File>> getGradleSourceSetsToClasspath();

/**
* Returns a map that associates output files with the Gradle source sets that
* generated them. This is useful for understanding the origin of generated artifacts.
*
* <p>
* The keys of the map are {@link File} objects, representing individual
* output files produced during the build process.
* </p>
* <p>
* The values of the map are instances of the {@link GradleSourceSet} class,
* indicating the source set that generated the corresponding output file.
* </p>
*/
Map<File, GradleSourceSet> getOutputsToSourceSet();

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

package com.microsoft.java.bs.gradle.model.impl;

import com.microsoft.java.bs.gradle.model.GradleSourceSet;
import com.microsoft.java.bs.gradle.model.GradleSourceSets;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import com.microsoft.java.bs.gradle.model.GradleSourceSet;
import com.microsoft.java.bs.gradle.model.GradleSourceSets;

/**
* Default implementation of {@link GradleSourceSets}.
*/
Expand All @@ -26,10 +26,12 @@ public DefaultGradleSourceSets(List<GradleSourceSet> gradleSourceSets) {
* Copy constructor.
*/
public DefaultGradleSourceSets(GradleSourceSets sourceSets) {
this.gradleSourceSets = sourceSets.getGradleSourceSets().stream()
.map(DefaultGradleSourceSet::new).collect(Collectors.toList());
this(sourceSets.getGradleSourceSets().stream()
.map(DefaultGradleSourceSet::new)
.collect(Collectors.toList()));
}

@Override
public List<GradleSourceSet> getGradleSourceSets() {
return gradleSourceSets;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.microsoft.java.bs.gradle.model.impl;

import com.microsoft.java.bs.gradle.model.GradleSourceSet;
import com.microsoft.java.bs.gradle.model.GradleSourceSetsMetadata;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
* Default implementation of {@link DefaultGradleSourceSetsMetadata}.
*/
public class DefaultGradleSourceSetsMetadata implements GradleSourceSetsMetadata {

private Map<GradleSourceSet, List<File>> sourceSetsToClasspath;
private Map<File, GradleSourceSet> outputsToSourceSet;

public DefaultGradleSourceSetsMetadata(
Map<GradleSourceSet, List<File>> sourceSetsToClasspath,
Map<File, GradleSourceSet> outputsToSourceSet
) {
this.sourceSetsToClasspath = sourceSetsToClasspath;
this.outputsToSourceSet = outputsToSourceSet;
}

@Override
public Map<GradleSourceSet, List<File>> getGradleSourceSetsToClasspath() {
return sourceSetsToClasspath;
}

public void setSourceSetsToClasspath(Map<GradleSourceSet, List<File>> sourceSetsToClasspath) {
this.sourceSetsToClasspath = sourceSetsToClasspath;
}

@Override
public Map<File, GradleSourceSet> getOutputsToSourceSet() {
return outputsToSourceSet;
}

public void setOutputsToSourceSet(Map<File, GradleSourceSet> outputsToSourceSet) {
this.outputsToSourceSet = outputsToSourceSet;
}

@Override
public int hashCode() {
return Objects.hash(sourceSetsToClasspath, outputsToSourceSet);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
DefaultGradleSourceSetsMetadata that = (DefaultGradleSourceSetsMetadata) obj;
return Objects.equals(sourceSetsToClasspath, that.sourceSetsToClasspath)
&& Objects.equals(outputsToSourceSet, that.outputsToSourceSet);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.util.Map;
import java.util.Set;

import com.microsoft.java.bs.gradle.model.GradleSourceSet;
import com.microsoft.java.bs.gradle.model.GradleSourceSetsMetadata;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.CopySpec;
Expand All @@ -29,12 +31,9 @@
import org.gradle.tooling.provider.model.ToolingModelBuilder;
import org.gradle.util.GradleVersion;

import com.microsoft.java.bs.gradle.model.BuildTargetDependency;
import com.microsoft.java.bs.gradle.model.GradleSourceSets;
import com.microsoft.java.bs.gradle.model.LanguageExtension;
import com.microsoft.java.bs.gradle.model.impl.DefaultBuildTargetDependency;
import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSet;
import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSets;
import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSetsMetadata;
import com.microsoft.java.bs.gradle.model.LanguageExtension;
import com.microsoft.java.bs.gradle.plugin.dependency.DependencyCollector;

/**
Expand All @@ -43,11 +42,15 @@
public class SourceSetsModelBuilder implements ToolingModelBuilder {
@Override
public boolean canBuild(String modelName) {
return modelName.equals(GradleSourceSets.class.getName());
return modelName.equals(GradleSourceSetsMetadata.class.getName());
}

@Override
public Object buildAll(String modelName, Project rootProject) {

Map<GradleSourceSet, List<File>> sourceSetsToClasspath = new HashMap<>();
Map<File, GradleSourceSet> outputsToSourceSet = new HashMap<>();

Set<Project> allProject = rootProject.getAllprojects();
SourceSetCache cache = new SourceSetCache();
// this set is used to eliminate the source, resource and output
Expand All @@ -63,6 +66,7 @@ public Object buildAll(String modelName, Project rootProject) {
DefaultGradleSourceSet gradleSourceSet = new DefaultGradleSourceSet();
cache.addGradleSourceSet(sourceSet, gradleSourceSet);
cache.addProject(sourceSet, project);
gradleSourceSet.setBuildTargetDependencies(new HashSet<>());
gradleSourceSet.setGradleVersion(project.getGradle().getGradleVersion());
gradleSourceSet.setProjectName(project.getName());
String projectPath = project.getPath();
Expand Down Expand Up @@ -108,10 +112,13 @@ public Object buildAll(String modelName, Project rootProject) {
List<File> compileClasspath = new LinkedList<>(sourceSet.getCompileClasspath().getFiles());
gradleSourceSet.setCompileClasspath(compileClasspath);

sourceSetsToClasspath.put(gradleSourceSet, compileClasspath);

// source output dir
File sourceOutputDir = getSourceOutputDir(sourceSet);
if (sourceOutputDir != null) {
gradleSourceSet.setSourceOutputDir(sourceOutputDir);
outputsToSourceSet.put(sourceOutputDir, gradleSourceSet);
exclusionFromDependencies.add(sourceOutputDir);
}

Expand All @@ -124,6 +131,7 @@ public Object buildAll(String modelName, Project rootProject) {
File resourceOutputDir = sourceSet.getOutput().getResourcesDir();
if (resourceOutputDir != null) {
gradleSourceSet.setResourceOutputDir(resourceOutputDir);
outputsToSourceSet.put(resourceOutputDir, gradleSourceSet);
exclusionFromDependencies.add(resourceOutputDir);
}

Expand Down Expand Up @@ -153,9 +161,35 @@ public Object buildAll(String modelName, Project rootProject) {
}
}
});

if (!sourceSets.isEmpty()) {
// get all archive tasks for this project and find the dirs that are included in the archive
TaskCollection<AbstractArchiveTask> archiveTasks =
project.getTasks().withType(AbstractArchiveTask.class);
for (AbstractArchiveTask archiveTask : archiveTasks) {
Set<Object> archiveSourcePaths = getArchiveSourcePaths(archiveTask.getRootSpec());
for (Object sourcePath : archiveSourcePaths) {
sourceSets.forEach(sourceSet -> {
DefaultGradleSourceSet gradleSourceSet = cache.getGradleSourceSet(sourceSet);
if (gradleSourceSet == null) {
return;
}

if (sourceSet.getOutput().equals(sourcePath)) {
File archiveFile;
if (GradleVersion.current().compareTo(GradleVersion.version("5.1")) >= 0) {
archiveFile = archiveTask.getArchiveFile().get().getAsFile();
} else {
archiveFile = archiveTask.getArchivePath();
}
outputsToSourceSet.put(archiveFile, gradleSourceSet);
}
});
}
}
}
}

setSourceSetDependencies(cache);
setModuleDependencies(cache, exclusionFromDependencies);

for (SourceSet sourceSet : cache.getAllSourceSets()) {
Expand All @@ -172,7 +206,6 @@ public Object buildAll(String modelName, Project rootProject) {
Map<String, LanguageExtension> extensions = new HashMap<>();
for (LanguageModelBuilder languageModelBuilder :
GradleBuildServerPlugin.SUPPORTED_LANGUAGE_BUILDERS) {

if (languageModelBuilder.appliesFor(project, sourceSet)) {
LanguageExtension extension = languageModelBuilder.getExtensionsFor(project, sourceSet,
gradleSourceSet.getModuleDependencies());
Expand All @@ -182,10 +215,9 @@ public Object buildAll(String modelName, Project rootProject) {
}
}
gradleSourceSet.setExtensions(extensions);

}

return new DefaultGradleSourceSets(new LinkedList<>(cache.getAllGradleSourceSets()));
return new DefaultGradleSourceSetsMetadata(sourceSetsToClasspath, outputsToSourceSet);
}

private void setModuleDependencies(SourceSetCache cache, Set<File> exclusionFromDependencies) {
Expand All @@ -201,64 +233,6 @@ private void setModuleDependencies(SourceSetCache cache, Set<File> exclusionFrom
}
}

private void setSourceSetDependencies(SourceSetCache cache) {
// map all output dirs to their source sets
Map<File, DefaultGradleSourceSet> outputsToSourceSet = new HashMap<>();
for (DefaultGradleSourceSet sourceSet : cache.getAllGradleSourceSets()) {
if (sourceSet.getSourceOutputDir() != null) {
outputsToSourceSet.put(sourceSet.getSourceOutputDir(), sourceSet);
}
if (sourceSet.getResourceOutputDir() != null) {
outputsToSourceSet.put(sourceSet.getResourceOutputDir(), sourceSet);
}
}

// map all output jars to their source sets
for (Project project : cache.getAllProjects()) {
SourceSetContainer sourceSets = getSourceSetContainer(project);
if (sourceSets == null || sourceSets.isEmpty()) {
continue;
}

// get all archive tasks for this project and find the dirs that are included in the archive
TaskCollection<AbstractArchiveTask> archiveTasks =
project.getTasks().withType(AbstractArchiveTask.class);
for (AbstractArchiveTask archiveTask : archiveTasks) {
Set<Object> archiveSourcePaths = getArchiveSourcePaths(archiveTask.getRootSpec());
for (Object sourcePath : archiveSourcePaths) {
sourceSets.forEach(sourceSet -> {
DefaultGradleSourceSet gradleSourceSet = cache.getGradleSourceSet(sourceSet);
if (gradleSourceSet == null) {
return;
}

if (sourceSet.getOutput().equals(sourcePath)) {
File archiveFile;
if (GradleVersion.current().compareTo(GradleVersion.version("5.1")) >= 0) {
archiveFile = archiveTask.getArchiveFile().get().getAsFile();
} else {
archiveFile = archiveTask.getArchivePath();
}
outputsToSourceSet.put(archiveFile, gradleSourceSet);
}
});
}
}
}

// match any classpath entries to other project's output dirs/jars to create dependencies
for (SourceSet sourceSet : cache.getAllSourceSets()) {
Set<BuildTargetDependency> dependencies = new HashSet<>();
for (File file : sourceSet.getCompileClasspath()) {
DefaultGradleSourceSet otherSourceSet = outputsToSourceSet.get(file);
if (otherSourceSet != null) {
dependencies.add(new DefaultBuildTargetDependency(otherSourceSet));
}
}
cache.getGradleSourceSet(sourceSet).setBuildTargetDependencies(dependencies);
}
}

private SourceSetContainer getSourceSetContainer(Project project) {
if (GradleVersion.current().compareTo(GradleVersion.version("5.0")) >= 0) {
SourceSetContainer sourceSetContainer = project.getExtensions()
Expand Down Expand Up @@ -326,7 +300,7 @@ private File getSourceOutputDir(SourceSet sourceSet) {
Method getOutputDirMethod = SourceDirectorySet.class.getMethod("getOutputDir");
return (File) getOutputDirMethod.invoke(sourceSet.getJava());
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
| IllegalArgumentException | InvocationTargetException e) {
// ignore
}
} else {
Expand Down Expand Up @@ -365,7 +339,7 @@ private Set<Object> getArchiveSourcePaths(CopySpec copySpec) {
}
}
} catch (NoSuchMethodException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
| IllegalArgumentException | InvocationTargetException e) {
// cannot get archive information
}
}
Expand Down
Loading

0 comments on commit 5af4df6

Please sign in to comment.