Skip to content

Commit

Permalink
Implement new artifact tracker.
Browse files Browse the repository at this point in the history
This CL implements the caching and tracking of artifacts produced by the build, but does not include the corresponding update to the project proto which will follow in later CLs. As such, it is not yet working.

Modify `JavaArtifactInfo` to use a new `BuildArtifact` class in place of paths for built artifacts. This allows the digest and origin target to be included here. Populate the digests in `NewArtifactTracker` using information derived from the BES. This allows the artifacts to be requested from the `BuildArtifactCache` later on. The existing `ArtifactTrackerImpl` does not need the digests so we create temporary support for omitting them.

To persist the artifact tracker state to disk, we use a new proto definition rather than re-using the format produced by the aspect. This allows extra information to be included (e.g. the digests), and also allows the persisted state to change in future without impacting the aspect code. Add serialization and deserialization code to support this.

PiperOrigin-RevId: 613190595
  • Loading branch information
Googler authored and copybara-github committed Mar 6, 2024
1 parent 41d78a5 commit da2daad
Show file tree
Hide file tree
Showing 16 changed files with 751 additions and 56 deletions.
6 changes: 5 additions & 1 deletion base/src/com/google/idea/blaze/base/qsync/ProjectLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ public QuerySyncProject loadProject(BlazeContext context) throws BuildException
RenderJarArtifactTracker renderJarArtifactTracker;
AppInspectorArtifactTracker appInspectorArtifactTracker;
if (QuerySync.USE_NEW_BUILD_ARTIFACT_MANAGEMENT) {
artifactTracker = new NewArtifactTracker<>();
NewArtifactTracker tracker =
new NewArtifactTracker<>(
BlazeDataStorage.getProjectDataDir(importSettings).toPath(), artifactCache, executor);

artifactTracker = tracker;
renderJarArtifactTracker = new RenderJarArtifactTrackerImpl();
appInspectorArtifactTracker = new AppInspectorArtifactTrackerImpl();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import com.google.idea.blaze.exception.BuildException;
import com.google.idea.blaze.qsync.ProjectProtoTransform;
import com.google.idea.blaze.qsync.TestSourceGlobMatcher;
import com.google.idea.blaze.qsync.artifacts.BuildArtifact;
import com.google.idea.blaze.qsync.cc.CcDependenciesInfo;
import com.google.idea.blaze.qsync.deps.ArtifactTracker;
import com.google.idea.blaze.qsync.deps.OutputGroup;
Expand Down Expand Up @@ -283,10 +284,11 @@ private void loadFromDisk() {
.collect(toImmutableMap(e -> Path.of(e.getKey()), e -> Path.of(e.getValue()))));
javaArtifacts.putAll(
saved.getArtifactInfo().getArtifactsList().stream()
.map(JavaArtifactInfo::create)
.map(p -> JavaArtifactInfo.create(p, BuildArtifact.NO_DIGESTS))
.collect(toImmutableMap(JavaArtifactInfo::label, Function.identity())));
for (JavaTargetArtifacts targetArtifact : saved.getArtifactInfo().getArtifactsList()) {
JavaArtifactInfo javaArtifactInfo = JavaArtifactInfo.create(targetArtifact);
JavaArtifactInfo javaArtifactInfo =
JavaArtifactInfo.create(targetArtifact, BuildArtifact.NO_DIGESTS);
javaArtifacts.put(javaArtifactInfo.label(), javaArtifactInfo);
}
ccDepencenciesInfo = CcDependenciesInfo.create(saved.getCcCompilationInfo());
Expand Down Expand Up @@ -615,7 +617,8 @@ private ImmutableListMultimap<Boolean, OutputArtifact> getGensrcsByInclusion(
*/
private void updateMaps(Set<Label> targets, JavaArtifacts newArtifacts) {
for (JavaTargetArtifacts targetArtifacts : newArtifacts.getArtifactsList()) {
JavaArtifactInfo javaArtifactInfo = JavaArtifactInfo.create(targetArtifacts);
JavaArtifactInfo javaArtifactInfo =
JavaArtifactInfo.create(targetArtifacts, BuildArtifact.NO_DIGESTS);
javaArtifacts.put(javaArtifactInfo.label(), javaArtifactInfo);
}
for (Label label : targets) {
Expand Down Expand Up @@ -666,17 +669,17 @@ private ProjectProto.Project updateProjectProtoForJavaDeps(ProjectProto.Project
if (!projectDefinition.isIncluded(ai.label())) {
continue;
}
for (Path blazeOutRelativePath : ai.genSrcs()) {
for (BuildArtifact artifact : ai.genSrcs()) {
// TODO(mathewi) depending on `JAVA_ARCHIVE_EXTENSIONS` here exposes a design problem, we
// shouldn't have to depend on such implementation details. Figure out a better design
// for the dance between this class and the cache.
if (!JavaSourcesArchiveCacheLayout.JAVA_ARCHIVE_EXTENSIONS.contains(
FileUtilRt.getExtension(blazeOutRelativePath.toString()))) {
artifact.getExtension())) {
continue;
}
Optional<Path> artifactPath = generatedSrcFileCache.getCacheFile(blazeOutRelativePath);
Optional<Path> artifactPath = generatedSrcFileCache.getCacheFile(artifact.path());
if (artifactPath.isEmpty()) {
logger.warn("No cached artifact found for source jar " + blazeOutRelativePath);
logger.warn("No cached artifact found for source jar " + artifact.path());
continue;
}
generatedProjectSrcJars.add(
Expand Down Expand Up @@ -707,6 +710,7 @@ private ProjectProto.Project updateProjectProtoForJavaDeps(ProjectProto.Project
.filter(not(ai -> projectDefinition.isIncluded(ai.label())))
.map(JavaArtifactInfo::genSrcs)
.flatMap(List::stream)
.map(BuildArtifact::path)
.filter(ArtifactTrackerImpl::hasJarOrZipExtension)
.map(generatedExternalSrcFileCache::getCacheFile)
.filter(Optional::isPresent)
Expand Down
2 changes: 2 additions & 0 deletions querysync/java/com/google/idea/blaze/qsync/artifacts/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ java_library(
deps = [
"//querysync/java/com/google/idea/blaze/qsync/project:project_java_proto",
"//querysync/java/com/google/idea/blaze/qsync/query",
"//shared",
"//shared:artifact",
"//shared:exception",
"//third_party/auto_value",
"@com_google_guava_guava//jar",
"@com_google_protobuf//:protobuf_java",
"@jsr305_annotations//jar",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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.artifacts;

import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.io.ByteSource;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.idea.blaze.common.Label;
import com.google.idea.blaze.common.artifact.BuildArtifactCache;
import com.google.idea.blaze.exception.BuildException;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;

/**
* An artifact produced by a build.
*
* <p>This includes the digest of the artifact as indicated by bazel, it's output path and the
* target that produced it.
*
* <p>Note, for the old {@link com.google.idea.blaze.base.qsync.cache.ArtifactTrackerImpl} codepaths
* the digest will not be populated.
*/
@AutoValue
public abstract class BuildArtifact {

/**
* Special case to support legacy codepaths that don't use digests in here
*
* <p>TODO(b/323346056) remove this when the old codepaths are deleted.
*/
public static final Function<Path, String> NO_DIGESTS = p -> "";

public abstract String digest();

public abstract Path path();

public abstract Label target();

public static BuildArtifact create(String digest, Path path, Label target) {
return new AutoValue_BuildArtifact(digest, path, target);
}

public static BuildArtifact create(Path path, Label target, Function<Path, String> digestMap) {
String digest =
Preconditions.checkNotNull(digestMap.apply(path), "No digest for %s from %s", path, target);
if (digestMap != NO_DIGESTS) {
Preconditions.checkState(!digest.isEmpty(), "Empty digest for %s from %s", path, target);
}
return create(digest, path, target);
}

public ByteSource blockingGetFrom(BuildArtifactCache cache) throws BuildException {
try {
return Uninterruptibles.getUninterruptibly(
cache
.get(digest())
.orElseThrow(() -> new BuildException("Artifact %s missing from the cache" + this)));
} catch (ExecutionException e) {
throw new BuildException("Failed to get artifact " + this, e);
}
}

public String getExtension() {
String fileName = path().getFileName().toString();
int lastDot = fileName.lastIndexOf('.');
if (lastDot == -1) {
return "";
}
return fileName.substring(lastDot + 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.idea.blaze.common.Label;
import com.google.idea.blaze.common.vcs.VcsState;
import com.google.idea.blaze.qsync.artifacts.BuildArtifact;
import com.google.idea.blaze.qsync.java.ArtifactTrackerProto;
import com.google.idea.blaze.qsync.java.JavaArtifactInfo;
import com.google.idea.blaze.qsync.java.cc.CcCompilationInfoOuterClass.CcToolchainInfo;
import com.google.idea.blaze.qsync.project.SnapshotDeserializer;
import java.nio.file.Path;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/** Deserializes {@link NewArtifactTracker} state from a proto. */
public class ArtifactTrackerStateDeserializer {

private final ImmutableMap.Builder<Label, TargetBuildInfo> map = ImmutableMap.builder();
private final Map<String, DependencyBuildContext> buildContexts = Maps.newHashMap();
private final ImmutableMap.Builder<String, CcToolchainInfo> ccToolchainMap =
ImmutableMap.builder();

public void visit(ArtifactTrackerProto.ArtifactTrackerState proto) {
proto.getBuildContextsList().forEach(this::visitBuildContext);
proto.getBuiltDepsMap().entrySet().forEach(this::visitTargetBuildInfo);
}

public ImmutableMap<Label, TargetBuildInfo> getBuiltDepsMap() {
return map.buildOrThrow();
}

private void visitBuildContext(ArtifactTrackerProto.BuildContext buildContext) {
Optional<VcsState> vcsState = Optional.empty();
if (buildContext.hasVcsState()) {
vcsState = Optional.of(SnapshotDeserializer.convertVcsState(buildContext.getVcsState()));
}
buildContexts.put(
buildContext.getBuildId(),
DependencyBuildContext.create(
buildContext.getBuildId(),
Instant.ofEpochMilli(buildContext.getStartTimeMillis()),
vcsState));
}

private void visitTargetBuildInfo(Map.Entry<String, ArtifactTrackerProto.TargetBuildInfo> entry) {
ArtifactTrackerProto.TargetBuildInfo proto = entry.getValue();
DependencyBuildContext buildContext =
Preconditions.checkNotNull(buildContexts.get(proto.getBuildId()));
Label owner = Label.of(entry.getKey());
if (proto.hasJavaArtifacts()) {
map.put(
owner,
TargetBuildInfo.forJavaTarget(
convertJavaArtifactInfo(owner, proto.getJavaArtifacts()), buildContext));
}
}

private JavaArtifactInfo convertJavaArtifactInfo(
Label owner, ArtifactTrackerProto.JavaArtifacts proto) {
return JavaArtifactInfo.builder()
.setLabel(owner)
.setJars(toArtifactList(proto.getJarsList(), owner))
.setIdeAars(toArtifactList(proto.getIdeAarsList(), owner))
.setGenSrcs(toArtifactList(proto.getGenSrcsList(), owner))
.setSources(proto.getSourcesList().stream().map(Path::of).collect(toImmutableSet()))
.setSrcJars(proto.getSrcJarsList().stream().map(Path::of).collect(toImmutableSet()))
.setAndroidResourcesPackage(proto.getAndroidResourcesPackage())
.build();
}

private ImmutableList<BuildArtifact> toArtifactList(
List<ArtifactTrackerProto.Artifact> protos, Label owner) {
return protos.stream()
.map(a -> BuildArtifact.create(a.getDigest(), Path.of(a.getPath()), owner))
.collect(toImmutableList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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.ImmutableList.toImmutableList;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.idea.blaze.common.Label;
import com.google.idea.blaze.qsync.artifacts.BuildArtifact;
import com.google.idea.blaze.qsync.java.ArtifactTrackerProto;
import com.google.idea.blaze.qsync.java.ArtifactTrackerProto.Artifact;
import com.google.idea.blaze.qsync.java.ArtifactTrackerProto.ArtifactTrackerState;
import com.google.idea.blaze.qsync.java.ArtifactTrackerProto.BuildContext;
import com.google.idea.blaze.qsync.java.JavaArtifactInfo;
import com.google.idea.blaze.qsync.project.SnapshotSerializer;
import java.nio.file.Path;
import java.util.Map;
import java.util.Set;

/** Serializes {@link NewArtifactTracker} state to a proto. */
public class ArtifactTrackerStateSerializer {

public static final int VERSION = 1;

private final ArtifactTrackerProto.ArtifactTrackerState.Builder proto =
ArtifactTrackerProto.ArtifactTrackerState.newBuilder().setVersion(VERSION);
private final Set<String> buildIdsSeen = Sets.newHashSet();

@CanIgnoreReturnValue
public ArtifactTrackerStateSerializer visitDepsMap(Map<Label, TargetBuildInfo> builtDeps) {
builtDeps.entrySet().forEach(e -> visitTargetBuildInfo(e.getKey(), e.getValue()));
return this;
}

public ArtifactTrackerState toProto() {
return proto.build();
}

private void visitTargetBuildInfo(Label target, TargetBuildInfo targetBuildInfo) {
visitBuildContext(targetBuildInfo.buildContext());

ArtifactTrackerProto.TargetBuildInfo.Builder builder =
ArtifactTrackerProto.TargetBuildInfo.newBuilder();
builder.setBuildId(targetBuildInfo.buildContext().buildId());
targetBuildInfo.javaInfo().ifPresent(ji -> visitJavaInfo(ji, builder));
proto.putBuiltDeps(target.toString(), builder.build());
}

private void visitBuildContext(DependencyBuildContext buildContext) {
if (buildIdsSeen.add(buildContext.buildId())) {
BuildContext.Builder builder =
proto
.addBuildContextsBuilder()
.setStartTimeMillis(buildContext.startTime().toEpochMilli())
.setBuildId(buildContext.buildId());
buildContext
.vcsState()
.ifPresent(vcs -> SnapshotSerializer.visitVcsState(vcs, builder.getVcsStateBuilder()));
}
}

private void visitJavaInfo(
JavaArtifactInfo javaInfo, ArtifactTrackerProto.TargetBuildInfo.Builder builder) {
builder
.getJavaArtifactsBuilder()
.addAllGenSrcs(toProtos(javaInfo.genSrcs()))
.addAllIdeAars(toProtos(javaInfo.ideAars()))
.addAllJars(toProtos(javaInfo.jars()))
.addAllSources(javaInfo.sources().stream().map(Path::toString).collect(toImmutableList()))
.addAllSrcJars(javaInfo.srcJars().stream().map(Path::toString).collect(toImmutableList()))
.setAndroidResourcesPackage(javaInfo.androidResourcesPackage());
}

private ImmutableList<Artifact> toProtos(ImmutableList<BuildArtifact> artifacts) {
return artifacts.stream()
.map(
artifact ->
ArtifactTrackerProto.Artifact.newBuilder()
.setDigest(artifact.digest())
.setPath(artifact.path().toString())
.build())
.collect(toImmutableList());
}
}
Loading

0 comments on commit da2daad

Please sign in to comment.