| /* |
| * 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.base.sync.aspects; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Ordering; |
| import com.google.common.collect.Sets; |
| import com.google.common.util.concurrent.Futures; |
| import com.google.common.util.concurrent.ListenableFuture; |
| import com.google.common.util.concurrent.ListeningExecutorService; |
| import com.google.idea.blaze.base.async.FutureUtil; |
| import com.google.idea.blaze.base.async.executor.BlazeExecutor; |
| import com.google.idea.blaze.base.async.process.ExternalTask; |
| import com.google.idea.blaze.base.async.process.LineProcessingOutputStream; |
| import com.google.idea.blaze.base.bazel.BuildSystemProvider; |
| import com.google.idea.blaze.base.command.BlazeCommand; |
| import com.google.idea.blaze.base.command.BlazeCommandName; |
| import com.google.idea.blaze.base.command.BlazeFlags; |
| import com.google.idea.blaze.base.command.BlazeInvocationContext; |
| import com.google.idea.blaze.base.command.buildresult.BuildResultHelper; |
| import com.google.idea.blaze.base.command.info.BlazeConfigurationHandler; |
| import com.google.idea.blaze.base.filecache.FileDiffer; |
| import com.google.idea.blaze.base.ideinfo.TargetIdeInfo; |
| import com.google.idea.blaze.base.ideinfo.TargetKey; |
| import com.google.idea.blaze.base.ideinfo.TargetMap; |
| import com.google.idea.blaze.base.issueparser.IssueOutputLineProcessor; |
| import com.google.idea.blaze.base.lang.AdditionalLanguagesHelper; |
| import com.google.idea.blaze.base.model.BlazeVersionData; |
| import com.google.idea.blaze.base.model.SyncState; |
| import com.google.idea.blaze.base.model.primitives.Kind; |
| import com.google.idea.blaze.base.model.primitives.LanguageClass; |
| import com.google.idea.blaze.base.model.primitives.TargetExpression; |
| import com.google.idea.blaze.base.model.primitives.WorkspaceRoot; |
| import com.google.idea.blaze.base.prefetch.PrefetchFileSource; |
| import com.google.idea.blaze.base.prefetch.PrefetchService; |
| import com.google.idea.blaze.base.projectview.ProjectViewSet; |
| import com.google.idea.blaze.base.scope.BlazeContext; |
| import com.google.idea.blaze.base.scope.Result; |
| import com.google.idea.blaze.base.scope.Scope; |
| import com.google.idea.blaze.base.scope.ScopedFunction; |
| import com.google.idea.blaze.base.scope.output.IssueOutput; |
| import com.google.idea.blaze.base.scope.output.PerformanceWarning; |
| import com.google.idea.blaze.base.scope.output.PrintOutput; |
| import com.google.idea.blaze.base.scope.scopes.TimingScope; |
| import com.google.idea.blaze.base.settings.Blaze; |
| import com.google.idea.blaze.base.settings.Blaze.BuildSystem; |
| import com.google.idea.blaze.base.sync.aspects.BuildResult.Status; |
| import com.google.idea.blaze.base.sync.aspects.strategy.AspectStrategy; |
| import com.google.idea.blaze.base.sync.aspects.strategy.AspectStrategyProvider; |
| import com.google.idea.blaze.base.sync.projectview.ImportRoots; |
| import com.google.idea.blaze.base.sync.projectview.LanguageSupport; |
| import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings; |
| import com.google.idea.blaze.base.sync.sharding.ShardedTargetList; |
| import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder; |
| import com.google.repackaged.devtools.intellij.ideinfo.IntellijIdeInfo; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.pom.NavigatableAdapter; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.atomic.AtomicLong; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| import java.util.zip.GZIPInputStream; |
| import javax.annotation.Nullable; |
| |
| /** Implementation of BlazeIdeInterface based on aspects. */ |
| public class BlazeIdeInterfaceAspectsImpl implements BlazeIdeInterface { |
| |
| private static final Logger logger = Logger.getInstance(BlazeIdeInterfaceAspectsImpl.class); |
| |
| static class State implements Serializable { |
| private static final long serialVersionUID = 14L; |
| TargetMap targetMap; |
| ImmutableMap<File, Long> fileState = null; |
| Map<File, TargetKey> fileToTargetMapKey = Maps.newHashMap(); |
| WorkspaceLanguageSettings workspaceLanguageSettings; |
| String aspectStrategyName; |
| } |
| |
| @Override |
| public IdeResult updateTargetMap( |
| Project project, |
| BlazeContext context, |
| WorkspaceRoot workspaceRoot, |
| ProjectViewSet projectViewSet, |
| BlazeVersionData blazeVersionData, |
| BlazeConfigurationHandler configHandler, |
| ShardedTargetList shardedTargets, |
| WorkspaceLanguageSettings workspaceLanguageSettings, |
| ArtifactLocationDecoder artifactLocationDecoder, |
| SyncState.Builder syncStateBuilder, |
| @Nullable SyncState previousSyncState, |
| boolean mergeWithOldState) { |
| State prevState = previousSyncState != null ? previousSyncState.get(State.class) : null; |
| |
| // If the language filter has changed, redo everything from scratch |
| if (prevState != null |
| && !prevState.workspaceLanguageSettings.equals(workspaceLanguageSettings)) { |
| prevState = null; |
| } |
| |
| // If the aspect strategy has changed, redo everything from scratch |
| final AspectStrategy aspectStrategy = getAspectStrategy(blazeVersionData); |
| if (prevState != null |
| && !Objects.equals(prevState.aspectStrategyName, aspectStrategy.getName())) { |
| prevState = null; |
| } |
| |
| IdeInfoResult ideInfoResult = |
| getIdeInfo( |
| project, |
| context, |
| workspaceRoot, |
| projectViewSet, |
| blazeVersionData, |
| workspaceLanguageSettings.activeLanguages, |
| shardedTargets, |
| aspectStrategy); |
| if (ideInfoResult.buildResult.status == BuildResult.Status.FATAL_ERROR) { |
| return new IdeResult( |
| prevState != null ? prevState.targetMap : null, ideInfoResult.buildResult); |
| } |
| // If there was a partial error, make a best-effort attempt to sync. Retain |
| // any old state that we have in an attempt not to lose too much code. |
| if (ideInfoResult.buildResult.status == BuildResult.Status.BUILD_ERROR) { |
| mergeWithOldState = true; |
| } |
| |
| Collection<File> fileList = ideInfoResult.files; |
| List<File> updatedFiles = Lists.newArrayList(); |
| List<File> removedFiles = Lists.newArrayList(); |
| ImmutableMap<File, Long> fileState = |
| FileDiffer.updateFiles( |
| prevState != null ? prevState.fileState : null, fileList, updatedFiles, removedFiles); |
| if (fileState == null) { |
| return new IdeResult(prevState != null ? prevState.targetMap : null, BuildResult.FATAL_ERROR); |
| } |
| |
| context.output( |
| PrintOutput.log( |
| String.format( |
| "Total rules: %d, new/changed: %d, removed: %d", |
| fileList.size(), updatedFiles.size(), removedFiles.size()))); |
| |
| ListenableFuture<?> prefetchFuture = |
| PrefetchService.getInstance().prefetchFiles(project, updatedFiles, true); |
| if (!FutureUtil.waitForFuture(context, prefetchFuture) |
| .timed("FetchAspectOutput") |
| .withProgressMessage("Reading IDE info result...") |
| .run() |
| .success()) { |
| return new IdeResult(prevState != null ? prevState.targetMap : null, BuildResult.FATAL_ERROR); |
| } |
| |
| ImportRoots importRoots = |
| ImportRoots.builder(workspaceRoot, Blaze.getBuildSystem(project)) |
| .add(projectViewSet) |
| .build(); |
| |
| State state = |
| updateState( |
| project, |
| context, |
| prevState, |
| fileState, |
| configHandler, |
| workspaceLanguageSettings, |
| importRoots, |
| aspectStrategy, |
| updatedFiles, |
| removedFiles, |
| mergeWithOldState); |
| if (state == null) { |
| return new IdeResult(prevState != null ? prevState.targetMap : null, BuildResult.FATAL_ERROR); |
| } |
| syncStateBuilder.put(State.class, state); |
| return new IdeResult(state.targetMap, ideInfoResult.buildResult); |
| } |
| |
| private static class IdeInfoResult { |
| final Collection<File> files; |
| final BuildResult buildResult; |
| |
| IdeInfoResult(Collection<File> files, BuildResult buildResult) { |
| this.files = files; |
| this.buildResult = buildResult; |
| } |
| } |
| |
| private static IdeInfoResult getIdeInfo( |
| Project project, |
| BlazeContext parentContext, |
| WorkspaceRoot workspaceRoot, |
| ProjectViewSet projectViewSet, |
| BlazeVersionData blazeVersionData, |
| ImmutableSet<LanguageClass> activeLanguages, |
| ShardedTargetList shardedTargets, |
| AspectStrategy aspectStrategy) { |
| return Scope.push( |
| parentContext, |
| context -> { |
| context.push( |
| new TimingScope(String.format("Execute%sCommand", Blaze.buildSystemName(project)))); |
| Set<File> ideInfoFiles = new LinkedHashSet<>(); |
| Function<Integer, String> progressMessage = |
| count -> |
| String.format( |
| "Building IDE info files for shard %s of %s...", |
| count, shardedTargets.shardedTargets.size()); |
| Function<List<TargetExpression>, BuildResult> invocation = |
| targets -> { |
| IdeInfoResult result = |
| getIdeInfoForTargets( |
| project, |
| context, |
| workspaceRoot, |
| projectViewSet, |
| blazeVersionData, |
| activeLanguages, |
| targets, |
| aspectStrategy); |
| ideInfoFiles.addAll(result.files); |
| return result.buildResult; |
| }; |
| BuildResult result = |
| shardedTargets.runShardedCommand(project, context, progressMessage, invocation); |
| return new IdeInfoResult(ideInfoFiles, result); |
| }); |
| } |
| |
| /** Runs blaze build with the aspect's ide-info output group for a given set of targets */ |
| private static IdeInfoResult getIdeInfoForTargets( |
| Project project, |
| BlazeContext context, |
| WorkspaceRoot workspaceRoot, |
| ProjectViewSet projectViewSet, |
| BlazeVersionData blazeVersionData, |
| ImmutableSet<LanguageClass> activeLanguages, |
| List<TargetExpression> targets, |
| AspectStrategy aspectStrategy) { |
| String fileExtension = aspectStrategy.getAspectOutputFileExtension(); |
| String gzFileExtension = fileExtension + ".gz"; |
| Predicate<String> fileFilter = |
| fileName -> fileName.endsWith(fileExtension) || fileName.endsWith(gzFileExtension); |
| BuildResultHelper buildResultHelper = BuildResultHelper.forFiles(blazeVersionData, fileFilter); |
| |
| BlazeCommand.Builder builder = |
| BlazeCommand.builder(getBinaryPath(project), BlazeCommandName.BUILD) |
| .addTargets(targets) |
| .addBlazeFlags(BlazeFlags.KEEP_GOING) |
| .addBlazeFlags(buildResultHelper.getBuildFlags()) |
| .addBlazeFlags( |
| BlazeFlags.blazeFlags( |
| project, projectViewSet, BlazeCommandName.BUILD, BlazeInvocationContext.Sync)); |
| |
| aspectStrategy.modifyIdeInfoCommand(builder, activeLanguages); |
| |
| int retVal = |
| ExternalTask.builder(workspaceRoot) |
| .addBlazeCommand(builder.build()) |
| .context(context) |
| .stderr( |
| buildResultHelper.stderr( |
| new IssueOutputLineProcessor(project, context, workspaceRoot))) |
| .build() |
| .run(); |
| |
| BuildResult buildResult = BuildResult.fromExitCode(retVal); |
| if (buildResult.status == Status.FATAL_ERROR) { |
| buildResultHelper.close(); |
| return new IdeInfoResult(ImmutableList.of(), buildResult); |
| } |
| return new IdeInfoResult(buildResultHelper.getBuildArtifacts(), buildResult); |
| } |
| |
| private static class TargetFilePair { |
| private final File file; |
| private final TargetIdeInfo target; |
| |
| TargetFilePair(File file, TargetIdeInfo target) { |
| this.file = file; |
| this.target = target; |
| } |
| } |
| |
| @Nullable |
| static State updateState( |
| Project project, |
| BlazeContext parentContext, |
| @Nullable State prevState, |
| ImmutableMap<File, Long> fileState, |
| BlazeConfigurationHandler configHandler, |
| WorkspaceLanguageSettings workspaceLanguageSettings, |
| ImportRoots importRoots, |
| AspectStrategy aspectStrategy, |
| List<File> newFiles, |
| List<File> removedFiles, |
| boolean mergeWithOldState) { |
| Result<State> result = |
| Scope.push( |
| parentContext, |
| (ScopedFunction<Result<State>>) |
| context -> { |
| context.push(new TimingScope("UpdateTargetMap")); |
| |
| // If we're not removing we have to merge the old state |
| // into the new one or we'll miss file removes next time |
| ImmutableMap<File, Long> nextFileState = fileState; |
| if (mergeWithOldState && prevState != null) { |
| ImmutableMap.Builder<File, Long> fileStateBuilder = |
| ImmutableMap.<File, Long>builder().putAll(fileState); |
| for (Map.Entry<File, Long> entry : prevState.fileState.entrySet()) { |
| if (!fileState.containsKey(entry.getKey())) { |
| fileStateBuilder.put(entry); |
| } |
| } |
| nextFileState = fileStateBuilder.build(); |
| } |
| |
| State state = new State(); |
| state.fileState = nextFileState; |
| state.workspaceLanguageSettings = workspaceLanguageSettings; |
| state.aspectStrategyName = aspectStrategy.getName(); |
| |
| Map<TargetKey, TargetIdeInfo> targetMap = Maps.newHashMap(); |
| if (prevState != null) { |
| targetMap.putAll(prevState.targetMap.map()); |
| state.fileToTargetMapKey.putAll(prevState.fileToTargetMapKey); |
| } |
| |
| // Update removed unless we're merging with the old state |
| if (!mergeWithOldState) { |
| for (File removedFile : removedFiles) { |
| TargetKey key = state.fileToTargetMapKey.remove(removedFile); |
| if (key != null) { |
| targetMap.remove(key); |
| } |
| } |
| } |
| |
| AtomicLong totalSizeLoaded = new AtomicLong(0); |
| Set<LanguageClass> ignoredLanguages = Sets.newConcurrentHashSet(); |
| |
| ListeningExecutorService executor = BlazeExecutor.getInstance().getExecutor(); |
| |
| // Read protos from any new files |
| List<ListenableFuture<TargetFilePair>> futures = Lists.newArrayList(); |
| for (File file : newFiles) { |
| futures.add( |
| executor.submit( |
| () -> { |
| totalSizeLoaded.addAndGet(file.length()); |
| try (InputStream inputStream = getAspectInputStream(file)) { |
| IntellijIdeInfo.TargetIdeInfo message = |
| aspectStrategy.readAspectFile(inputStream); |
| TargetIdeInfo target = |
| protoToTarget( |
| workspaceLanguageSettings, |
| importRoots, |
| message, |
| ignoredLanguages); |
| return new TargetFilePair(file, target); |
| } |
| })); |
| } |
| |
| Set<TargetKey> newTargets = new HashSet<>(); |
| Set<String> configurations = new LinkedHashSet<>(); |
| configurations.add(configHandler.defaultConfigurationPathComponent); |
| |
| // Update state with result from proto files |
| int duplicateTargetLabels = 0; |
| try { |
| for (TargetFilePair targetFilePair : Futures.allAsList(futures).get()) { |
| if (targetFilePair.target != null) { |
| File file = targetFilePair.file; |
| String config = configHandler.getConfigurationPathComponent(file); |
| configurations.add(config); |
| TargetKey key = targetFilePair.target.key; |
| if (targetMap.putIfAbsent(key, targetFilePair.target) == null) { |
| state.fileToTargetMapKey.put(file, key); |
| } else { |
| if (!newTargets.add(key)) { |
| duplicateTargetLabels++; |
| } |
| // prioritize the default configuration over build order |
| if (Objects.equals( |
| config, configHandler.defaultConfigurationPathComponent)) { |
| targetMap.put(key, targetFilePair.target); |
| state.fileToTargetMapKey.put(file, key); |
| } |
| } |
| } |
| } |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| return Result.error(null); |
| } catch (ExecutionException e) { |
| return Result.error(e); |
| } |
| |
| context.output( |
| PrintOutput.log( |
| String.format( |
| "Loaded %d aspect files, total size %dkB", |
| newFiles.size(), totalSizeLoaded.get() / 1024))); |
| if (duplicateTargetLabels > 0) { |
| context.output( |
| new PerformanceWarning( |
| String.format( |
| "There were %d duplicate rules, built with the following " |
| + "configurations: %s.\nYour IDE sync is slowed down by ~%d%%.", |
| duplicateTargetLabels, |
| configurations, |
| (100 * duplicateTargetLabels / targetMap.size())))); |
| } |
| |
| ignoredLanguages.retainAll( |
| LanguageSupport.availableAdditionalLanguages( |
| workspaceLanguageSettings.getWorkspaceType())); |
| warnIgnoredLanguages(project, context, ignoredLanguages); |
| |
| state.targetMap = new TargetMap(ImmutableMap.copyOf(targetMap)); |
| return Result.of(state); |
| }); |
| |
| if (result.error != null) { |
| logger.error(result.error); |
| return null; |
| } |
| return result.result; |
| } |
| |
| @Nullable |
| private static TargetIdeInfo protoToTarget( |
| WorkspaceLanguageSettings languageSettings, |
| ImportRoots importRoots, |
| IntellijIdeInfo.TargetIdeInfo message, |
| Set<LanguageClass> ignoredLanguages) { |
| Kind kind = IdeInfoFromProtobuf.getKind(message); |
| if (kind == null) { |
| return null; |
| } |
| if (languageSettings.isLanguageActive(kind.languageClass)) { |
| return IdeInfoFromProtobuf.makeTargetIdeInfo(message); |
| } |
| if (importRoots.importAsSource(IdeInfoFromProtobuf.getKey(message).label)) { |
| ignoredLanguages.add(kind.languageClass); |
| } |
| return null; |
| } |
| |
| private static void warnIgnoredLanguages( |
| Project project, BlazeContext context, Set<LanguageClass> ignoredLangs) { |
| if (ignoredLangs.isEmpty()) { |
| return; |
| } |
| List<LanguageClass> sorted = new ArrayList<>(ignoredLangs); |
| sorted.sort(Ordering.usingToString()); |
| |
| String msg = |
| "Some project targets were ignored because the corresponding language support " |
| + "isn't enabled. Click here to enable support for: " |
| + Joiner.on(", ").join(sorted); |
| IssueOutput.warn(msg) |
| .navigatable( |
| new NavigatableAdapter() { |
| @Override |
| public void navigate(boolean requestFocus) { |
| AdditionalLanguagesHelper.enableLanguageSupport(project, sorted); |
| } |
| }) |
| .submit(context); |
| } |
| |
| private static InputStream getAspectInputStream(File file) throws IOException { |
| InputStream inputStream = new FileInputStream(file); |
| if (file.getName().endsWith(".gz")) { |
| inputStream = new GZIPInputStream(inputStream); |
| } |
| return inputStream; |
| } |
| |
| @Override |
| public BuildResult resolveIdeArtifacts( |
| Project project, |
| BlazeContext context, |
| WorkspaceRoot workspaceRoot, |
| ProjectViewSet projectViewSet, |
| BlazeVersionData blazeVersionData, |
| WorkspaceLanguageSettings workspaceLanguageSettings, |
| ShardedTargetList shardedTargets) { |
| return resolveIdeArtifacts( |
| project, |
| context, |
| workspaceRoot, |
| projectViewSet, |
| blazeVersionData, |
| workspaceLanguageSettings, |
| shardedTargets, |
| false); |
| } |
| |
| @Override |
| public BuildResult compileIdeArtifacts( |
| Project project, |
| BlazeContext context, |
| WorkspaceRoot workspaceRoot, |
| ProjectViewSet projectViewSet, |
| BlazeVersionData blazeVersionData, |
| WorkspaceLanguageSettings workspaceLanguageSettings, |
| ShardedTargetList shardedTargets) { |
| boolean ideCompile = hasIdeCompileOutputGroup(blazeVersionData); |
| return resolveIdeArtifacts( |
| project, |
| context, |
| workspaceRoot, |
| projectViewSet, |
| blazeVersionData, |
| workspaceLanguageSettings, |
| shardedTargets, |
| ideCompile); |
| } |
| |
| private static boolean hasIdeCompileOutputGroup(BlazeVersionData blazeVersionData) { |
| return blazeVersionData.buildSystem() == BuildSystem.Blaze |
| || blazeVersionData.bazelIsAtLeastVersion(0, 4, 4); |
| } |
| |
| private static BuildResult resolveIdeArtifacts( |
| Project project, |
| BlazeContext context, |
| WorkspaceRoot workspaceRoot, |
| ProjectViewSet projectViewSet, |
| BlazeVersionData blazeVersionData, |
| WorkspaceLanguageSettings workspaceLanguageSettings, |
| ShardedTargetList shardedTargets, |
| boolean useIdeCompileOutputGroup) { |
| |
| Function<Integer, String> progressMessage = |
| count -> |
| String.format( |
| "Building IDE resolve files for shard %s of %s...", |
| count, shardedTargets.shardedTargets.size()); |
| Function<List<TargetExpression>, BuildResult> invocation = |
| targets -> |
| useIdeCompileOutputGroup |
| ? doCompileIdeArtifacts( |
| project, |
| context, |
| workspaceRoot, |
| projectViewSet, |
| blazeVersionData, |
| workspaceLanguageSettings, |
| targets) |
| : doResolveIdeArtifacts( |
| project, |
| context, |
| workspaceRoot, |
| projectViewSet, |
| blazeVersionData, |
| workspaceLanguageSettings, |
| targets); |
| return shardedTargets.runShardedCommand(project, context, progressMessage, invocation); |
| } |
| |
| /** |
| * Blaze build invocation requesting the 'intellij-resolve' aspect output group. |
| * |
| * <p>Prefetches the output artifacts built by this invocation. |
| */ |
| private static BuildResult doResolveIdeArtifacts( |
| Project project, |
| BlazeContext context, |
| WorkspaceRoot workspaceRoot, |
| ProjectViewSet projectViewSet, |
| BlazeVersionData blazeVersionData, |
| WorkspaceLanguageSettings workspaceLanguageSettings, |
| List<TargetExpression> targets) { |
| BuildResultHelper buildResultHelper = |
| BuildResultHelper.forFiles(blazeVersionData, getGenfilePrefetchFilter()); |
| |
| BlazeCommand.Builder blazeCommandBuilder = |
| BlazeCommand.builder(getBinaryPath(project), BlazeCommandName.BUILD) |
| .addTargets(targets) |
| .addBlazeFlags(BlazeFlags.KEEP_GOING) |
| .addBlazeFlags(buildResultHelper.getBuildFlags()) |
| .addBlazeFlags( |
| BlazeFlags.blazeFlags( |
| project, projectViewSet, BlazeCommandName.BUILD, BlazeInvocationContext.Sync)); |
| |
| // Request the 'intellij-resolve' aspect output group. |
| getAspectStrategy(blazeVersionData) |
| .modifyIdeResolveCommand(blazeCommandBuilder, workspaceLanguageSettings.activeLanguages); |
| |
| // Run the blaze build command, parsing any output artifacts produced. |
| int retVal = |
| ExternalTask.builder(workspaceRoot) |
| .addBlazeCommand(blazeCommandBuilder.build()) |
| .context(context) |
| .stderr( |
| buildResultHelper.stderr( |
| new IssueOutputLineProcessor(project, context, workspaceRoot))) |
| .build() |
| .run(new TimingScope("ExecuteBlazeCommand")); |
| |
| BuildResult result = BuildResult.fromExitCode(retVal); |
| if (result.status != BuildResult.Status.FATAL_ERROR) { |
| prefetchGenfiles(project, context, buildResultHelper.getBuildArtifacts()); |
| } else { |
| buildResultHelper.close(); |
| } |
| return result; |
| } |
| |
| /** Blaze build invocation requesting the 'intellij-compile' aspect output group. */ |
| private static BuildResult doCompileIdeArtifacts( |
| Project project, |
| BlazeContext context, |
| WorkspaceRoot workspaceRoot, |
| ProjectViewSet projectViewSet, |
| BlazeVersionData blazeVersionData, |
| WorkspaceLanguageSettings workspaceLanguageSettings, |
| List<TargetExpression> targets) { |
| BlazeCommand.Builder blazeCommandBuilder = |
| BlazeCommand.builder(getBinaryPath(project), BlazeCommandName.BUILD) |
| .addTargets(targets) |
| .addBlazeFlags() |
| .addBlazeFlags(BlazeFlags.KEEP_GOING) |
| .addBlazeFlags( |
| BlazeFlags.blazeFlags( |
| project, projectViewSet, BlazeCommandName.BUILD, BlazeInvocationContext.Sync)); |
| |
| getAspectStrategy(blazeVersionData) |
| .modifyIdeCompileCommand(blazeCommandBuilder, workspaceLanguageSettings.activeLanguages); |
| |
| // Run the blaze build command. |
| int retVal = |
| ExternalTask.builder(workspaceRoot) |
| .addBlazeCommand(blazeCommandBuilder.build()) |
| .context(context) |
| .stderr( |
| LineProcessingOutputStream.of( |
| new IssueOutputLineProcessor(project, context, workspaceRoot))) |
| .build() |
| .run(); |
| |
| return BuildResult.fromExitCode(retVal); |
| } |
| |
| /** A filename filter for blaze output artifacts to prefetch. */ |
| private static Predicate<String> getGenfilePrefetchFilter() { |
| ImmutableSet<String> extensions = PrefetchFileSource.getAllPrefetchFileExtensions(); |
| return fileName -> extensions.contains(FileUtil.getExtension(fileName)); |
| } |
| |
| /** Prefetch a list of blaze output artifacts, blocking until complete. */ |
| private static void prefetchGenfiles( |
| Project project, BlazeContext context, ImmutableList<File> artifacts) { |
| ListenableFuture<?> prefetchFuture = |
| PrefetchService.getInstance().prefetchFiles(project, artifacts, false); |
| FutureUtil.waitForFuture(context, prefetchFuture) |
| .timed("PrefetchGenfiles") |
| .withProgressMessage("Prefetching genfiles...") |
| .run(); |
| } |
| |
| private static AspectStrategy getAspectStrategy(BlazeVersionData blazeVersionData) { |
| for (AspectStrategyProvider provider : AspectStrategyProvider.EP_NAME.getExtensions()) { |
| AspectStrategy aspectStrategy = provider.getAspectStrategy(blazeVersionData); |
| if (aspectStrategy != null) { |
| return aspectStrategy; |
| } |
| } |
| // Should never get here |
| throw new IllegalStateException("No aspect strategy found."); |
| } |
| |
| private static String getBinaryPath(Project project) { |
| BuildSystemProvider buildSystemProvider = Blaze.getBuildSystemProvider(project); |
| return buildSystemProvider.getSyncBinaryPath(); |
| } |
| } |