getBugreportFiles() {
private void saveState() throws IOException {
ArtifactTrackerStateSerializer serializer;
synchronized (stateLock) {
- serializer = new ArtifactTrackerStateSerializer().visitDepsMap(builtDeps);
+ serializer =
+ new ArtifactTrackerStateSerializer()
+ .visitDepsMap(builtDeps)
+ .visitToolchainMap(ccToolchainMap);
}
// TODO(b/328563748) write to a new file and then rename to avoid the risk of truncation.
try (OutputStream stream = new GZIPOutputStream(Files.newOutputStream(stateFile))) {
@@ -205,6 +236,7 @@ private void loadState() {
synchronized (stateLock) {
builtDeps.putAll(deserializer.getBuiltDepsMap());
+ ccToolchainMap.putAll(deserializer.getCcToolchainMap());
}
}
}
diff --git a/querysync/java/com/google/idea/blaze/qsync/deps/ProjectProtoUpdate.java b/querysync/java/com/google/idea/blaze/qsync/deps/ProjectProtoUpdate.java
new file mode 100644
index 00000000000..27b260602d8
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/deps/ProjectProtoUpdate.java
@@ -0,0 +1,109 @@
+/*
+ * 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.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.idea.blaze.common.Context;
+import com.google.idea.blaze.qsync.project.BlazeProjectDataStorage;
+import com.google.idea.blaze.qsync.project.BuildGraphData;
+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.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.
+ *
+ * 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 BuildGraphData buildGraph;
+ private final Context> context;
+ private final ProjectProto.Module.Builder workspaceModule;
+ private final Map libraries = Maps.newHashMap();
+ private final Map artifactDirs = Maps.newHashMap();
+
+ public ProjectProtoUpdate(
+ ProjectProto.Project existingProject, BuildGraphData graph, Context> context) {
+ this.project = existingProject.toBuilder();
+ this.buildGraph = graph;
+ this.context = context;
+ 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 Context> context() {
+ return context;
+ }
+
+ public ProjectProto.Project.Builder project() {
+ return project;
+ }
+
+ public BuildGraphData buildGraph() {
+ return buildGraph;
+ }
+
+ 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 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(ProjectPath path) {
+ Preconditions.checkState(path.rootType() == Root.PROJECT);
+ return artifactDirs.computeIfAbsent(path.relativePath(), ArtifactDirectoryBuilder::new);
+ }
+
+ public ProjectProto.Project build() {
+ artifactDirs.values().forEach(d -> d.addTo(project.getArtifactDirectoriesBuilder()));
+ return project.build();
+ }
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/deps/ProjectProtoUpdateOperation.java b/querysync/java/com/google/idea/blaze/qsync/deps/ProjectProtoUpdateOperation.java
new file mode 100644
index 00000000000..38919a7c9d8
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/deps/ProjectProtoUpdateOperation.java
@@ -0,0 +1,31 @@
+/*
+ * 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.ImmutableSet;
+import com.google.idea.blaze.exception.BuildException;
+
+/**
+ * An update to the project proto that operates on a {@link ProjectProtoUpdate}. Also defines some
+ * constants that are useful to implementations.
+ */
+public interface ProjectProtoUpdateOperation {
+
+ String JAVA_DEPS_LIB_NAME = ".dependencies";
+ ImmutableSet JAVA_ARCHIVE_EXTENSIONS = ImmutableSet.of("jar", "srcjar");
+
+ void update(ProjectProtoUpdate update) throws BuildException;
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/deps/TargetBuildInfo.java b/querysync/java/com/google/idea/blaze/qsync/deps/TargetBuildInfo.java
index f23cb013905..d0cdf12cbc0 100644
--- a/querysync/java/com/google/idea/blaze/qsync/deps/TargetBuildInfo.java
+++ b/querysync/java/com/google/idea/blaze/qsync/deps/TargetBuildInfo.java
@@ -16,19 +16,40 @@
package com.google.idea.blaze.qsync.deps;
import com.google.auto.value.AutoValue;
-import com.google.idea.blaze.qsync.java.JavaArtifactInfo;
import java.util.Optional;
+/** Information about a target that was extracted from the build at dependencies build time. */
@AutoValue
-abstract class TargetBuildInfo {
- abstract Optional javaInfo();
+public abstract class TargetBuildInfo {
+ public abstract Optional javaInfo();
- // TODO(b/323346056) Add cc info here.
+ public abstract Optional ccInfo();
- abstract DependencyBuildContext buildContext();
+ public abstract DependencyBuildContext buildContext();
- static TargetBuildInfo forJavaTarget(
+ public static TargetBuildInfo forJavaTarget(
JavaArtifactInfo javaInfo, DependencyBuildContext buildContext) {
- return new AutoValue_TargetBuildInfo(Optional.of(javaInfo), buildContext);
+ return builder().buildContext(buildContext).javaInfo(javaInfo).build();
+ }
+
+ public static TargetBuildInfo forCcTarget(
+ CcCompilationInfo targetInfo, DependencyBuildContext buildContext) {
+ return builder().buildContext(buildContext).ccInfo(targetInfo).build();
+ }
+
+ static Builder builder() {
+ return new AutoValue_TargetBuildInfo.Builder();
+ }
+
+ /** Builder for {@link TargetBuildInfo}. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder javaInfo(JavaArtifactInfo javaInfo);
+
+ public abstract Builder ccInfo(CcCompilationInfo ccInfo);
+
+ public abstract Builder buildContext(DependencyBuildContext buildContext);
+
+ public abstract TargetBuildInfo build();
}
}
diff --git a/querysync/java/com/google/idea/blaze/qsync/deps/artifact_tracker_state.proto b/querysync/java/com/google/idea/blaze/qsync/deps/artifact_tracker_state.proto
index 65e9cce4832..27c8282baad 100644
--- a/querysync/java/com/google/idea/blaze/qsync/deps/artifact_tracker_state.proto
+++ b/querysync/java/com/google/idea/blaze/qsync/deps/artifact_tracker_state.proto
@@ -16,6 +16,7 @@ syntax = "proto3";
package ij.qsync.deps;
+import "querysync/java/com/google/idea/blaze/qsync/project/project.proto";
import "querysync/java/com/google/idea/blaze/qsync/project/snapshot.proto";
// option java_api_version = 2;
@@ -28,6 +29,7 @@ message ArtifactTrackerState {
map built_deps = 2;
// Build contexts. These are referred to by their ID from TargetBuildInfo
repeated BuildContext build_contexts = 3;
+ map cc_toolchains = 4;
}
message TargetBuildInfo {
@@ -35,6 +37,7 @@ message TargetBuildInfo {
string build_id = 1;
// Java artifacts produced by the build
JavaArtifacts java_artifacts = 2;
+ CcCompilationInfo cc_info = 3;
}
message Artifact {
@@ -60,3 +63,23 @@ message BuildContext {
int64 start_time_millis = 2;
VcsState vcs_state = 3;
}
+
+message CcCompilationInfo {
+ repeated string defines = 1;
+ repeated ProjectPath include_directories = 2;
+ repeated ProjectPath quote_include_directories = 3;
+ repeated ProjectPath sysytem_include_directories = 4;
+ repeated ProjectPath framework_include_directories = 5;
+ repeated Artifact gen_headers = 6;
+ string toolchain_id = 7;
+}
+
+message CcToolchain {
+ string compiler = 1;
+ ProjectPath compiler_executable = 2;
+ string cpu = 3;
+ string target_gnu_system_name = 4;
+ repeated ProjectPath built_in_include_directories = 5;
+ repeated string c_options = 6;
+ repeated string cpp_options = 7;
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AddAndroidResPackages.java b/querysync/java/com/google/idea/blaze/qsync/java/AddAndroidResPackages.java
new file mode 100644
index 00000000000..45db2e7cf8f
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AddAndroidResPackages.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 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.java;
+
+import static java.util.function.Predicate.not;
+
+import com.google.common.base.Strings;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.exception.BuildException;
+import com.google.idea.blaze.qsync.deps.ArtifactTracker.State;
+import com.google.idea.blaze.qsync.deps.JavaArtifactInfo;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdate;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdateOperation;
+import com.google.idea.blaze.qsync.deps.TargetBuildInfo;
+import java.util.Optional;
+
+/**
+ * Updates the project proto with the android resources packages extracted by the aspect in a
+ * dependencies build.
+ */
+public class AddAndroidResPackages implements ProjectProtoUpdateOperation {
+
+ private final Supplier artifactStateSupplier;
+
+ public AddAndroidResPackages(Supplier artifactStateSupplier) {
+ this.artifactStateSupplier = artifactStateSupplier;
+ }
+
+ @Override
+ public void update(ProjectProtoUpdate update) throws BuildException {
+ update
+ .workspaceModule()
+ .addAllAndroidSourcePackages(
+ artifactStateSupplier.get().depsMap().values().stream()
+ .map(TargetBuildInfo::javaInfo)
+ .flatMap(Optional::stream)
+ .map(JavaArtifactInfo::androidResourcesPackage)
+ .filter(not(Strings::isNullOrEmpty))
+ .distinct()
+ .collect(ImmutableList.toImmutableList()));
+ }
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AddCompiledJavaDeps.java b/querysync/java/com/google/idea/blaze/qsync/java/AddCompiledJavaDeps.java
new file mode 100644
index 00000000000..1ce4b1aa033
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AddCompiledJavaDeps.java
@@ -0,0 +1,58 @@
+/*
+ * 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.java;
+
+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.deps.ArtifactDirectories;
+import com.google.idea.blaze.qsync.deps.ArtifactDirectoryBuilder;
+import com.google.idea.blaze.qsync.deps.JavaArtifactInfo;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdate;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdateOperation;
+import com.google.idea.blaze.qsync.deps.TargetBuildInfo;
+import com.google.idea.blaze.qsync.project.ProjectProto.JarDirectory;
+
+/** Adds compiled jars from dependencies to the project. */
+public class AddCompiledJavaDeps implements ProjectProtoUpdateOperation {
+ private final Supplier> builtTargetsSupplier;
+
+ public AddCompiledJavaDeps(Supplier> builtTargetsSupplier) {
+ this.builtTargetsSupplier = builtTargetsSupplier;
+ }
+
+ @Override
+ public void update(ProjectProtoUpdate update) {
+ ArtifactDirectoryBuilder javaDepsDir = update.artifactDirectory(ArtifactDirectories.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);
+ }
+ }
+ }
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AddDependencyAars.java b/querysync/java/com/google/idea/blaze/qsync/java/AddDependencyAars.java
new file mode 100644
index 00000000000..70d86a1bc26
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AddDependencyAars.java
@@ -0,0 +1,113 @@
+/*
+ * 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.java;
+
+import com.google.common.base.Supplier;
+import com.google.idea.blaze.common.artifact.BuildArtifactCache;
+import com.google.idea.blaze.exception.BuildException;
+import com.google.idea.blaze.qsync.artifacts.BuildArtifact;
+import com.google.idea.blaze.qsync.deps.ArtifactDirectories;
+import com.google.idea.blaze.qsync.deps.ArtifactDirectoryBuilder;
+import com.google.idea.blaze.qsync.deps.ArtifactTracker;
+import com.google.idea.blaze.qsync.deps.JavaArtifactInfo;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdate;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdateOperation;
+import com.google.idea.blaze.qsync.deps.TargetBuildInfo;
+import com.google.idea.blaze.qsync.project.ProjectDefinition;
+import com.google.idea.blaze.qsync.project.ProjectPath;
+import com.google.idea.blaze.qsync.project.ProjectProto.ExternalAndroidLibrary;
+import com.google.idea.blaze.qsync.project.ProjectProto.ProjectArtifact.ArtifactTransform;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Adds external {@code .aar} files to the project proto as {@link ExternalAndroidLibrary}s. This
+ * allows resources references to external libraries to be resolved in Android Studio.
+ */
+public class AddDependencyAars implements ProjectProtoUpdateOperation {
+
+ private final Supplier artifactStateSupplier;
+ private final BuildArtifactCache buildCache;
+ private final ProjectDefinition projectDefinition;
+ private final AndroidManifestParser manifestParser;
+
+ public AddDependencyAars(
+ Supplier artifactStateSupplier,
+ BuildArtifactCache buildCache,
+ ProjectDefinition projectDefinition,
+ AndroidManifestParser manifestParser) {
+ this.artifactStateSupplier = artifactStateSupplier;
+ this.buildCache = buildCache;
+ this.projectDefinition = projectDefinition;
+ this.manifestParser = manifestParser;
+ }
+
+ @Override
+ public void update(ProjectProtoUpdate update) throws BuildException {
+ ArtifactDirectoryBuilder aarDir = null;
+ for (TargetBuildInfo target : artifactStateSupplier.get().depsMap().values()) {
+ if (target.javaInfo().isEmpty()) {
+ continue;
+ }
+ JavaArtifactInfo javaInfo = target.javaInfo().get();
+ if (projectDefinition.isIncluded(javaInfo.label())) {
+ continue;
+ }
+ for (BuildArtifact aar : javaInfo.ideAars()) {
+ if (aarDir == null) {
+ aarDir = update.artifactDirectory(ArtifactDirectories.DEFAULT);
+ }
+ String packageName = readPackageFromAarManifest(aar);
+ ProjectPath dest =
+ aarDir
+ .addIfNewer(aar.path(), aar, target.buildContext(), ArtifactTransform.UNZIP)
+ .orElse(null);
+ if (dest != null) {
+ update
+ .workspaceModule()
+ .addAndroidExternalLibraries(
+ ExternalAndroidLibrary.newBuilder()
+ .setName(aar.path().toString().replace('/', '_'))
+ .setLocation(dest.toProto())
+ .setManifestFile(dest.resolveChild(Path.of("AndroidManifest.xml")).toProto())
+ .setResFolder(dest.resolveChild(Path.of("res")).toProto())
+ .setSymbolFile(dest.resolveChild(Path.of("R.txt")).toProto())
+ .setPackageName(packageName));
+ }
+ }
+ }
+ }
+
+ public String readPackageFromAarManifest(BuildArtifact aar) throws BuildException {
+ try (ZipInputStream zis =
+ new ZipInputStream(aar.blockingGetFrom(buildCache).openBufferedStream())) {
+ ZipEntry entry;
+ while ((entry = zis.getNextEntry()) != null) {
+ if (entry.getName().equals("AndroidManifest.xml")) {
+ return manifestParser.readPackageNameFrom(zis);
+ }
+ }
+ } catch (IOException e) {
+ throw new BuildException(
+ String.format("Failed to read aar file %s (built by %s)", aar.path(), aar.target()), e);
+ }
+ throw new BuildException(
+ String.format(
+ "Failed to find AndroidManifest.xml in %s (built by %s)", aar.path(), aar.target()));
+ }
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AddDependencyGenSrcsJars.java b/querysync/java/com/google/idea/blaze/qsync/java/AddDependencyGenSrcsJars.java
new file mode 100644
index 00000000000..b773c1a9f63
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AddDependencyGenSrcsJars.java
@@ -0,0 +1,91 @@
+/*
+ * 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.java;
+
+import static com.google.idea.blaze.qsync.java.SrcJarInnerPathFinder.AllowPackagePrefixes.EMPTY_PACKAGE_PREFIXES_ONLY;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableCollection;
+import com.google.idea.blaze.common.artifact.BuildArtifactCache;
+import com.google.idea.blaze.exception.BuildException;
+import com.google.idea.blaze.qsync.artifacts.BuildArtifact;
+import com.google.idea.blaze.qsync.deps.ArtifactDirectories;
+import com.google.idea.blaze.qsync.deps.JavaArtifactInfo;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdate;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdateOperation;
+import com.google.idea.blaze.qsync.deps.TargetBuildInfo;
+import com.google.idea.blaze.qsync.project.ProjectDefinition;
+import com.google.idea.blaze.qsync.project.ProjectPath;
+import com.google.idea.blaze.qsync.project.ProjectProto.LibrarySource;
+
+/**
+ * Adds generated {@code .srcjar} files from external dependencies to the {@code .dependencies}
+ * library. This means that when navigating to these dependencies, we see the generated sources
+ * rather than decompiled code.
+ */
+public class AddDependencyGenSrcsJars implements ProjectProtoUpdateOperation {
+
+ private final Supplier> builtTargetsSupplier;
+ private final BuildArtifactCache buildCache;
+ private final ProjectDefinition projectDefinition;
+ private final SrcJarInnerPathFinder srcJarInnerPathFinder;
+
+ public AddDependencyGenSrcsJars(
+ Supplier> builtTargetsSupplier,
+ ProjectDefinition projectDefinition,
+ BuildArtifactCache buildCache,
+ SrcJarInnerPathFinder srcJarInnerPathFinder) {
+ this.builtTargetsSupplier = builtTargetsSupplier;
+ this.projectDefinition = projectDefinition;
+ this.buildCache = buildCache;
+ this.srcJarInnerPathFinder = srcJarInnerPathFinder;
+ }
+
+ @Override
+ public void update(ProjectProtoUpdate update) throws BuildException {
+ for (TargetBuildInfo target : builtTargetsSupplier.get()) {
+ if (target.javaInfo().isEmpty()) {
+ continue;
+ }
+ JavaArtifactInfo javaInfo = target.javaInfo().get();
+ if (projectDefinition.isIncluded(javaInfo.label())) {
+ continue;
+ }
+ for (BuildArtifact genSrc : javaInfo.genSrcs()) {
+ if (!JAVA_ARCHIVE_EXTENSIONS.contains(genSrc.getExtension())) {
+ continue;
+ }
+
+ ProjectPath projectArtifact =
+ update
+ .artifactDirectory(ArtifactDirectories.DEFAULT)
+ .addIfNewer(genSrc.path(), genSrc, target.buildContext())
+ .orElse(null);
+
+ if (projectArtifact != null) {
+ srcJarInnerPathFinder
+ .findInnerJarPaths(genSrc.blockingGetFrom(buildCache), EMPTY_PACKAGE_PREFIXES_ONLY)
+ .stream()
+ .map(p -> p.path)
+ .map(projectArtifact::withInnerJarPath)
+ .map(ProjectPath::toProto)
+ .map(LibrarySource.newBuilder()::setSrcjar)
+ .forEach(update.library(JAVA_DEPS_LIB_NAME)::addSources);
+ }
+ }
+ }
+ }
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AddDependencySrcJars.java b/querysync/java/com/google/idea/blaze/qsync/java/AddDependencySrcJars.java
new file mode 100644
index 00000000000..045775ff947
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AddDependencySrcJars.java
@@ -0,0 +1,78 @@
+/*
+ * 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.java;
+
+import static com.google.idea.blaze.qsync.java.SrcJarInnerPathFinder.AllowPackagePrefixes.EMPTY_PACKAGE_PREFIXES_ONLY;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableCollection;
+import com.google.idea.blaze.exception.BuildException;
+import com.google.idea.blaze.qsync.deps.JavaArtifactInfo;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdate;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdateOperation;
+import com.google.idea.blaze.qsync.deps.TargetBuildInfo;
+import com.google.idea.blaze.qsync.project.ProjectDefinition;
+import com.google.idea.blaze.qsync.project.ProjectPath;
+import com.google.idea.blaze.qsync.project.ProjectProto.LibrarySource;
+import java.nio.file.Path;
+
+/**
+ * Adds checked-in {@code .srcjar} files from external dependencies to the project proto. This
+ * allows those sources to be shown in the IDE instead of decompiled class files.
+ */
+public class AddDependencySrcJars implements ProjectProtoUpdateOperation {
+
+ private final Supplier> builtTargetsSupplier;
+ private final ProjectDefinition projectDefinition;
+ private final ProjectPath.Resolver pathResolver;
+ private final SrcJarInnerPathFinder srcJarInnerPathFinder;
+
+ public AddDependencySrcJars(
+ Supplier> builtTargetsSupplier,
+ ProjectDefinition projectDefinition,
+ ProjectPath.Resolver pathResolver,
+ SrcJarInnerPathFinder srcJarInnerPathFinder) {
+ this.builtTargetsSupplier = builtTargetsSupplier;
+ this.projectDefinition = projectDefinition;
+ this.pathResolver = pathResolver;
+ this.srcJarInnerPathFinder = srcJarInnerPathFinder;
+ }
+
+ @Override
+ public void update(ProjectProtoUpdate update) throws BuildException {
+ for (TargetBuildInfo target : builtTargetsSupplier.get()) {
+ if (target.javaInfo().isEmpty()) {
+ continue;
+ }
+ JavaArtifactInfo javaInfo = target.javaInfo().get();
+ if (projectDefinition.isIncluded(javaInfo.label())) {
+ continue;
+ }
+ for (Path srcJar : javaInfo.srcJars()) {
+ // these are workspace relative srcjar paths.
+ ProjectPath jarPath = ProjectPath.workspaceRelative(srcJar);
+ srcJarInnerPathFinder
+ .findInnerJarPaths(pathResolver.resolve(jarPath).toFile(), EMPTY_PACKAGE_PREFIXES_ONLY)
+ .stream()
+ .map(p -> p.path)
+ .map(jarPath::withInnerJarPath)
+ .map(ProjectPath::toProto)
+ .map(LibrarySource.newBuilder()::setSrcjar)
+ .forEach(update.library(JAVA_DEPS_LIB_NAME)::addSources);
+ }
+ }
+ }
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AddProjectGenSrcJars.java b/querysync/java/com/google/idea/blaze/qsync/java/AddProjectGenSrcJars.java
new file mode 100644
index 00000000000..cff816e9d63
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AddProjectGenSrcJars.java
@@ -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.java;
+
+import static com.google.idea.blaze.qsync.java.SrcJarInnerPathFinder.AllowPackagePrefixes.ALLOW_NON_EMPTY_PACKAGE_PREFIXES;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableCollection;
+import com.google.idea.blaze.common.artifact.BuildArtifactCache;
+import com.google.idea.blaze.exception.BuildException;
+import com.google.idea.blaze.qsync.artifacts.BuildArtifact;
+import com.google.idea.blaze.qsync.deps.ArtifactDirectories;
+import com.google.idea.blaze.qsync.deps.JavaArtifactInfo;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdate;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdateOperation;
+import com.google.idea.blaze.qsync.deps.TargetBuildInfo;
+import com.google.idea.blaze.qsync.java.SrcJarInnerPathFinder.JarPath;
+import com.google.idea.blaze.qsync.project.ProjectDefinition;
+import com.google.idea.blaze.qsync.project.ProjectPath;
+import com.google.idea.blaze.qsync.project.ProjectProto;
+import com.google.idea.blaze.qsync.project.TestSourceGlobMatcher;
+
+/**
+ * Adds in-project generated {@code .srcjar} files to the project proto. This allows these sources
+ * to be resolved and viewed.
+ */
+public class AddProjectGenSrcJars implements ProjectProtoUpdateOperation {
+
+ private final Supplier> builtTargetsSupplier;
+ private final BuildArtifactCache buildCache;
+ private final ProjectDefinition projectDefinition;
+ private final SrcJarInnerPathFinder srcJarInnerPathFinder;
+ private final TestSourceGlobMatcher testSourceMatcher;
+
+ public AddProjectGenSrcJars(
+ Supplier> builtTargetsSupplier,
+ ProjectDefinition projectDefinition,
+ BuildArtifactCache buildCache,
+ SrcJarInnerPathFinder srcJarInnerPathFinder) {
+ this.builtTargetsSupplier = builtTargetsSupplier;
+ this.projectDefinition = projectDefinition;
+ this.buildCache = buildCache;
+ this.srcJarInnerPathFinder = srcJarInnerPathFinder;
+ testSourceMatcher = TestSourceGlobMatcher.create(projectDefinition);
+ }
+
+ @Override
+ public void update(ProjectProtoUpdate update) throws BuildException {
+ for (TargetBuildInfo target : builtTargetsSupplier.get()) {
+ if (target.javaInfo().isEmpty()) {
+ continue;
+ }
+ JavaArtifactInfo javaInfo = target.javaInfo().get();
+ if (!projectDefinition.isIncluded(javaInfo.label())) {
+ continue;
+ }
+ for (BuildArtifact genSrc : javaInfo.genSrcs()) {
+ if (JAVA_ARCHIVE_EXTENSIONS.contains(genSrc.getExtension())) {
+ // a zip of generated sources
+ ProjectPath added =
+ update
+ .artifactDirectory(ArtifactDirectories.DEFAULT)
+ .addIfNewer(genSrc.path(), genSrc, target.buildContext())
+ .orElse(null);
+ if (added != null) {
+ ProjectProto.ContentEntry.Builder genSrcJarContentEntry =
+ ProjectProto.ContentEntry.newBuilder().setRoot(added.toProto());
+ for (JarPath innerPath :
+ srcJarInnerPathFinder.findInnerJarPaths(
+ genSrc.blockingGetFrom(buildCache), ALLOW_NON_EMPTY_PACKAGE_PREFIXES)) {
+
+ genSrcJarContentEntry.addSources(
+ ProjectProto.SourceFolder.newBuilder()
+ .setProjectPath(added.withInnerJarPath(innerPath.path).toProto())
+ .setIsGenerated(true)
+ .setIsTest(testSourceMatcher.matches(genSrc.target().getPackage()))
+ .setPackagePrefix(innerPath.packagePrefix)
+ .build());
+ }
+ update.workspaceModule().addContentEntries(genSrcJarContentEntry.build());
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AddProjectGenSrcs.java b/querysync/java/com/google/idea/blaze/qsync/java/AddProjectGenSrcs.java
new file mode 100644
index 00000000000..bc370d58082
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AddProjectGenSrcs.java
@@ -0,0 +1,202 @@
+/*
+ * 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.java;
+
+import static java.util.stream.Collectors.joining;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.common.PrintOutput;
+import com.google.idea.blaze.common.artifact.BuildArtifactCache;
+import com.google.idea.blaze.exception.BuildException;
+import com.google.idea.blaze.qsync.artifacts.BuildArtifact;
+import com.google.idea.blaze.qsync.deps.ArtifactDirectories;
+import com.google.idea.blaze.qsync.deps.ArtifactDirectoryBuilder;
+import com.google.idea.blaze.qsync.deps.DependencyBuildContext;
+import com.google.idea.blaze.qsync.deps.JavaArtifactInfo;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdate;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdateOperation;
+import com.google.idea.blaze.qsync.deps.TargetBuildInfo;
+import com.google.idea.blaze.qsync.project.ProjectDefinition;
+import com.google.idea.blaze.qsync.project.ProjectProto;
+import com.google.idea.blaze.qsync.project.TestSourceGlobMatcher;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Collection;
+
+/**
+ * Adds generated java and kotlin source files to the project proto.
+ *
+ * This class also resolves conflicts between multiple generated source files that resolve to the
+ * same output path, i.e. that have the same java class name.
+ */
+public class AddProjectGenSrcs implements ProjectProtoUpdateOperation {
+
+ private static final ImmutableSet JAVA_SRC_EXTENSIONS = ImmutableSet.of("java", "kt");
+
+ private final Supplier> builtTargetsSupplier;
+ private final BuildArtifactCache buildCache;
+ private final ProjectDefinition projectDefinition;
+ private final PackageStatementParser packageReader;
+ private final TestSourceGlobMatcher testSourceMatcher;
+
+ public AddProjectGenSrcs(
+ Supplier> builtTargetsSupplier,
+ ProjectDefinition projectDefinition,
+ BuildArtifactCache buildCache,
+ PackageStatementParser packageReader) {
+ this.builtTargetsSupplier = builtTargetsSupplier;
+ this.projectDefinition = projectDefinition;
+ this.buildCache = buildCache;
+ this.packageReader = packageReader;
+ testSourceMatcher = TestSourceGlobMatcher.create(projectDefinition);
+ }
+
+ /**
+ * A simple holder class for a build artifact and info about the build that produced it. This is
+ * used to resolve conflicts between source files.
+ */
+ @AutoValue
+ abstract static class ArtifactWithOrigin {
+ abstract BuildArtifact artifact();
+
+ abstract DependencyBuildContext origin();
+
+ static ArtifactWithOrigin create(BuildArtifact artifact, DependencyBuildContext origin) {
+ return new AutoValue_AddProjectGenSrcs_ArtifactWithOrigin(artifact, origin);
+ }
+
+ /**
+ * When we find conflicting generated sources (same java source path), we resolve the conflict
+ * by selecting the per this method.
+ *
+ * If the files were produced by different build invocations, select the most recent.
+ * Otherwise, disambiguate using the target string.
+ */
+ int compareTo(ArtifactWithOrigin other) {
+ // Note: we do a reverse comparison for start time to ensure the newest build "wins".
+ int compare = other.origin().startTime().compareTo(origin().startTime());
+ if (compare == 0) {
+ compare = artifact().target().toString().compareTo(other.artifact().target().toString());
+ }
+ return compare;
+ }
+ }
+
+ @Override
+ public void update(ProjectProtoUpdate update) throws BuildException {
+ ArtifactDirectoryBuilder javaSrc = update.artifactDirectory(ArtifactDirectories.JAVA_GEN_SRC);
+ ArtifactDirectoryBuilder javatestsSrc =
+ update.artifactDirectory(ArtifactDirectories.JAVA_GEN_TESTSRC);
+ ArrayListMultimap srcsByJavaPath = ArrayListMultimap.create();
+ for (TargetBuildInfo target : builtTargetsSupplier.get()) {
+ if (target.javaInfo().isEmpty()) {
+ continue;
+ }
+ JavaArtifactInfo javaInfo = target.javaInfo().get();
+ if (!projectDefinition.isIncluded(javaInfo.label())) {
+ continue;
+ }
+ for (BuildArtifact genSrc : javaInfo.genSrcs()) {
+ if (JAVA_SRC_EXTENSIONS.contains(genSrc.getExtension())) {
+ String javaPackage = readJavaPackage(genSrc);
+ Path finalDest =
+ Path.of(javaPackage.replace('.', '/')).resolve(genSrc.path().getFileName());
+ srcsByJavaPath.put(finalDest, ArtifactWithOrigin.create(genSrc, target.buildContext()));
+ }
+ }
+ }
+ for (var entry : srcsByJavaPath.asMap().entrySet()) {
+ Path finalDest = entry.getKey();
+ Collection candidates = entry.getValue();
+ if (candidates.size() > 1) {
+ update
+ .context()
+ .output(
+ PrintOutput.error(
+ "WARNING: your project contains conflicting generated java sources for:\n"
+ + " %s\n"
+ + "From:\n"
+ + " %s",
+ finalDest,
+ candidates.stream()
+ .map(
+ a ->
+ String.format(
+ "%s (%s built %s ago)",
+ a.artifact().path(),
+ a.artifact().target(),
+ formatDuration(
+ Duration.between(a.origin().startTime(), Instant.now()))))
+ .collect(joining("\n "))));
+ update.context().setHasWarnings();
+ }
+
+ ArtifactWithOrigin chosen = candidates.stream().min((a, b) -> a.compareTo(b)).orElseThrow();
+ if (testSourceMatcher.matches(chosen.artifact().target().getPackage())) {
+ javatestsSrc.addIfNewer(finalDest, chosen.artifact(), chosen.origin());
+ } else {
+ javaSrc.addIfNewer(finalDest, chosen.artifact(), chosen.origin());
+ }
+ }
+ for (ArtifactDirectoryBuilder gensrcDir : ImmutableList.of(javaSrc, javatestsSrc)) {
+ if (!gensrcDir.isEmpty()) {
+ ProjectProto.ProjectPath pathProto = gensrcDir.root().toProto();
+ ProjectProto.ContentEntry.Builder genSourcesContentEntry =
+ ProjectProto.ContentEntry.newBuilder().setRoot(pathProto);
+ genSourcesContentEntry.addSources(
+ ProjectProto.SourceFolder.newBuilder()
+ .setProjectPath(pathProto)
+ .setIsGenerated(true)
+ .setIsTest(gensrcDir == javatestsSrc)
+ .setPackagePrefix(""));
+ update.workspaceModule().addContentEntries(genSourcesContentEntry.build());
+ }
+ }
+ }
+
+ /**
+ * A simple inexact duration format, returning a duration in whichever unit of (days, hours,
+ * minutes, seconds) is the first to get a non-zero figure.
+ */
+ private String formatDuration(Duration p) {
+ for (ChronoUnit unit :
+ ImmutableList.of(ChronoUnit.DAYS, ChronoUnit.HOURS, ChronoUnit.MINUTES)) {
+ long durationInUnits = p.getSeconds() / unit.getDuration().getSeconds();
+ if (durationInUnits > 0) {
+ return String.format("%d %s", durationInUnits, unit);
+ }
+ }
+ return String.format("%d seconds", p.getSeconds());
+ }
+
+ /** Parses the java package statement from a build artifact. */
+ private String readJavaPackage(BuildArtifact genSrc) throws BuildException {
+ try (InputStream javaSrcStream = genSrc.blockingGetFrom(buildCache).openStream()) {
+ return packageReader.readPackage(javaSrcStream);
+ } catch (IOException e) {
+ throw new BuildException("Failed to read package name for " + genSrc, e);
+ }
+ }
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AndroidManifestParser.java b/querysync/java/com/google/idea/blaze/qsync/java/AndroidManifestParser.java
new file mode 100644
index 00000000000..53962f4a9dd
--- /dev/null
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AndroidManifestParser.java
@@ -0,0 +1,27 @@
+/*
+ * 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.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Interface to an android manifest parser. This is used to read android manifest package names from
+ * {@code .aar} files.
+ */
+public interface AndroidManifestParser {
+ String readPackageNameFrom(InputStream in) throws IOException;
+}
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/AndroidResPackagesProjectUpdater.java b/querysync/java/com/google/idea/blaze/qsync/java/AndroidResPackagesProjectUpdater.java
index 21c911efbff..af3f2ed8771 100644
--- a/querysync/java/com/google/idea/blaze/qsync/java/AndroidResPackagesProjectUpdater.java
+++ b/querysync/java/com/google/idea/blaze/qsync/java/AndroidResPackagesProjectUpdater.java
@@ -20,6 +20,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.idea.blaze.qsync.deps.JavaArtifactInfo;
import com.google.idea.blaze.qsync.project.ProjectProto.Project;
/**
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/BUILD b/querysync/java/com/google/idea/blaze/qsync/java/BUILD
index 309d1934eb4..485655f8ce4 100644
--- a/querysync/java/com/google/idea/blaze/qsync/java/BUILD
+++ b/querysync/java/com/google/idea/blaze/qsync/java/BUILD
@@ -20,11 +20,12 @@ java_library(
name = "java",
srcs = glob(["*.java"]),
deps = [
- ":java_target_info_java_proto",
"//querysync/java/com/google/idea/blaze/qsync/artifacts",
+ "//querysync/java/com/google/idea/blaze/qsync/deps",
"//querysync/java/com/google/idea/blaze/qsync/project",
"//querysync/java/com/google/idea/blaze/qsync/project:project_java_proto",
"//shared",
+ "//shared:artifact",
"//third_party/auto_value",
"@com_google_guava_guava//jar",
],
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/GeneratedSourceProjectUpdater.java b/querysync/java/com/google/idea/blaze/qsync/java/GeneratedSourceProjectUpdater.java
index 82de40fb3bc..ac9ff77b0fa 100644
--- a/querysync/java/com/google/idea/blaze/qsync/java/GeneratedSourceProjectUpdater.java
+++ b/querysync/java/com/google/idea/blaze/qsync/java/GeneratedSourceProjectUpdater.java
@@ -58,8 +58,7 @@ public GeneratedSourceProjectUpdater(
this.genSrcRoots = genSrcFileFolders;
this.genSrcJars = genSrcJars;
this.resolver = resolver;
- srcJarInnerPathFinder =
- new SrcJarInnerPathFinder(new PackageStatementParser(), ALLOW_NON_EMPTY_PACKAGE_PREFIXES);
+ srcJarInnerPathFinder = new SrcJarInnerPathFinder(new PackageStatementParser());
}
public Project addGenSrcContentEntry() {
@@ -90,7 +89,8 @@ public Project addGenSrcContentEntry() {
ProjectProto.ContentEntry.Builder genSrcJarContentEntry =
ProjectProto.ContentEntry.newBuilder().setRoot(jar.path().toProto());
for (JarPath innerPath :
- srcJarInnerPathFinder.findInnerJarPaths(resolver.resolve(jar.path()).toFile())) {
+ srcJarInnerPathFinder.findInnerJarPaths(
+ resolver.resolve(jar.path()).toFile(), ALLOW_NON_EMPTY_PACKAGE_PREFIXES)) {
genSrcJarContentEntry.addSources(
ProjectProto.SourceFolder.newBuilder()
.setProjectPath(jar.path().withInnerJarPath(innerPath.path).toProto())
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/SrcJarInnerPathFinder.java b/querysync/java/com/google/idea/blaze/qsync/java/SrcJarInnerPathFinder.java
index 8d479161ee3..01f37f783c9 100644
--- a/querysync/java/com/google/idea/blaze/qsync/java/SrcJarInnerPathFinder.java
+++ b/querysync/java/com/google/idea/blaze/qsync/java/SrcJarInnerPathFinder.java
@@ -17,16 +17,18 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.io.ByteSource;
+import java.io.BufferedInputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
-import java.util.Enumeration;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
/** Utility for finding inner paths of a source jar corresponding to package roots */
public class SrcJarInnerPathFinder {
@@ -63,60 +65,75 @@ static JarPath create(Path path, String packagePrefix) {
private final Logger logger = Logger.getLogger(SrcJarInnerPathFinder.class.getSimpleName());
private final PackageStatementParser packageStatementParser;
- private final AllowPackagePrefixes allowPackagePrefixes;
- public SrcJarInnerPathFinder(
- PackageStatementParser packageStatementParser, AllowPackagePrefixes allowPackagePrefixes) {
+ public SrcJarInnerPathFinder(PackageStatementParser packageStatementParser) {
this.packageStatementParser = packageStatementParser;
- this.allowPackagePrefixes = allowPackagePrefixes;
}
- public ImmutableSet findInnerJarPaths(File jarFile) {
+ public ImmutableSet findInnerJarPaths(
+ File jarFile, AllowPackagePrefixes allowPackagePrefixes) {
+ try (InputStream in = new FileInputStream(jarFile)) {
+ return findInnerJarPaths(in, allowPackagePrefixes);
+ } catch (IOException ioe) {
+ logger.log(Level.WARNING, "Failed to examine " + jarFile, ioe);
+ // return the jar file root to ensure we don't ignore it.
+ return ImmutableSet.of(JarPath.create("", ""));
+ }
+ }
+
+ public ImmutableSet findInnerJarPaths(
+ ByteSource artifact, AllowPackagePrefixes allowPackagePrefixes) {
+ try (InputStream in = artifact.openBufferedStream()) {
+ return findInnerJarPaths(in, allowPackagePrefixes);
+ } catch (IOException ioe) {
+ logger.log(Level.WARNING, "Failed to examine " + artifact, ioe);
+ // return the jar file root to ensure we don't ignore it.
+ return ImmutableSet.of(JarPath.create("", ""));
+ }
+ }
+
+ private ImmutableSet findInnerJarPaths(
+ InputStream jarFile, AllowPackagePrefixes allowPackagePrefixes) throws IOException {
Set paths = Sets.newHashSet();
- try {
- ZipFile zip = new ZipFile(jarFile);
- Enumeration extends ZipEntry> entries = zip.entries();
- Set topLevelPaths = Sets.newHashSet();
- while (entries.hasMoreElements()) {
- ZipEntry e = entries.nextElement();
- if (e.isDirectory()) {
- continue;
- }
- Path zipfilePath = Path.of(e.getName());
- if (!(zipfilePath.getFileName().toString().endsWith(".java")
- || zipfilePath.getFileName().toString().endsWith(".kt"))) {
- continue;
- }
- if (!topLevelPaths.add(zipfilePath.getName(0))) {
- continue;
- }
- try (InputStream in = zip.getInputStream(e)) {
- String pname = packageStatementParser.readPackage(in);
- Path packageAsPath = Path.of(pname.replace('.', '/'));
- Path zipPath = zipfilePath.getParent();
- if (zipPath == null) {
- zipPath = Path.of("");
- }
- if (zipPath.equals(packageAsPath)) {
- // package root is the jar file root.
- paths.add(JarPath.create("", ""));
- } else if (zipPath.endsWith(packageAsPath)) {
- paths.add(
- JarPath.create(
- zipPath.subpath(0, zipPath.getNameCount() - packageAsPath.getNameCount()), ""));
- } else {
- if (allowPackagePrefixes == AllowPackagePrefixes.ALLOW_NON_EMPTY_PACKAGE_PREFIXES) {
- paths.add(JarPath.create(zipPath, pname));
- } else {
- logger.log(
- Level.WARNING,
- "Java package name " + pname + " does not match srcjar path " + zipfilePath);
- }
- }
+ ZipInputStream zis = new ZipInputStream(new BufferedInputStream(jarFile));
+
+ ZipEntry e;
+ Set topLevelPaths = Sets.newHashSet();
+ while ((e = zis.getNextEntry()) != null) {
+ if (e.isDirectory()) {
+ continue;
+ }
+ Path zipfilePath = Path.of(e.getName());
+ if (!(zipfilePath.getFileName().toString().endsWith(".java")
+ || zipfilePath.getFileName().toString().endsWith(".kt"))) {
+ continue;
+ }
+ if (!topLevelPaths.add(zipfilePath.getName(0))) {
+ continue;
+ }
+ String pname = packageStatementParser.readPackage(zis);
+ Path packageAsPath = Path.of(pname.replace('.', '/'));
+ Path zipPath = zipfilePath.getParent();
+ if (zipPath == null) {
+ zipPath = Path.of("");
+ }
+ if (zipPath.equals(packageAsPath)) {
+ // package root is the jar file root.
+ paths.add(JarPath.create("", ""));
+ } else if (zipPath.endsWith(packageAsPath)) {
+ paths.add(
+ JarPath.create(
+ zipPath.subpath(0, zipPath.getNameCount() - packageAsPath.getNameCount()), ""));
+ } else {
+ if (allowPackagePrefixes == AllowPackagePrefixes.ALLOW_NON_EMPTY_PACKAGE_PREFIXES) {
+ paths.add(JarPath.create(zipPath, pname));
+ } else {
+ logger.log(
+ Level.WARNING,
+ "Java package name " + pname + " does not match srcjar path " + zipfilePath);
}
}
- } catch (IOException ioe) {
- logger.log(Level.WARNING, "Failed to examine " + jarFile, ioe);
+ zis.closeEntry();
}
if (paths.isEmpty()) {
// we didn't find any java/kt sources. Add the jar file root to ensure we don't ignore it.
diff --git a/querysync/java/com/google/idea/blaze/qsync/java/SrcJarProjectUpdater.java b/querysync/java/com/google/idea/blaze/qsync/java/SrcJarProjectUpdater.java
index 72e6e115339..e58c726b3c9 100644
--- a/querysync/java/com/google/idea/blaze/qsync/java/SrcJarProjectUpdater.java
+++ b/querysync/java/com/google/idea/blaze/qsync/java/SrcJarProjectUpdater.java
@@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.qsync.java.SrcJarInnerPathFinder.AllowPackagePrefixes;
import com.google.idea.blaze.qsync.project.ProjectPath;
import com.google.idea.blaze.qsync.project.ProjectProto;
import com.google.idea.blaze.qsync.project.ProjectProto.Library;
@@ -46,8 +47,7 @@ public SrcJarProjectUpdater(
this.resolver = resolver;
// Require empty package prefixes for srcjar inner paths, since the ultimate consumer of these
// paths does not support setting a package prefix (see `Library.ModifiableModel.addRoot`).
- srcJarInnerPathFinder =
- new SrcJarInnerPathFinder(new PackageStatementParser(), EMPTY_PACKAGE_PREFIXES_ONLY);
+ srcJarInnerPathFinder = new SrcJarInnerPathFinder(new PackageStatementParser());
}
private int findDepsLib(List libs) {
@@ -66,7 +66,8 @@ public ProjectProto.Project addSrcJars() {
return project;
}
- ImmutableList srcJars = resolveSrcJarInnerPaths(this.srcJars);
+ ImmutableList srcJars =
+ resolveSrcJarInnerPaths(this.srcJars, EMPTY_PACKAGE_PREFIXES_ONLY);
ImmutableSet existingSrcjars =
project.getLibrary(depLibPos).getSourcesList().stream()
@@ -101,11 +102,12 @@ public ProjectProto.Project addSrcJars() {
* For each of {@code srcJars}, sets the {@link ProjectPath#innerJarPath()} to the java source
* root within that jar file, if necessary.
*/
- private ImmutableList resolveSrcJarInnerPaths(Collection srcJars) {
+ private ImmutableList resolveSrcJarInnerPaths(
+ Collection srcJars, AllowPackagePrefixes allowPackagePrefixes) {
ImmutableList.Builder newSrcJars = ImmutableList.builder();
for (ProjectPath srcJar : srcJars) {
Path jarFile = resolver.resolve(srcJar);
- srcJarInnerPathFinder.findInnerJarPaths(jarFile.toFile()).stream()
+ srcJarInnerPathFinder.findInnerJarPaths(jarFile.toFile(), allowPackagePrefixes).stream()
.map(p -> p.path)
.map(srcJar::withInnerJarPath)
.forEach(newSrcJars::add);
diff --git a/querysync/java/com/google/idea/blaze/qsync/project/PostQuerySyncData.java b/querysync/java/com/google/idea/blaze/qsync/project/PostQuerySyncData.java
index 2ec24fdf760..711e5d30909 100644
--- a/querysync/java/com/google/idea/blaze/qsync/project/PostQuerySyncData.java
+++ b/querysync/java/com/google/idea/blaze/qsync/project/PostQuerySyncData.java
@@ -44,6 +44,7 @@ public abstract class PostQuerySyncData {
/* languageClasses= */ ImmutableSet.of(),
/* testSources= */ ImmutableSet.of()))
.setVcsState(Optional.empty())
+ .setBazelVersion(Optional.empty())
.setQuerySummary(QuerySummary.EMPTY)
.build();
@@ -53,6 +54,9 @@ public abstract class PostQuerySyncData {
/** The VCS state at the time that the query was run. */
public abstract Optional vcsState();
+ /** The version of bazel that the query was run. */
+ public abstract Optional bazelVersion();
+
/** The summarised output from the query. */
public abstract QuerySummary querySummary();
@@ -71,6 +75,8 @@ public abstract static class Builder {
public abstract Builder setVcsState(Optional value);
+ public abstract Builder setBazelVersion(Optional value);
+
public abstract Builder setQuerySummary(QuerySummary value);
@CanIgnoreReturnValue
diff --git a/querysync/java/com/google/idea/blaze/qsync/project/ProjectPath.java b/querysync/java/com/google/idea/blaze/qsync/project/ProjectPath.java
index faade1265b4..899f4eb0fc1 100644
--- a/querysync/java/com/google/idea/blaze/qsync/project/ProjectPath.java
+++ b/querysync/java/com/google/idea/blaze/qsync/project/ProjectPath.java
@@ -17,6 +17,7 @@
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import com.google.idea.blaze.qsync.project.ProjectProto.ProjectPath.Base;
import java.nio.file.Path;
@@ -43,6 +44,11 @@ public ProjectPath withInnerJarPath(Path inner) {
return create(rootType(), relativePath(), inner);
}
+ public ProjectPath resolveChild(Path child) {
+ Preconditions.checkState(!child.isAbsolute(), child);
+ return create(rootType(), relativePath().resolve(child));
+ }
+
public ProjectProto.ProjectPath toProto() {
ProjectProto.ProjectPath.Builder proto = ProjectProto.ProjectPath.newBuilder();
switch (rootType()) {
diff --git a/querysync/java/com/google/idea/blaze/qsync/ProjectProtoTransform.java b/querysync/java/com/google/idea/blaze/qsync/project/ProjectProtoTransform.java
similarity index 90%
rename from querysync/java/com/google/idea/blaze/qsync/ProjectProtoTransform.java
rename to querysync/java/com/google/idea/blaze/qsync/project/ProjectProtoTransform.java
index 145431c7947..68c4613f8f6 100644
--- a/querysync/java/com/google/idea/blaze/qsync/ProjectProtoTransform.java
+++ b/querysync/java/com/google/idea/blaze/qsync/project/ProjectProtoTransform.java
@@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.google.idea.blaze.qsync;
+package com.google.idea.blaze.qsync.project;
+import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.idea.blaze.common.Context;
import com.google.idea.blaze.exception.BuildException;
-import com.google.idea.blaze.qsync.project.BuildGraphData;
-import com.google.idea.blaze.qsync.project.ProjectProto;
import java.util.List;
/** Applies a transform to a project proto instance, yielding a new instance. */
@@ -59,6 +58,10 @@ public void add(ProjectProtoTransform transform) {
transforms.add(transform);
}
+ public void addAll(ImmutableCollection transforms) {
+ this.transforms.addAll(transforms);
+ }
+
public ProjectProtoTransform getComposedTransform() {
return compose(ImmutableList.copyOf(transforms));
}
diff --git a/querysync/java/com/google/idea/blaze/qsync/project/SnapshotDeserializer.java b/querysync/java/com/google/idea/blaze/qsync/project/SnapshotDeserializer.java
index e81c6e0a1bb..10379e5f64f 100644
--- a/querysync/java/com/google/idea/blaze/qsync/project/SnapshotDeserializer.java
+++ b/querysync/java/com/google/idea/blaze/qsync/project/SnapshotDeserializer.java
@@ -57,6 +57,9 @@ public Optional readFrom(InputStream in, Context> contex
if (proto.hasVcsState()) {
visitVcsState(proto.getVcsState());
}
+ if (proto.hasBazelVersion()) {
+ snapshot.setBazelVersion(Optional.of(proto.getBazelVersion()));
+ }
visitQuerySummay(proto.getQuerySummary());
return Optional.of(this);
}
diff --git a/querysync/java/com/google/idea/blaze/qsync/TestSourceGlobMatcher.java b/querysync/java/com/google/idea/blaze/qsync/project/TestSourceGlobMatcher.java
similarity index 95%
rename from querysync/java/com/google/idea/blaze/qsync/TestSourceGlobMatcher.java
rename to querysync/java/com/google/idea/blaze/qsync/project/TestSourceGlobMatcher.java
index 84c3f2fba6a..612251a14e2 100644
--- a/querysync/java/com/google/idea/blaze/qsync/TestSourceGlobMatcher.java
+++ b/querysync/java/com/google/idea/blaze/qsync/project/TestSourceGlobMatcher.java
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.google.idea.blaze.qsync;
+package com.google.idea.blaze.qsync.project;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
-import com.google.idea.blaze.qsync.project.ProjectDefinition;
import java.io.File;
import java.nio.file.Path;
diff --git a/querysync/java/com/google/idea/blaze/qsync/project/project.proto b/querysync/java/com/google/idea/blaze/qsync/project/project.proto
index a47f3a82e67..184ebc7c9f8 100644
--- a/querysync/java/com/google/idea/blaze/qsync/project/project.proto
+++ b/querysync/java/com/google/idea/blaze/qsync/project/project.proto
@@ -45,6 +45,10 @@ message Module {
// Separate custom package with source packages as it's only used by R class.
repeated string android_custom_packages = 7;
+
+ // Android libs are kept separate to regular libraries reflecting the
+ // separatation between the APIs we use to configure them.
+ repeated ExternalAndroidLibrary android_external_libraries = 8;
}
/**
@@ -132,6 +136,20 @@ message LibrarySource {
ProjectPath srcjar = 2;
}
+/**
+ * An external android library.
+ *
+ * Corresponds to the AS class com.android.projectmodel.ExternalAndroidLibrary.
+ */
+message ExternalAndroidLibrary {
+ string name = 1;
+ ProjectPath location = 2;
+ ProjectPath manifest_file = 3;
+ ProjectPath res_folder = 4;
+ ProjectPath symbol_file = 5;
+ string package_name = 6;
+}
+
/**
* A single compiler flag that supports resolving to an absolute path when
* configuring the project model.
@@ -241,6 +259,7 @@ message ProjectArtifact {
BuildArtifact build_artifact = 3;
string workspace_relative_path = 4;
}
+ string target = 5;
enum ArtifactTransform {
NONE = 0;
diff --git a/querysync/java/com/google/idea/blaze/qsync/project/snapshot.proto b/querysync/java/com/google/idea/blaze/qsync/project/snapshot.proto
index e811b51a74b..fde699ff0f0 100644
--- a/querysync/java/com/google/idea/blaze/qsync/project/snapshot.proto
+++ b/querysync/java/com/google/idea/blaze/qsync/project/snapshot.proto
@@ -2,9 +2,8 @@ syntax = "proto3";
package ij.qsync;
-import "querysync/java/com/google/idea/blaze/qsync/query/querysummary.proto";
import "querysync/java/com/google/idea/blaze/qsync/project/language_class.proto";
-
+import "querysync/java/com/google/idea/blaze/qsync/query/querysummary.proto";
// option java_api_version = 2;
option java_outer_classname = "SnapshotProto";
@@ -15,6 +14,7 @@ message Snapshot {
VcsState vcs_state = 2;
Summary query_summary = 3;
int32 version = 4;
+ optional string bazel_version = 5;
}
message ProjectDefinition {
diff --git a/querysync/javatests/com/google/idea/blaze/qsync/BUILD b/querysync/javatests/com/google/idea/blaze/qsync/BUILD
index 41f78057b91..0879d887d42 100644
--- a/querysync/javatests/com/google/idea/blaze/qsync/BUILD
+++ b/querysync/javatests/com/google/idea/blaze/qsync/BUILD
@@ -16,6 +16,7 @@ java_library(
],
deps = [
"//querysync/java/com/google/idea/blaze/qsync",
+ "//querysync/java/com/google/idea/blaze/qsync/deps",
"//querysync/java/com/google/idea/blaze/qsync/java",
"//querysync/java/com/google/idea/blaze/qsync/project",
"//querysync/java/com/google/idea/blaze/qsync/project:project_java_proto",
@@ -109,15 +110,3 @@ java_test(
"@truth//jar",
],
)
-
-java_test(
- name = "TestSourceGlobMatcherTest",
- size = "small",
- srcs = ["TestSourceGlobMatcherTest.java"],
- deps = [
- "//querysync/java/com/google/idea/blaze/qsync",
- "@com_google_guava_guava//jar",
- "@junit//jar",
- "@truth//jar",
- ],
-)
diff --git a/querysync/javatests/com/google/idea/blaze/qsync/GraphToProjectConverters.java b/querysync/javatests/com/google/idea/blaze/qsync/GraphToProjectConverters.java
index d1ecc6f6637..acd635ca63f 100644
--- a/querysync/javatests/com/google/idea/blaze/qsync/GraphToProjectConverters.java
+++ b/querysync/javatests/com/google/idea/blaze/qsync/GraphToProjectConverters.java
@@ -80,7 +80,8 @@ public GraphToProjectConverter build() {
info.projectExcludes(),
info.languageClasses(),
info.testSources()),
- newDirectExecutorService());
+ newDirectExecutorService(),
+ false);
}
}
}
diff --git a/querysync/javatests/com/google/idea/blaze/qsync/PartialProjectRefreshTest.java b/querysync/javatests/com/google/idea/blaze/qsync/PartialProjectRefreshTest.java
index e88f285ab87..252af5da2e4 100644
--- a/querysync/javatests/com/google/idea/blaze/qsync/PartialProjectRefreshTest.java
+++ b/querysync/javatests/com/google/idea/blaze/qsync/PartialProjectRefreshTest.java
@@ -24,6 +24,7 @@
import com.google.idea.blaze.qsync.query.Query;
import com.google.idea.blaze.qsync.query.QuerySummary;
import java.nio.file.Path;
+import java.util.Optional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -101,6 +102,7 @@ public void testApplyDelta_replacePackage() {
Path.of("/workspace/root"),
baseProject,
QuerySyncTestUtils.CLEAN_VCS_STATE,
+ Optional.empty(),
/* modifiedPackages= */ ImmutableSet.of(Path.of("my/build/package1")),
ImmutableSet.of());
QuerySummary applied = queryStrategy.applyDelta(delta);
@@ -166,6 +168,7 @@ public void testApplyDelta_deletePackage() {
Path.of("/workspace/root"),
baseProject,
QuerySyncTestUtils.CLEAN_VCS_STATE,
+ Optional.empty(),
ImmutableSet.of(),
/* deletedPackages= */ ImmutableSet.of(Path.of("my/build/package1")));
Truth8.assertThat(queryStrategy.getQuerySpec()).isEmpty();
@@ -227,6 +230,7 @@ public void testDelta_addPackage() {
Path.of("/workspace/root"),
baseProject,
QuerySyncTestUtils.CLEAN_VCS_STATE,
+ Optional.empty(),
/* modifiedPackages= */ ImmutableSet.of(Path.of("my/build/package2")),
ImmutableSet.of());
QuerySummary applied = queryStrategy.applyDelta(delta);
@@ -257,6 +261,7 @@ public void testDelta_packagesWithErrors() {
Path.of("/workspace/root"),
baseProject,
QuerySyncTestUtils.CLEAN_VCS_STATE,
+ Optional.empty(),
/* modifiedPackages= */ ImmutableSet.of(Path.of("my/build/package")),
ImmutableSet.of());
QuerySummary applied = queryStrategy.applyDelta(delta);
diff --git a/querysync/javatests/com/google/idea/blaze/qsync/ProjectRefresherTest.java b/querysync/javatests/com/google/idea/blaze/qsync/ProjectRefresherTest.java
index b665378bba0..74ba33f1fb9 100644
--- a/querysync/javatests/com/google/idea/blaze/qsync/ProjectRefresherTest.java
+++ b/querysync/javatests/com/google/idea/blaze/qsync/ProjectRefresherTest.java
@@ -24,7 +24,6 @@
import com.google.idea.blaze.common.vcs.VcsState;
import com.google.idea.blaze.common.vcs.WorkspaceFileChange;
import com.google.idea.blaze.common.vcs.WorkspaceFileChange.Operation;
-import com.google.idea.blaze.qsync.project.BlazeProjectSnapshot;
import com.google.idea.blaze.qsync.project.PostQuerySyncData;
import com.google.idea.blaze.qsync.project.ProjectDefinition;
import com.google.idea.blaze.qsync.project.QuerySyncLanguage;
@@ -72,6 +71,7 @@ public void testStartPartialRefresh_pluginVersionChanged() throws Exception {
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
project.vcsState(),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(FullProjectUpdate.class);
}
@@ -97,6 +97,7 @@ public void testStartPartialRefresh_vcsSnapshotUnchanged_existingProjectSnapshot
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
project.vcsState(),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(NoopProjectRefresh.class);
assertThat(update.createPostQuerySyncData(QuerySummary.EMPTY))
@@ -124,6 +125,7 @@ public void testStartPartialRefresh_vcsSnapshotUnchanged_noExistingProjectSnapsh
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
project.vcsState(),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(PartialProjectRefresh.class);
}
@@ -142,6 +144,7 @@ public void testStartPartialRefresh_workspaceChange() throws Exception {
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
Optional.of(new VcsState("workspace2", "1", ImmutableSet.of(), Optional.empty())),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(FullProjectUpdate.class);
}
@@ -160,10 +163,46 @@ public void testStartPartialRefresh_upstreamRevisionChange() throws Exception {
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
Optional.of(new VcsState("workspaceId", "2", ImmutableSet.of(), Optional.empty())),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(FullProjectUpdate.class);
}
+ @Test
+ public void testStartPartialRefresh_bazelVersionChanged() throws Exception {
+ PostQuerySyncData project =
+ PostQuerySyncData.EMPTY.toBuilder()
+ .setQuerySummary(QuerySummaryTestUtil.createProtoForPackages("//package/path:rule"))
+ .setVcsState(
+ Optional.of(
+ new VcsState(
+ "workspaceId",
+ "1",
+ ImmutableSet.of(
+ new WorkspaceFileChange(
+ Operation.MODIFY, Path.of("package/path/BUILD"))),
+ Optional.empty())))
+ .setProjectDefinition(
+ ProjectDefinition.create(
+ ImmutableSet.of(Path.of("package")),
+ ImmutableSet.of(),
+ ImmutableSet.of(QuerySyncLanguage.JAVA),
+ ImmutableSet.of()))
+ .setBazelVersion(Optional.of("1.0.0"))
+ .build();
+
+ RefreshOperation update =
+ createRefresher(VcsStateDiffer.NONE)
+ .startPartialRefresh(
+ QuerySyncTestUtils.LOGGING_CONTEXT,
+ project,
+ project.vcsState(),
+ Optional.of("2.0.0"),
+ project.projectDefinition());
+
+ assertThat(update).isInstanceOf(FullProjectUpdate.class);
+ }
+
@Test
public void testStartPartialRefresh_buildFileAddedThenReverted() throws Exception {
PostQuerySyncData project =
@@ -191,6 +230,7 @@ public void testStartPartialRefresh_buildFileAddedThenReverted() throws Exceptio
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
Optional.of(new VcsState("workspaceId", "1", ImmutableSet.of(), Optional.empty())),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(PartialProjectRefresh.class);
@@ -226,6 +266,7 @@ public void testStartPartialRefresh_buildFileDeletedThenReverted() throws Except
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
Optional.of(new VcsState("workspaceId", "1", ImmutableSet.of(), Optional.empty())),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(PartialProjectRefresh.class);
@@ -257,6 +298,7 @@ public void testStartPartialRefresh_buildFileModified() throws Exception {
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
Optional.of(new VcsState("workspaceId", "1", workingSet, Optional.empty())),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(PartialProjectRefresh.class);
@@ -290,6 +332,7 @@ public void testStartPartialRefresh_buildFileInWorkingSet_unmodified() throws Ex
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
Optional.of(new VcsState("workspaceId", "1", workingSet, Optional.empty())),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(NoopProjectRefresh.class);
@@ -323,6 +366,7 @@ public void testStartPartialRefresh_buildFileModifiedThenReverted() throws Excep
QuerySyncTestUtils.LOGGING_CONTEXT,
project,
Optional.of(new VcsState("workspaceId", "1", ImmutableSet.of(), Optional.empty())),
+ project.bazelVersion(),
project.projectDefinition());
assertThat(update).isInstanceOf(PartialProjectRefresh.class);
diff --git a/querysync/javatests/com/google/idea/blaze/qsync/TestDataSyncRunner.java b/querysync/javatests/com/google/idea/blaze/qsync/TestDataSyncRunner.java
index f4185f9e997..4a74415da04 100644
--- a/querysync/javatests/com/google/idea/blaze/qsync/TestDataSyncRunner.java
+++ b/querysync/javatests/com/google/idea/blaze/qsync/TestDataSyncRunner.java
@@ -22,8 +22,8 @@
import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.common.Context;
import com.google.idea.blaze.exception.BuildException;
+import com.google.idea.blaze.qsync.deps.ArtifactTracker;
import com.google.idea.blaze.qsync.java.PackageReader;
-import com.google.idea.blaze.qsync.project.BlazeProjectSnapshot;
import com.google.idea.blaze.qsync.project.BuildGraphData;
import com.google.idea.blaze.qsync.project.PostQuerySyncData;
import com.google.idea.blaze.qsync.project.ProjectDefinition;
@@ -41,10 +41,13 @@ public class TestDataSyncRunner {
private final Context> context;
private final PackageReader packageReader;
+ private final boolean useNewArtifactLogic;
- public TestDataSyncRunner(Context> context, PackageReader packageReader) {
+ public TestDataSyncRunner(
+ Context> context, PackageReader packageReader, boolean useNewArtifactLogic) {
this.context = context;
this.packageReader = packageReader;
+ this.useNewArtifactLogic = useNewArtifactLogic;
}
public BlazeProjectSnapshot sync(TestData testProject) throws IOException, BuildException {
@@ -60,6 +63,7 @@ public BlazeProjectSnapshot sync(TestData testProject) throws IOException, Build
.setProjectDefinition(projectDefinition)
.setQuerySummary(querySummary)
.setVcsState(Optional.empty())
+ .setBazelVersion(Optional.empty())
.build();
BuildGraphData buildGraphData =
new BlazeQueryParser(querySummary, context, ImmutableSet.of()).parse();
@@ -69,11 +73,13 @@ public BlazeProjectSnapshot sync(TestData testProject) throws IOException, Build
Predicates.alwaysTrue(),
context,
projectDefinition,
- newDirectExecutorService());
+ newDirectExecutorService(),
+ useNewArtifactLogic);
Project project = converter.createProject(buildGraphData);
return BlazeProjectSnapshot.builder()
.queryData(pqsd)
.graph(new BlazeQueryParser(querySummary, context, ImmutableSet.of()).parse())
+ .artifactState(ArtifactTracker.State.EMPTY)
.project(project)
.build();
}
diff --git a/querysync/javatests/com/google/idea/blaze/qsync/cc/BUILD b/querysync/javatests/com/google/idea/blaze/qsync/cc/BUILD
index 6c67dd2ca8d..57b625a1718 100644
--- a/querysync/javatests/com/google/idea/blaze/qsync/cc/BUILD
+++ b/querysync/javatests/com/google/idea/blaze/qsync/cc/BUILD
@@ -20,3 +20,26 @@ java_test(
"@truth//jar",
],
)
+
+java_test(
+ name = "ConfigureCcCompilationTest",
+ size = "small",
+ srcs = ["ConfigureCcCompilationTest.java"],
+ deps = [
+ "//querysync/java/com/google/idea/blaze/qsync",
+ "//querysync/java/com/google/idea/blaze/qsync/cc",
+ "//querysync/java/com/google/idea/blaze/qsync/cc:cc_compilation_info_java_proto",
+ "//querysync/java/com/google/idea/blaze/qsync/deps",
+ "//querysync/java/com/google/idea/blaze/qsync/java",
+ "//querysync/java/com/google/idea/blaze/qsync/project",
+ "//querysync/java/com/google/idea/blaze/qsync/project:language_class_java_proto",
+ "//querysync/java/com/google/idea/blaze/qsync/project:project_java_proto",
+ "//querysync/javatests/com/google/idea/blaze/qsync:TestUtils",
+ "//querysync/javatests/com/google/idea/blaze/qsync/testdata",
+ "//shared",
+ "//shared/javatests/com/google/idea/blaze/common:test_utils",
+ "@com_google_guava_guava//jar",
+ "@junit//jar",
+ "@truth//jar",
+ ],
+)
diff --git a/querysync/javatests/com/google/idea/blaze/qsync/cc/ConfigureCcCompilationTest.java b/querysync/javatests/com/google/idea/blaze/qsync/cc/ConfigureCcCompilationTest.java
new file mode 100644
index 00000000000..d954cb2c52e
--- /dev/null
+++ b/querysync/javatests/com/google/idea/blaze/qsync/cc/ConfigureCcCompilationTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2023 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.cc;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Functions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.truth.Truth8;
+import com.google.idea.blaze.common.Context;
+import com.google.idea.blaze.common.Label;
+import com.google.idea.blaze.common.NoopContext;
+import com.google.idea.blaze.qsync.BlazeProjectSnapshot;
+import com.google.idea.blaze.qsync.TestDataSyncRunner;
+import com.google.idea.blaze.qsync.deps.ArtifactTracker;
+import com.google.idea.blaze.qsync.deps.ArtifactTracker.State;
+import com.google.idea.blaze.qsync.deps.CcToolchain;
+import com.google.idea.blaze.qsync.deps.DependencyBuildContext;
+import com.google.idea.blaze.qsync.deps.ProjectProtoUpdate;
+import com.google.idea.blaze.qsync.deps.TargetBuildInfo;
+import com.google.idea.blaze.qsync.java.PackageStatementParser;
+import com.google.idea.blaze.qsync.java.cc.CcCompilationInfoOuterClass.CcCompilationInfo;
+import com.google.idea.blaze.qsync.java.cc.CcCompilationInfoOuterClass.CcTargetInfo;
+import com.google.idea.blaze.qsync.java.cc.CcCompilationInfoOuterClass.CcToolchainInfo;
+import com.google.idea.blaze.qsync.project.LanguageClassProto.LanguageClass;
+import com.google.idea.blaze.qsync.project.ProjectPath;
+import com.google.idea.blaze.qsync.project.ProjectProto;
+import com.google.idea.blaze.qsync.project.ProjectProto.CcCompilationContext;
+import com.google.idea.blaze.qsync.project.ProjectProto.CcCompilerSettings;
+import com.google.idea.blaze.qsync.project.ProjectProto.CcLanguage;
+import com.google.idea.blaze.qsync.project.ProjectProto.CcSourceFile;
+import com.google.idea.blaze.qsync.project.ProjectProto.CcWorkspace;
+import com.google.idea.blaze.qsync.project.ProjectProto.ProjectPath.Base;
+import com.google.idea.blaze.qsync.testdata.TestData;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.function.Function;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ConfigureCcCompilationTest {
+
+ private final Context> context = new NoopContext();
+ private final TestDataSyncRunner syncRunner =
+ new TestDataSyncRunner(context, new PackageStatementParser(), true);
+
+ private static State toArtifactState(CcCompilationInfo proto) {
+ Function digestMap = p -> Integer.toHexString(p.hashCode());
+ return ArtifactTracker.State.create(
+ proto.getTargetsList().stream()
+ .map(t -> com.google.idea.blaze.qsync.deps.CcCompilationInfo.create(t, digestMap))
+ .collect(
+ toImmutableMap(
+ cc -> cc.target(),
+ cc -> TargetBuildInfo.forCcTarget(cc, DependencyBuildContext.NONE))),
+ proto.getToolchainsList().stream()
+ .map(CcToolchain::create)
+ .collect(toImmutableMap(CcToolchain::id, Functions.identity())));
+ }
+
+ @Test
+ public void empty() throws Exception {
+ BlazeProjectSnapshot original = syncRunner.sync(TestData.CC_LIBRARY_QUERY);
+ ProjectProtoUpdate update =
+ new ProjectProtoUpdate(original.project(), original.graph(), context);
+ ConfigureCcCompilation ccConfig =
+ new ConfigureCcCompilation(ArtifactTracker.State.EMPTY, update);
+ ccConfig.update();
+ ProjectProto.Project project = update.build();
+ assertThat(project.getCcWorkspace()).isEqualTo(CcWorkspace.getDefaultInstance());
+ }
+
+ @Test
+ public void basics() throws Exception {
+ BlazeProjectSnapshot original = syncRunner.sync(TestData.CC_LIBRARY_QUERY);
+ ProjectProtoUpdate update =
+ new ProjectProtoUpdate(original.project(), original.graph(), context);
+
+ Label ccTargetLabel = getOnlyElement(TestData.CC_LIBRARY_QUERY.getAssumedLabels());
+ CcCompilationInfo compilationInfo =
+ CcCompilationInfo.newBuilder()
+ .addToolchains(
+ CcToolchainInfo.newBuilder()
+ .setId("//my/cc_toolchain")
+ .setCompiler("clang")
+ .setCompilerExecutable("workspace/path/to/clang")
+ .setCpu("k8")
+ .setTargetName("k8-debug")
+ .addBuiltInIncludeDirectories("bazel-out/builtin/include/directory")
+ .addBuiltInIncludeDirectories("src/builtin/include/directory")
+ .addAllCOptions(ImmutableList.of("--sharedopt", "--conlyopt"))
+ .addAllCppOptions(ImmutableList.of("--sharedopt", "--cppopt"))
+ .build())
+ .addTargets(
+ CcTargetInfo.newBuilder()
+ .setLabel(ccTargetLabel.toString())
+ .setToolchainId("//my/cc_toolchain")
+ .addDefines("DEBUG")
+ .addIncludeDirectories("bazel-out/include/directory")
+ .addIncludeDirectories("src/include/directory")
+ .addQuoteIncludeDirectories("bazel-out/quote/include/directory")
+ .addQuoteIncludeDirectories("src/quote/include/directory")
+ .addSystemIncludeDirectories("bazel-out/system/include/directory")
+ .addSystemIncludeDirectories("src/system/include/directory")
+ .addFrameworkIncludeDirectories("bazel-out/framework/include/directory")
+ .addFrameworkIncludeDirectories("src/framework/include/directory")
+ .addAllGenHdrs(
+ ImmutableList.of(
+ "bazel-out/include/directory/include_header.h",
+ "bazel-out/quote/include/directory/quote_include_header.h",
+ "bazel-out/system/include/directory/system_include_header.h",
+ "bazel-out/framework/include/directory/framework_include_header",
+ "bazel-out/builtin/include/directory/builtin_include.h")))
+ .build();
+
+ ConfigureCcCompilation ccConfig =
+ new ConfigureCcCompilation(toArtifactState(compilationInfo), update);
+ ccConfig.update();
+
+ ProjectProto.Project project = update.build();
+
+ assertThat(project.getActiveLanguagesList()).contains(LanguageClass.LANGUAGE_CLASS_CC);
+ ProjectProto.CcWorkspace workspace = project.getCcWorkspace();
+ CcCompilationContext context = getOnlyElement(workspace.getContextsList());
+ assertThat(context.getHumanReadableName()).isNotEmpty();
+ CcSourceFile sourceFile = getOnlyElement(context.getSourcesList());
+ assertThat(sourceFile.getLanguage()).isEqualTo(CcLanguage.CPP);
+ assertThat(sourceFile.getWorkspacePath())
+ .isEqualTo(
+ TestData.CC_LIBRARY_QUERY.getOnlySourcePath().resolve("TestClass.cc").toString());
+ CcCompilerSettings compilerSettings = sourceFile.getCompilerSettings();
+ FlagResolver resolver =
+ new FlagResolver(
+ ProjectPath.Resolver.create(Path.of("/workspace"), Path.of("/project")), false);
+ assertThat(resolver.resolveAll(workspace.getFlagSetsOrThrow(compilerSettings.getFlagSetId())))
+ .containsExactly(
+ "-DDEBUG",
+ "-I/project/.bazel/buildout/bazel-out/builtin/include/directory",
+ "-I/workspace/src/builtin/include/directory",
+ "-I/project/.bazel/buildout/bazel-out/include/directory",
+ "-I/workspace/src/include/directory",
+ "-iquote/project/.bazel/buildout/bazel-out/quote/include/directory",
+ "-iquote/workspace/src/quote/include/directory",
+ "-isystem/project/.bazel/buildout/bazel-out/system/include/directory",
+ "-isystem/workspace/src/system/include/directory",
+ "-F/project/.bazel/buildout/bazel-out/framework/include/directory",
+ "-F/workspace/src/framework/include/directory",
+ "-w", // This is defined in `copts` of the test project build rule.
+ "--sharedopt",
+ "--cppopt");
+
+ assertThat(compilerSettings.getCompilerExecutablePath().getBase()).isEqualTo(Base.WORKSPACE);
+ assertThat(compilerSettings.getCompilerExecutablePath().getPath())
+ .isEqualTo("workspace/path/to/clang");
+
+ Truth8.assertThat(
+ context.getLanguageToCompilerSettingsMap().keySet().stream()
+ .map(l -> CcLanguage.valueOf(CcLanguage.getDescriptor().findValueByName(l))))
+ .containsExactly(CcLanguage.CPP, CcLanguage.C);
+
+ assertThat(
+ resolver.resolveAll(
+ workspace.getFlagSetsOrThrow(
+ context
+ .getLanguageToCompilerSettingsMap()
+ .get(CcLanguage.CPP.name())
+ .getFlagSetId())))
+ .containsExactly(
+ "-I/project/.bazel/buildout/bazel-out/builtin/include/directory",
+ "-I/workspace/src/builtin/include/directory",
+ "--sharedopt",
+ "--cppopt");
+
+ assertThat(
+ resolver.resolveAll(
+ workspace.getFlagSetsOrThrow(
+ context
+ .getLanguageToCompilerSettingsMap()
+ .get(CcLanguage.C.name())
+ .getFlagSetId())))
+ .containsExactly(
+ "-I/project/.bazel/buildout/bazel-out/builtin/include/directory",
+ "-I/workspace/src/builtin/include/directory",
+ "--sharedopt",
+ "--conlyopt");
+
+ assertThat(
+ project
+ .getArtifactDirectories()
+ .getDirectoriesMap()
+ .get(".bazel/buildout")
+ .getContentsMap()
+ .keySet())
+ .containsExactly(
+ "bazel-out/include/directory/include_header.h",
+ "bazel-out/quote/include/directory/quote_include_header.h",
+ "bazel-out/system/include/directory/system_include_header.h",
+ "bazel-out/framework/include/directory/framework_include_header",
+ "bazel-out/builtin/include/directory/builtin_include.h");
+ }
+
+ @Test
+ public void multi_srcs_share_flagset() throws Exception {
+ BlazeProjectSnapshot original = syncRunner.sync(TestData.CC_MULTISRC_QUERY);
+ ProjectProtoUpdate update =
+ new ProjectProtoUpdate(original.project(), original.graph(), context);
+ Path pkgPath = getOnlyElement(TestData.CC_MULTISRC_QUERY.getRelativeSourcePaths());
+ ImmutableList