blob: 93e4854701c28b075c2f278cb0fdd7157eab0d51 [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.plugin.run;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
import com.google.idea.blaze.base.command.buildresult.BuildResultHelper;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.repackaged.devtools.intellij.plugin.IntellijPluginTargetDeployInfo.IntellijPluginDeployFile;
import com.google.repackaged.devtools.intellij.plugin.IntellijPluginTargetDeployInfo.IntellijPluginDeployInfo;
import com.google.repackaged.protobuf.TextFormat;
import com.intellij.concurrency.AsyncUtil;
import com.intellij.execution.ExecutionException;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.util.BuildNumber;
import com.intellij.openapi.util.Key;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import javax.annotation.Nullable;
/** Handles finding files to deploy and copying these into the sandbox. */
class BlazeIntellijPluginDeployer {
static final Key<BlazeIntellijPluginDeployer> USER_DATA_KEY =
Key.create(BlazeIntellijPluginDeployer.class.getName());
private final String sandboxHome;
private final String buildNumber;
private final Label pluginTarget;
private final List<File> deployInfoFiles = new ArrayList<>();
private final Map<File, File> filesToDeploy = Maps.newHashMap();
private File executionRoot;
private Future<Void> fileCopyingTask;
BlazeIntellijPluginDeployer(String sandboxHome, String buildNumber, Label pluginTarget) {
this.sandboxHome = sandboxHome;
this.buildNumber = buildNumber;
this.pluginTarget = pluginTarget;
}
void reportBuildComplete(File executionRoot, BuildResultHelper buildResultHelper) {
this.executionRoot = executionRoot;
for (File file : buildResultHelper.getBuildArtifactsForTarget(pluginTarget)) {
if (file.getName().endsWith(".intellij-plugin-debug-target-deploy-info")) {
deployInfoFiles.add(file);
}
}
}
/**
* Returns a list of plugin IDs, and asynchronously copies the corresponding files to the sandbox.
*/
List<String> deployNonBlocking() throws ExecutionException {
List<IntellijPluginDeployInfo> deployInfoList = Lists.newArrayList();
for (File deployInfoFile : deployInfoFiles) {
deployInfoList.addAll(readDeployInfoFromFile(deployInfoFile));
}
ImmutableMap<File, File> filesToDeploy = getFilesToDeploy(executionRoot, deployInfoList);
this.filesToDeploy.putAll(filesToDeploy);
for (File file : filesToDeploy.keySet()) {
if (!file.exists()) {
throw new ExecutionException(
String.format("Plugin file '%s' not found. Did the build fail?", file.getName()));
}
}
// kick off file copying task asynchronously, so it doesn't block the EDT.
fileCopyingTask =
BlazeExecutor.getInstance()
.submit(
() -> {
for (Map.Entry<File, File> entry : filesToDeploy.entrySet()) {
copyFileToSandbox(entry.getKey(), entry.getValue());
}
return null;
});
return readPluginIds(filesToDeploy.keySet());
}
/** Blocks until the plugin files have been copied to the sandbox */
void blockUntilDeployComplete() {
AsyncUtil.get(fileCopyingTask);
fileCopyingTask = null;
}
void deleteDeployment() {
for (File file : filesToDeploy.values()) {
if (file.exists()) {
file.delete();
}
}
}
private static ImmutableList<IntellijPluginDeployInfo> readDeployInfoFromFile(File deployInfoFile)
throws ExecutionException {
ImmutableList.Builder<IntellijPluginDeployInfo> result = ImmutableList.builder();
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(deployInfoFile))) {
IntellijPluginDeployInfo.Builder builder = IntellijPluginDeployInfo.newBuilder();
TextFormat.Parser parser = TextFormat.Parser.newBuilder().setAllowUnknownFields(true).build();
parser.merge(new InputStreamReader(inputStream, UTF_8), builder);
IntellijPluginDeployInfo deployInfo = builder.build();
result.add(deployInfo);
} catch (IOException e) {
throw new ExecutionException(e);
}
return result.build();
}
private ImmutableMap<File, File> getFilesToDeploy(
File executionRoot, Collection<IntellijPluginDeployInfo> deployInfos) {
ImmutableMap.Builder<File, File> result = ImmutableMap.builder();
for (IntellijPluginDeployInfo deployInfo : deployInfos) {
for (IntellijPluginDeployFile deployFile : deployInfo.getDeployFilesList()) {
File src = new File(executionRoot, deployFile.getExecutionPath());
File dest = new File(sandboxPluginDirectory(sandboxHome), deployFile.getDeployLocation());
result.put(src, dest);
}
}
return result.build();
}
private static File sandboxPluginDirectory(String sandboxHome) {
return new File(sandboxHome, "plugins");
}
private List<String> readPluginIds(Collection<File> files) throws ExecutionException {
List<String> pluginIds = Lists.newArrayList();
for (File file : files) {
if (file.getName().endsWith(".jar")) {
String pluginId = readPluginIdFromJar(buildNumber, file);
if (pluginId != null) {
pluginIds.add(pluginId);
}
}
}
return pluginIds;
}
@Nullable
private static String readPluginIdFromJar(String buildNumber, File jar)
throws ExecutionException {
IdeaPluginDescriptor pluginDescriptor = PluginManagerCore.loadDescriptor(jar, "plugin.xml");
if (pluginDescriptor == null) {
return null;
}
if (PluginManagerCore.isIncompatible(pluginDescriptor, BuildNumber.fromString(buildNumber))) {
throw new ExecutionException(
String.format(
"Plugin SDK version '%s' is incompatible with this plugin "
+ "(since: '%s', until: '%s')",
buildNumber, pluginDescriptor.getSinceBuild(), pluginDescriptor.getUntilBuild()));
}
return pluginDescriptor.getPluginId().getIdString();
}
private static void copyFileToSandbox(File src, File dest) throws ExecutionException {
try {
dest.getParentFile().mkdirs();
Files.copy(src.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
dest.deleteOnExit();
} catch (IOException e) {
throw new ExecutionException("Error copying plugin file to sandbox", e);
}
}
}