IDEA-161641 - initial Jlink artifacts support

IDEA-161641 - moved jlink packages to java.impl and java.compiler.impl modules

IDEA-161641 - small refactoring

IDEA-161641 - small refactoring

IDEA-161641 - fixed jlink tab ui, partially setup build task provider

IDEA-161641 - initial jlink implementation

GitOrigin-RevId: 2ce649f886ea43d362414d7b949f07feac5d231d
This commit is contained in:
Ilyas Selimov
2021-03-23 17:15:02 +07:00
committed by intellij-monorepo-bot
parent b566b6e97d
commit 3ad3f5c6fe
11 changed files with 481 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
# Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
com.intellij.packaging.impl.jlink.JLinkArtifactBuildTaskProvider

View File

@@ -0,0 +1,2 @@
# Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
com.intellij.packaging.impl.jlink.JpsJLinkModelSerializerExtension

View File

@@ -0,0 +1,191 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.impl.jlink;
import com.intellij.execution.CommandLineUtil;
import com.intellij.execution.process.BaseOSProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.builders.artifacts.ArtifactBuildTaskProvider;
import org.jetbrains.jps.incremental.BuildTask;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ProjectBuildException;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.model.JpsElement;
import org.jetbrains.jps.model.artifact.JpsArtifact;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import java.io.File;
import java.io.IOException;
import java.lang.module.ModuleFinder;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class JLinkArtifactBuildTaskProvider extends ArtifactBuildTaskProvider {
@Override
public @NotNull List<? extends BuildTask> createArtifactBuildTasks(@NotNull JpsArtifact artifact,
@NotNull ArtifactBuildPhase buildPhase) {
if (buildPhase != ArtifactBuildPhase.POST_PROCESSING || !(artifact.getArtifactType() instanceof JpsJLinkArtifactType)) {
return Collections.emptyList();
}
final JpsElement properties = artifact.getProperties();
if (!(properties instanceof JpsJLinkProperties)) {
return Collections.emptyList();
}
return Collections.singletonList(new JLinkBuildTask(artifact));
}
private static class JLinkBuildTask extends BuildTask {
private static final Logger LOG = Logger.getInstance(JLinkBuildTask.class);
private final JpsArtifact myArtifact;
private JLinkBuildTask(@NotNull JpsArtifact artifact) {
myArtifact = artifact;
}
@Override
public void build(CompileContext context) throws ProjectBuildException {
LOG.warn("jlink task was started");
Set<JpsSdk<?>> sdks = context.getProjectDescriptor().getProjectJavaSdks();
if (sdks.isEmpty()) {
return;
}
ModuleFinder moduleFinder = ModuleFinder.of(Path.of(myArtifact.getOutputFilePath()));
String modulesStr = moduleFinder.findAll().stream().map(mr -> mr.descriptor().name()).collect(Collectors.joining(","));
if (modulesStr.isEmpty()) {
error(context, "No module has been found");
return;
}
JpsSdk<?> javaSdk = sdks.iterator().next();
String sdkHomePath = javaSdk.getHomePath();
String jLinkPath = sdkHomePath + File.separatorChar + "bin" + File.separatorChar + "jlink";
List<String> commands = new ArrayList<>();
commands.add(jLinkPath);
JpsJLinkProperties properties = (JpsJLinkProperties)myArtifact.getProperties();
if (properties.compressionLevel.hasCompression()) {
addOption(commands, "--compress", String.valueOf(properties.compressionLevel.myValue));
}
if (properties.verbose) {
commands.add("--verbose");
}
commands.add("--module-path");
commands.add(myArtifact.getOutputFilePath());
commands.add("--add-modules");
commands.add(modulesStr);
String outputPath = myArtifact.getOutputFilePath() + File.separator + "jdk";
addOption(commands, "--output", outputPath);
LOG.warn(String.join(" ", commands));
try {
FileUtil.delete(Path.of(outputPath));
}
catch (IOException e) {
error(context,"Couldn't delete existing run-time image: " + e.getMessage());
}
final int errorCode = startProcess(context, commands, properties);
if (errorCode != 0) {
error(context,"jlink task has failed");
}
LOG.warn("jlink task was finished");
}
private int startProcess(@NotNull CompileContext context, @NotNull List<String> commands, @NotNull JpsJLinkProperties properties) {
try {
final AtomicInteger exitCode = new AtomicInteger();
final @NlsSafe StringBuilder errorOutput = new StringBuilder();
final List<@NlsSafe String> delayedInfoOutput = new ArrayList<>();
final Process process = new ProcessBuilder(CommandLineUtil.toCommandLine(commands)).start();
BaseOSProcessHandler handler = new BaseOSProcessHandler(process, commands.toString(), null);
handler.addProcessListener(new ProcessAdapter() {
@Override
public void startNotified(@NotNull ProcessEvent event) {
if (properties.verbose) {
LOG.info("Started " + commands);
}
}
@Override
public void processTerminated(@NotNull ProcessEvent event) {
if (properties.verbose) {
LOG.info("Terminated " + commands + ", exit code: " + event.getExitCode());
}
exitCode.set(event.getExitCode());
}
@Override
public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
String message = StringUtil.trimTrailing(event.getText());
if (outputType == ProcessOutputTypes.STDERR) {
LOG.error(message, (Throwable)null);
errorOutput.append(event.getText());
}
else {
LOG.info(message);
if (properties.verbose) {
info(context, message);
}
else {
delayedInfoOutput.add(message);
}
}
}
});
handler.startNotify();
handler.waitFor();
int result = exitCode.get();
if (result != 0) {
final String message = errorOutput.toString();
if (!StringUtil.isEmptyOrSpaces(message)) {
error(context, message);
}
for (@NlsSafe String info : delayedInfoOutput) {
error(context, info);
}
}
return result;
}
catch (Exception e) {
error(context, e.getMessage());
LOG.warn(e);
return -1;
}
}
private static void addOption(List<String> commands, @NotNull String key, @Nullable String value) {
if (!StringUtil.isEmpty(value)) {
commands.add(key);
commands.add(value);
}
}
private void error(@NotNull CompileContext compileContext, @Nls String message) {
compileContext.processMessage(new CompilerMessage("jlink", BuildMessage.Kind.ERROR, message));
}
private void info(@NotNull CompileContext compileContext, @Nls String message) {
compileContext.processMessage(new CompilerMessage("jlink", BuildMessage.Kind.INFO, message));
}
}
}

View File

@@ -0,0 +1,13 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.impl.jlink;
import org.jetbrains.jps.model.artifact.JpsArtifactType;
import org.jetbrains.jps.model.ex.JpsElementTypeBase;
public class JpsJLinkArtifactType extends JpsElementTypeBase<JpsJLinkProperties> implements JpsArtifactType<JpsJLinkProperties> {
public static final JpsJLinkArtifactType INSTANCE = new JpsJLinkArtifactType();
private JpsJLinkArtifactType() {
}
}

View File

@@ -0,0 +1,44 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.impl.jlink;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.XmlSerializer;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension;
import org.jetbrains.jps.model.serialization.artifact.ArtifactPropertiesState;
import org.jetbrains.jps.model.serialization.artifact.JpsArtifactPropertiesSerializer;
import java.util.Collections;
import java.util.List;
public class JpsJLinkModelSerializerExtension extends JpsModelSerializerExtension {
@Override
public @NotNull List<? extends JpsArtifactPropertiesSerializer<?>> getArtifactTypePropertiesSerializers() {
return Collections.singletonList(new JpsJLinkArtifactPropertiesSerializer());
}
static class JpsJLinkArtifactPropertiesSerializer extends JpsArtifactPropertiesSerializer<JpsJLinkProperties> {
JpsJLinkArtifactPropertiesSerializer() {
super("jlink", JpsJLinkArtifactType.INSTANCE);
}
@Override
public JpsJLinkProperties loadProperties(List<ArtifactPropertiesState> stateList) {
final ArtifactPropertiesState state = findApplicationProperties(stateList);
if (state != null) {
final Element options = state.getOptions();
if (options != null) {
return new JpsJLinkProperties(XmlSerializer.deserialize(options, JpsJLinkProperties.class));
}
}
return new JpsJLinkProperties();
}
private static ArtifactPropertiesState findApplicationProperties(List<ArtifactPropertiesState> stateList) {
return ContainerUtil.find(stateList, state -> "jlink-properties".equals(state.getId()));
}
}
}

View File

@@ -0,0 +1,55 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.impl.jlink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.ex.JpsElementBase;
import java.util.stream.Stream;
public class JpsJLinkProperties extends JpsElementBase<JpsJLinkProperties> {
public CompressionLevel compressionLevel = CompressionLevel.ZERO;
public boolean verbose;
public JpsJLinkProperties() {
}
public JpsJLinkProperties(@NotNull JpsJLinkProperties properties) {
copyToThis(properties);
}
@Override
public @NotNull JpsJLinkProperties createCopy() {
return new JpsJLinkProperties(this);
}
@Override
public void applyChanges(@NotNull JpsJLinkProperties modified) {
copyToThis(modified);
}
private void copyToThis(@NotNull JpsJLinkProperties copy) {
compressionLevel = copy.compressionLevel;
verbose = copy.verbose;
}
public enum CompressionLevel {
ZERO(0),
FIRST(1),
SECOND(2);
public final int myValue;
CompressionLevel(int value) {
this.myValue = value;
}
@Nullable
public static CompressionLevel getLevelByValue(int value) {
return Stream.of(values()).filter(v -> v.myValue == value).findFirst().orElse(null);
}
public boolean hasCompression() {
return this == FIRST || this == SECOND;
}
}
}

View File

@@ -269,6 +269,7 @@
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
<compileServer.plugin classpath="java-compiler-impl.jar"/>
<notificationGroup id="jps configuration error"
toolWindowId="Build"
@@ -383,6 +384,8 @@
implementation="com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.ArtifactsSourceItemsProvider"/>
<packaging.artifactType implementation="com.intellij.packaging.impl.artifacts.JarArtifactType" order="first"/>
<packaging.artifactType implementation="com.intellij.packaging.impl.artifacts.PlainArtifactType" order="last"/>
<packaging.artifactType implementation="com.intellij.packaging.jlink.JLinkArtifactType"/>
<packaging.artifactPropertiesProvider implementation="com.intellij.packaging.jlink.JLinkArtifactPropertiesProvider"/>
<compiler.buildTargetScopeProvider implementation="com.intellij.packaging.impl.compiler.ArtifactBuildTargetScopeProvider"/>
<projectTaskRunner implementation="com.intellij.task.impl.JpsProjectTaskRunner" id="jps" order="last"/>

View File

@@ -0,0 +1,51 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.jlink;
import com.intellij.packaging.artifacts.ArtifactProperties;
import com.intellij.packaging.ui.ArtifactEditorContext;
import com.intellij.packaging.ui.ArtifactPropertiesEditor;
import com.intellij.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.stream.Stream;
public class JLinkArtifactProperties extends ArtifactProperties<JLinkArtifactProperties> {
public CompressionLevel compressionLevel = CompressionLevel.ZERO;
public boolean verbose;
@Override
public ArtifactPropertiesEditor createEditor(@NotNull ArtifactEditorContext context) {
return new JLinkArtifactPropertiesEditor(this, context.getProject());
}
@Override
public @Nullable JLinkArtifactProperties getState() {
return this;
}
@Override
public void loadState(@NotNull JLinkArtifactProperties state) {
XmlSerializerUtil.copyBean(state, this);
}
public enum CompressionLevel {
ZERO(0),
FIRST(1),
SECOND(2);
public final int myValue;
CompressionLevel(int value) {
this.myValue = value;
}
@Nullable
public static CompressionLevel getLevelByValue(int value) {
return Stream.of(values()).filter(v -> v.myValue == value).findFirst().orElse(null);
}
public boolean hasCompression() {
return this == FIRST || this == SECOND;
}
}
}

View File

@@ -0,0 +1,64 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.jlink;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.packaging.ui.ArtifactPropertiesEditor;
import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.EnumSet;
import java.util.Optional;
import static com.intellij.packaging.jlink.JLinkArtifactProperties.CompressionLevel;
public class JLinkArtifactPropertiesEditor extends ArtifactPropertiesEditor {
private final JLinkArtifactProperties myProperties;
private final Project myProject;
private ComboBox<Integer> myCompressionLevel;
private JCheckBox myVerbose;
public JLinkArtifactPropertiesEditor(@NotNull JLinkArtifactProperties properties, @NotNull Project project) {
myProperties = properties;
myProject = project;
}
@Nls
@Override
public String getTabName() {
return "JLink";
}
@Override
public @Nullable JComponent createComponent() {
final FormBuilder builder = new FormBuilder();
myCompressionLevel = new ComboBox<>(EnumSet.allOf(CompressionLevel.class).stream().map(level -> level.myValue).toArray(Integer[]::new));
myCompressionLevel.setItem(myProperties.compressionLevel.myValue);
builder.addLabeledComponent("Compress level", myCompressionLevel);
myVerbose = new JCheckBox("Enable verbose tracing", myProperties.verbose);
builder.addComponent(myVerbose);
return builder.getPanel();
}
@Override
public boolean isModified() {
if (myProperties.compressionLevel != CompressionLevel.getLevelByValue(myCompressionLevel.getItem())) return true;
if (myProperties.verbose != myVerbose.isSelected()) return true;
return false;
}
@Override
public void apply() {
myProperties.compressionLevel = Optional.ofNullable(myCompressionLevel.getItem())
.map(i -> CompressionLevel.getLevelByValue(i))
.orElse(CompressionLevel.ZERO);
myProperties.verbose = myVerbose.isSelected();
}
}

View File

@@ -0,0 +1,23 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.jlink;
import com.intellij.packaging.artifacts.ArtifactProperties;
import com.intellij.packaging.artifacts.ArtifactPropertiesProvider;
import com.intellij.packaging.artifacts.ArtifactType;
import org.jetbrains.annotations.NotNull;
public class JLinkArtifactPropertiesProvider extends ArtifactPropertiesProvider {
protected JLinkArtifactPropertiesProvider() {
super("jlink-properties");
}
@Override
public @NotNull ArtifactProperties<?> createProperties(@NotNull ArtifactType artifactType) {
return new JLinkArtifactProperties();
}
@Override
public boolean isAvailableFor(@NotNull ArtifactType type) {
return type instanceof JLinkArtifactType;
}
}

View File

@@ -0,0 +1,33 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.jlink;
import com.intellij.icons.AllIcons;
import com.intellij.packaging.artifacts.ArtifactType;
import com.intellij.packaging.elements.CompositePackagingElement;
import com.intellij.packaging.elements.PackagingElementFactory;
import com.intellij.packaging.elements.PackagingElementOutputKind;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class JLinkArtifactType extends ArtifactType {
protected JLinkArtifactType() {
super("jlink", () -> "Run-time image");
}
@Override
public @NotNull Icon getIcon() {
return AllIcons.Nodes.Artifact;
}
@Override
public @Nullable String getDefaultPathFor(@NotNull PackagingElementOutputKind kind) {
return "/";
}
@Override
public @NotNull CompositePackagingElement<?> createRootElement(@NotNull String artifactName) {
return PackagingElementFactory.getInstance().createArtifactRootElement();
}
}