-
Notifications
You must be signed in to change notification settings - Fork 304
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add compiled artifact class jars to the project proto.
This implements support for basic java deps with the new artifact tracker, and also adds appropriate support for more types of artifact in the future: - `ProjectProtoUpdate` is a helper class for making updates to the prooject proto, providing simpler access to the `.workspace` module and the like. - `ArtifactDirectoryBuilder` helps to construct an `ArtifactDirectoryContents` proto message and provides paths to the final artifact desinations for adding to other parts of the project proto. - `DependenciesProjectProtoUpdater` to hook this into the existing `ProjectProtoTransform`.i PiperOrigin-RevId: 613549386
- Loading branch information
1 parent
8e9f9f8
commit 955b24e
Showing
14 changed files
with
500 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
querysync/java/com/google/idea/blaze/qsync/deps/AddCompiledJavaDeps.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* Copyright 2024 The Bazel Authors. All rights reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.google.idea.blaze.qsync.deps; | ||
|
||
import com.google.common.base.Supplier; | ||
import com.google.common.collect.ImmutableCollection; | ||
import com.google.idea.blaze.qsync.artifacts.BuildArtifact; | ||
import com.google.idea.blaze.qsync.java.JavaArtifactInfo; | ||
import com.google.idea.blaze.qsync.project.ProjectProto.JarDirectory; | ||
import java.nio.file.Path; | ||
|
||
/** Adds compiled jars from dependencies to the project. */ | ||
public class AddCompiledJavaDeps implements ProjectProtoUpdateOperation { | ||
private final Supplier<ImmutableCollection<TargetBuildInfo>> builtTargetsSupplier; | ||
|
||
public AddCompiledJavaDeps(Supplier<ImmutableCollection<TargetBuildInfo>> builtTargetsSupplier) { | ||
this.builtTargetsSupplier = builtTargetsSupplier; | ||
} | ||
|
||
@Override | ||
public void update(ProjectProtoUpdate update) { | ||
ArtifactDirectoryBuilder javaDepsDir = update.artifactDirectory(Path.of("javadeps")); | ||
for (TargetBuildInfo target : builtTargetsSupplier.get()) { | ||
if (target.javaInfo().isPresent()) { | ||
JavaArtifactInfo javaInfo = target.javaInfo().get(); | ||
for (BuildArtifact jar : javaInfo.jars()) { | ||
javaDepsDir.addIfNewer(jar.path(), jar, target.buildContext()); | ||
} | ||
} | ||
} | ||
if (!javaDepsDir.isEmpty()) { | ||
update | ||
.library(JAVA_DEPS_LIB_NAME) | ||
.addClassesJar( | ||
JarDirectory.newBuilder().setPath(javaDepsDir.path().toString()).setRecursive(true)); | ||
if (!update.workspaceModule().getLibraryNameList().contains(JAVA_DEPS_LIB_NAME)) { | ||
update.workspaceModule().addLibraryName(JAVA_DEPS_LIB_NAME); | ||
} | ||
} | ||
} | ||
} |
127 changes: 127 additions & 0 deletions
127
querysync/java/com/google/idea/blaze/qsync/deps/ArtifactDirectoryBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* Copyright 2024 The Bazel Authors. All rights reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.google.idea.blaze.qsync.deps; | ||
|
||
import static com.google.common.collect.ImmutableMap.toImmutableMap; | ||
|
||
import com.google.auto.value.AutoValue; | ||
import com.google.common.base.Preconditions; | ||
import com.google.common.collect.Maps; | ||
import com.google.errorprone.annotations.CanIgnoreReturnValue; | ||
import com.google.idea.blaze.qsync.artifacts.BuildArtifact; | ||
import com.google.idea.blaze.qsync.project.ProjectPath; | ||
import com.google.idea.blaze.qsync.project.ProjectPath.Root; | ||
import com.google.idea.blaze.qsync.project.ProjectProto; | ||
import com.google.idea.blaze.qsync.project.ProjectProto.ProjectArtifact.ArtifactTransform; | ||
import java.nio.file.Path; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
/** Populates a {@link ProjectProto.ArtifactDirectoryContents} proto from build artifacts. */ | ||
public class ArtifactDirectoryBuilder { | ||
|
||
@AutoValue | ||
abstract static class Entry { | ||
|
||
abstract String destination(); | ||
|
||
abstract BuildArtifact source(); | ||
|
||
abstract DependencyBuildContext fromBuild(); | ||
|
||
abstract ArtifactTransform transform(); | ||
|
||
static Entry create( | ||
Path destination, | ||
BuildArtifact source, | ||
DependencyBuildContext fromBuild, | ||
ArtifactTransform transform) { | ||
return new AutoValue_ArtifactDirectoryBuilder_Entry( | ||
destination.toString(), source, fromBuild, transform); | ||
} | ||
|
||
ProjectProto.ProjectArtifact toProto() { | ||
return ProjectProto.ProjectArtifact.newBuilder() | ||
.setBuildArtifact( | ||
ProjectProto.BuildArtifact.newBuilder().setDigest(source().digest()).build()) | ||
.setTransform(transform()) | ||
.build(); | ||
} | ||
} | ||
|
||
private final Path path; | ||
private final Map<Path, Entry> contents = Maps.newHashMap(); | ||
|
||
/** | ||
* @param relativePath Project directory relative path of the destination directory. | ||
*/ | ||
public ArtifactDirectoryBuilder(Path relativePath) { | ||
Preconditions.checkState( | ||
!relativePath.isAbsolute(), "Expected a relative path: " + relativePath); | ||
this.path = relativePath; | ||
} | ||
|
||
/** | ||
* @return Project directory relative path of the destination directory. | ||
*/ | ||
public Path path() { | ||
return path; | ||
} | ||
|
||
/** | ||
* @return This directories root as a project path. | ||
*/ | ||
public ProjectPath root() { | ||
return ProjectPath.projectRelative(path()); | ||
} | ||
|
||
/** | ||
* Adds a new artifact to the directory is it is not already present, or was produced by a more | ||
* recent build that an existing artifact at the same location. | ||
* | ||
* @param relativePath Path to place the artifact at, relative to {@link #root()}. | ||
* @param source The artifact to put there. | ||
* @param fromBuild The build that produced the artifact. | ||
* @return The path to the final artifact, if it was added. | ||
*/ | ||
@CanIgnoreReturnValue | ||
public Optional<ProjectPath> addIfNewer( | ||
Path relativePath, BuildArtifact source, DependencyBuildContext fromBuild) { | ||
Entry existing = contents.get(relativePath); | ||
if (existing != null && existing.fromBuild().startTime().isAfter(fromBuild.startTime())) { | ||
// we already have the same artifact from a more recent build. | ||
return Optional.empty(); | ||
} | ||
Entry e = Entry.create(relativePath, source, fromBuild, ArtifactTransform.COPY); | ||
contents.put(relativePath, e); | ||
return Optional.of(ProjectPath.create(Root.PROJECT, path.resolve(relativePath))); | ||
} | ||
|
||
public boolean isEmpty() { | ||
return contents.isEmpty(); | ||
} | ||
|
||
public ProjectProto.ArtifactDirectoryContents toProto() { | ||
return ProjectProto.ArtifactDirectoryContents.newBuilder() | ||
.putAllContents( | ||
contents.values().stream().collect(toImmutableMap(Entry::destination, Entry::toProto))) | ||
.build(); | ||
} | ||
|
||
public void addTo(ProjectProto.ArtifactDirectories.Builder directories) { | ||
directories.putDirectories(path.toString(), toProto()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
querysync/java/com/google/idea/blaze/qsync/deps/DependenciesProjectProtoUpdater.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Copyright 2024 The Bazel Authors. All rights reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.google.idea.blaze.qsync.deps; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import com.google.idea.blaze.common.Context; | ||
import com.google.idea.blaze.exception.BuildException; | ||
import com.google.idea.blaze.qsync.ProjectProtoTransform; | ||
import com.google.idea.blaze.qsync.project.BuildGraphData; | ||
import com.google.idea.blaze.qsync.project.ProjectProto.Project; | ||
|
||
/** | ||
* A {@link ProjectProtoTransform} that adds built artifact information to the project proto, based | ||
* on all artifacts that have been built. | ||
*/ | ||
public class DependenciesProjectProtoUpdater implements ProjectProtoTransform { | ||
private final ImmutableList<ProjectProtoUpdateOperation> updateOperations; | ||
|
||
public DependenciesProjectProtoUpdater(NewArtifactTracker<?> dependencyTracker) { | ||
ImmutableList.Builder<ProjectProtoUpdateOperation> updateOperations = | ||
ImmutableList.<ProjectProtoUpdateOperation>builder() | ||
.add(new AddCompiledJavaDeps(dependencyTracker::getBuiltDeps)); | ||
this.updateOperations = updateOperations.build(); | ||
} | ||
|
||
@Override | ||
public Project apply(Project proto, BuildGraphData graph, Context<?> context) | ||
throws BuildException { | ||
|
||
ProjectProtoUpdate protoUpdate = new ProjectProtoUpdate(proto); | ||
for (ProjectProtoUpdateOperation op : updateOperations) { | ||
op.update(protoUpdate); | ||
} | ||
return protoUpdate.build(); | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
querysync/java/com/google/idea/blaze/qsync/deps/ProjectProtoUpdate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright 2024 The Bazel Authors. All rights reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.google.idea.blaze.qsync.deps; | ||
|
||
import com.google.common.collect.Maps; | ||
import com.google.idea.blaze.qsync.project.BlazeProjectDataStorage; | ||
import com.google.idea.blaze.qsync.project.ProjectProto; | ||
import com.google.idea.blaze.qsync.project.ProjectProto.Library; | ||
import java.nio.file.Path; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Helper class for making a number of updates to the project proto. | ||
* | ||
* <p>This class provides a convenient way of accessing and updating various interesting parts of | ||
* the project proto, such as the {@code .workspace} module and libraries by name. | ||
*/ | ||
public class ProjectProtoUpdate { | ||
|
||
private final ProjectProto.Project.Builder project; | ||
private final ProjectProto.Module.Builder workspaceModule; | ||
private final Map<String, Library.Builder> libraries = Maps.newHashMap(); | ||
private final Map<Path, ArtifactDirectoryBuilder> artifactDirs = Maps.newHashMap(); | ||
|
||
public ProjectProtoUpdate(ProjectProto.Project existingProject) { | ||
this.project = existingProject.toBuilder(); | ||
this.workspaceModule = getWorkspaceModuleBuilder(project); | ||
} | ||
|
||
private static ProjectProto.Module.Builder getWorkspaceModuleBuilder( | ||
ProjectProto.Project.Builder project) { | ||
for (int i = 0; i < project.getModulesCount(); i++) { | ||
if (project.getModules(i).getName().equals(BlazeProjectDataStorage.WORKSPACE_MODULE_NAME)) { | ||
return project.getModulesBuilder(i); | ||
} | ||
} | ||
throw new IllegalArgumentException( | ||
"Module with name " | ||
+ BlazeProjectDataStorage.WORKSPACE_MODULE_NAME | ||
+ " not found in project proto."); | ||
} | ||
|
||
public ProjectProto.Project.Builder project() { | ||
return project; | ||
} | ||
|
||
public ProjectProto.Module.Builder workspaceModule() { | ||
return workspaceModule; | ||
} | ||
|
||
/** Gets a builder for a library, creating it if it doesn't already exist. */ | ||
public ProjectProto.Library.Builder library(String name) { | ||
if (!libraries.containsKey(name)) { | ||
Optional<ProjectProto.Library.Builder> existingProto = | ||
project.getLibraryBuilderList().stream() | ||
.filter(l -> l.getName().equals(name)) | ||
.findFirst(); | ||
|
||
if (existingProto.isPresent()) { | ||
libraries.put(name, existingProto.get()); | ||
} else { | ||
libraries.put(name, project.addLibraryBuilder().setName(name)); | ||
} | ||
} | ||
return libraries.get(name); | ||
} | ||
|
||
public ArtifactDirectoryBuilder artifactDirectory(Path relativePath) { | ||
return artifactDirs.computeIfAbsent(relativePath, ArtifactDirectoryBuilder::new); | ||
} | ||
|
||
public ProjectProto.Project build() { | ||
artifactDirs.values().forEach(d -> d.addTo(project.getArtifactDirectoriesBuilder())); | ||
return project.build(); | ||
} | ||
} |
Oops, something went wrong.