blob: 9868352d9d27766669628ef5c90438d33088556b [file] [log] [blame]
/*
* Copyright 2016 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.java.sync.importer;
import com.google.common.collect.*;
import com.google.idea.blaze.base.ideinfo.*;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.PrintOutput;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
import com.google.idea.blaze.base.sync.projectview.ProjectViewRuleImportFilter;
import com.google.idea.blaze.base.sync.projectview.SourceTestConfig;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.java.sync.DuplicateSourceDetector;
import com.google.idea.blaze.java.sync.jdeps.JdepsMap;
import com.google.idea.blaze.java.sync.model.BlazeContentEntry;
import com.google.idea.blaze.java.sync.model.BlazeJavaImportResult;
import com.google.idea.blaze.java.sync.model.BlazeLibrary;
import com.google.idea.blaze.java.sync.model.LibraryKey;
import com.google.idea.blaze.java.sync.source.SourceArtifact;
import com.google.idea.blaze.java.sync.source.SourceDirectoryCalculator;
import com.google.idea.blaze.java.sync.workingset.JavaWorkingSet;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import javax.annotation.Nullable;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Builds a BlazeWorkspace.
*/
public final class BlazeJavaWorkspaceImporter {
private static final Logger LOG = Logger.getInstance(BlazeJavaWorkspaceImporter.class);
private final Project project;
private final WorkspaceRoot workspaceRoot;
private final ImportRoots importRoots;
private final ImmutableMap<Label, RuleIdeInfo> ruleMap;
private final SourceTestConfig sourceTestConfig;
private final JdepsMap jdepsMap;
@Nullable private final JavaWorkingSet workingSet;
private final ArtifactLocationDecoder artifactLocationDecoder;
private final ProjectViewRuleImportFilter importFilter;
private final DuplicateSourceDetector duplicateSourceDetector = new DuplicateSourceDetector();
public BlazeJavaWorkspaceImporter(
Project project,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
ImmutableMap<Label, RuleIdeInfo> ruleMap,
JdepsMap jdepsMap,
@Nullable JavaWorkingSet workingSet,
ArtifactLocationDecoder artifactLocationDecoder) {
this.project = project;
this.workspaceRoot = workspaceRoot;
this.importRoots = ImportRoots.builder(workspaceRoot, Blaze.getBuildSystem(project))
.add(projectViewSet)
.build();
this.ruleMap = ruleMap;
this.jdepsMap = jdepsMap;
this.workingSet = workingSet;
this.artifactLocationDecoder = artifactLocationDecoder;
this.importFilter = new ProjectViewRuleImportFilter(project, workspaceRoot, projectViewSet);
this.sourceTestConfig = new SourceTestConfig(projectViewSet);
}
public BlazeJavaImportResult importWorkspace(BlazeContext context) {
List<RuleIdeInfo> includedRules = ruleMap.values().stream()
.filter(rule -> !importFilter.excludeTarget(rule))
.collect(Collectors.toList());
List<RuleIdeInfo> javaRules = includedRules.stream()
.filter(rule -> rule.javaRuleIdeInfo != null)
.collect(Collectors.toList());
List<RuleIdeInfo> sourceRules = Lists.newArrayList();
List<RuleIdeInfo> libraryRules = Lists.newArrayList();
for (RuleIdeInfo rule : javaRules) {
boolean importAsSource =
importFilter.isSourceRule(rule)
&& canImportAsSource(rule)
&& !allSourcesGenerated(rule);
if (importAsSource) {
sourceRules.add(rule);
} else {
libraryRules.add(rule);
}
}
List<RuleIdeInfo> protoLibraries = includedRules.stream()
.filter(rule -> rule.kind == Kind.PROTO_LIBRARY)
.collect(Collectors.toList());
WorkspaceBuilder workspaceBuilder = new WorkspaceBuilder();
for (RuleIdeInfo rule : sourceRules) {
addRuleAsSource(workspaceBuilder, rule);
}
SourceDirectoryCalculator sourceDirectoryCalculator = new SourceDirectoryCalculator();
ImmutableList<BlazeContentEntry> contentEntries = sourceDirectoryCalculator.calculateContentEntries(
context,
workspaceRoot,
sourceTestConfig,
artifactLocationDecoder,
importRoots.rootDirectories(),
workspaceBuilder.sourceArtifacts,
workspaceBuilder.javaPackageManifests
);
int totalContentEntryCount = 0;
for (BlazeContentEntry contentEntry : contentEntries) {
totalContentEntryCount += contentEntry.sources.size();
}
context.output(PrintOutput.output("Java content entry count: " + totalContentEntryCount));
ImmutableMap<LibraryKey, BlazeLibrary> libraries = buildLibraries(workspaceBuilder, ruleMap, libraryRules, protoLibraries);
duplicateSourceDetector.reportDuplicates(context);
String sourceVersion = findSourceVersion(ruleMap);
return new BlazeJavaImportResult(
contentEntries,
libraries,
ImmutableList.copyOf(workspaceBuilder.buildOutputJars
.stream()
.sorted()
.collect(Collectors.toList())),
ImmutableSet.copyOf(workspaceBuilder.addedSourceFiles),
sourceVersion
);
}
private boolean canImportAsSource(RuleIdeInfo rule) {
return !rule.kindIsOneOf(Kind.JAVA_WRAP_CC, Kind.JAVA_IMPORT);
}
private boolean allSourcesGenerated(RuleIdeInfo rule) {
return !rule.sources.isEmpty() && rule.sources.stream().allMatch(ArtifactLocation::isGenerated);
}
private ImmutableMap<LibraryKey, BlazeLibrary> buildLibraries(WorkspaceBuilder workspaceBuilder,
Map<Label, RuleIdeInfo> ruleMap,
List<RuleIdeInfo> libraryRules,
List<RuleIdeInfo> protoLibraries) {
// Build library maps
Multimap<Label, LibraryArtifact> labelToLibrary = ArrayListMultimap.create();
Map<String, LibraryArtifact> jdepsPathToLibrary = Maps.newHashMap();
for (RuleIdeInfo rule : libraryRules) {
JavaRuleIdeInfo javaRuleIdeInfo = rule.javaRuleIdeInfo;
if (javaRuleIdeInfo == null) {
continue;
}
Iterable<LibraryArtifact> libraries = Iterables.concat(javaRuleIdeInfo.jars, javaRuleIdeInfo.generatedJars);
labelToLibrary.putAll(rule.label, libraries);
for (LibraryArtifact libraryArtifact : libraries) {
addLibraryToJdeps(jdepsPathToLibrary, libraryArtifact);
}
}
// proto legacy jdeps support
for (RuleIdeInfo rule : protoLibraries) {
ProtoLibraryLegacyInfo protoLibraryLegacyInfo = rule.protoLibraryLegacyInfo;
if (protoLibraryLegacyInfo == null) {
continue;
}
for (LibraryArtifact libraryArtifact : Iterables.concat(protoLibraryLegacyInfo.jarsV1,
protoLibraryLegacyInfo.jarsMutable,
protoLibraryLegacyInfo.jarsImmutable)) {
addLibraryToJdeps(jdepsPathToLibrary, libraryArtifact);
}
}
// Collect jars from jdep references
Set<LibraryArtifact> libraries = Sets.newHashSet();
for (String jdepsPath : workspaceBuilder.jdeps) {
LibraryArtifact libraryArtifact = jdepsPathToLibrary.get(jdepsPath);
if (libraryArtifact != null) {
libraries.add(libraryArtifact);
}
}
// Collect jars referenced by direct deps from your working set
for (Label deps : workspaceBuilder.directDeps) {
libraries.addAll(labelToLibrary.get(deps));
}
// Collect legacy proto libraries from direct deps
addProtoLegacyLibrariesFromDirectDeps(workspaceBuilder, ruleMap, libraries);
// Collect generated jars from source rules
libraries.addAll(workspaceBuilder.generatedJars);
ImmutableMap.Builder<LibraryKey, BlazeLibrary> result = ImmutableMap.builder();
for (LibraryArtifact libraryArtifact : libraries) {
File jar = libraryArtifact.jar.getFile();
LibraryKey key = LibraryKey.fromJarFile(jar);
BlazeLibrary blazeLibrary = new BlazeLibrary(key, libraryArtifact);
result.put(key, blazeLibrary);
}
return result.build();
}
private void addProtoLegacyLibrariesFromDirectDeps(WorkspaceBuilder workspaceBuilder,
Map<Label, RuleIdeInfo> ruleMap,
Set<LibraryArtifact> result) {
List<Label> version1Roots = Lists.newArrayList();
List<Label> immutableRoots = Lists.newArrayList();
List<Label> mutableRoots = Lists.newArrayList();
for (Label label : workspaceBuilder.directDeps) {
RuleIdeInfo rule = ruleMap.get(label);
if (rule == null) {
continue;
}
ProtoLibraryLegacyInfo protoLibraryLegacyInfo = rule.protoLibraryLegacyInfo;
if (protoLibraryLegacyInfo == null) {
continue;
}
switch (protoLibraryLegacyInfo.apiFlavor) {
case VERSION_1:
version1Roots.add(label);
break;
case IMMUTABLE:
immutableRoots.add(label);
break;
case MUTABLE:
mutableRoots.add(label);
break;
case BOTH:
mutableRoots.add(label);
immutableRoots.add(label);
break;
default:
// Can't happen
break;
}
}
addProtoLegacyLibrariesFromDirectDepsForFlavor(ruleMap, ProtoLibraryLegacyInfo.ApiFlavor.VERSION_1, version1Roots, result);
addProtoLegacyLibrariesFromDirectDepsForFlavor(ruleMap, ProtoLibraryLegacyInfo.ApiFlavor.IMMUTABLE, immutableRoots, result);
addProtoLegacyLibrariesFromDirectDepsForFlavor(ruleMap, ProtoLibraryLegacyInfo.ApiFlavor.MUTABLE, mutableRoots, result);
}
private void addProtoLegacyLibrariesFromDirectDepsForFlavor(Map<Label, RuleIdeInfo> ruleMap,
ProtoLibraryLegacyInfo.ApiFlavor apiFlavor,
List<Label> roots,
Set<LibraryArtifact> result) {
Set<Label> seen = Sets.newHashSet();
while (!roots.isEmpty()) {
Label label = roots.remove(roots.size() - 1);
if (!seen.add(label)) {
continue;
}
RuleIdeInfo rule = ruleMap.get(label);
if (rule == null) {
continue;
}
ProtoLibraryLegacyInfo protoLibraryLegacyInfo = rule.protoLibraryLegacyInfo;
if (protoLibraryLegacyInfo == null) {
continue;
}
switch (apiFlavor) {
case VERSION_1:
result.addAll(protoLibraryLegacyInfo.jarsV1);
break;
case MUTABLE:
result.addAll(protoLibraryLegacyInfo.jarsMutable);
break;
case IMMUTABLE:
result.addAll(protoLibraryLegacyInfo.jarsImmutable);
break;
default:
// Can't happen
break;
}
roots.addAll(rule.dependencies);
}
}
private void addLibraryToJdeps(Map<String, LibraryArtifact> jdepsPathToLibrary, LibraryArtifact libraryArtifact) {
ArtifactLocation jar = libraryArtifact.jar;
jdepsPathToLibrary.put(jar.getExecutionRootRelativePath(), libraryArtifact);
ArtifactLocation runtimeJar = libraryArtifact.runtimeJar;
if (runtimeJar != null) {
jdepsPathToLibrary.put(runtimeJar.getExecutionRootRelativePath(), libraryArtifact);
}
}
private void addRuleAsSource(
WorkspaceBuilder workspaceBuilder,
RuleIdeInfo rule) {
JavaRuleIdeInfo javaRuleIdeInfo = rule.javaRuleIdeInfo;
if (javaRuleIdeInfo == null) {
return;
}
Collection<String> jars = jdepsMap.getDependenciesForRule(rule.label);
if (jars != null) {
workspaceBuilder.jdeps.addAll(jars);
}
// Add all deps if this rule is in the current working set
if (workingSet == null || workingSet.isRuleInWorkingSet(rule)) {
workspaceBuilder.directDeps.addAll(rule.dependencies);
}
for (ArtifactLocation artifactLocation : rule.sources) {
if (!artifactLocation.isGenerated()) {
duplicateSourceDetector.add(rule.label, artifactLocation);
workspaceBuilder.sourceArtifacts.add(new SourceArtifact(rule.label, artifactLocation));
workspaceBuilder.addedSourceFiles.add(artifactLocation.getFile());
}
}
ArtifactLocation manifest = javaRuleIdeInfo.packageManifest;
if (manifest != null) {
workspaceBuilder.javaPackageManifests.put(rule.label, manifest);
}
for (LibraryArtifact libraryArtifact : javaRuleIdeInfo.jars) {
ArtifactLocation runtimeJar = libraryArtifact.runtimeJar;
if (runtimeJar != null) {
workspaceBuilder.buildOutputJars.add(runtimeJar.getFile());
}
}
workspaceBuilder.generatedJars.addAll(javaRuleIdeInfo.generatedJars);
}
@Nullable
private String findSourceVersion(ImmutableMap<Label, RuleIdeInfo> ruleMap) {
for (RuleIdeInfo rule : ruleMap.values()) {
if (rule.javaToolchainIdeInfo != null) {
return rule.javaToolchainIdeInfo.sourceVersion;
}
}
return null;
}
static class WorkspaceBuilder {
Set<String> jdeps = Sets.newHashSet();
Set<Label> directDeps = Sets.newHashSet();
Set<File> addedSourceFiles = Sets.newHashSet();
List<LibraryArtifact> generatedJars = Lists.newArrayList();
List<File> buildOutputJars = Lists.newArrayList();
List<SourceArtifact> sourceArtifacts = Lists.newArrayList();
Map<Label, ArtifactLocation> javaPackageManifests = Maps.newHashMap();
}
}