Add IDEA run configuration that runs dev server

This commit is contained in:
Alexey Andreev 2018-12-14 19:53:22 +03:00
parent bab0cd59a6
commit d7d4dc1571
44 changed files with 1504 additions and 167 deletions

View File

@ -1,9 +1,11 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="IDEA" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin"> <configuration default="false" name="IDEA" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType">
<module name="teavm-idea" /> <module name="teavm-idea" />
<option name="VM_PARAMETERS" value="-Xmx1024m -Xms256m -ea" /> <option name="VM_PARAMETERS" value="-Xmx1024m -Xms256m -ea" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<predefined_log_file id="idea.log" enabled="true" /> <predefined_log_file enabled="true" id="idea.log" />
<method /> <method v="2">
<option name="Make" enabled="true" />
</method>
</configuration> </configuration>
</component> </component>

View File

@ -80,6 +80,11 @@
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
@ -135,6 +140,15 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>html/**</exclude>
</excludes>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
@ -151,7 +165,7 @@
</includes> </includes>
</artifactSet> </artifactSet>
<relocations> <relocations>
<relocation>cd <relocation>
<pattern>org.objectweb.asm</pattern> <pattern>org.objectweb.asm</pattern>
<shadedPattern>org.teavm.asm</shadedPattern> <shadedPattern>org.teavm.asm</shadedPattern>
</relocation> </relocation>

View File

@ -107,10 +107,19 @@
<artifactSet> <artifactSet>
<excludes> <excludes>
<exclude>junit:junit</exclude> <exclude>junit:junit</exclude>
<exclude>org:teavm:*</exclude> <exclude>org.teavm:teavm-interop</exclude>
<exclude>org.teavm:teavm-metaprogramming-api</exclude>
<exclude>com.fasterxml.jackson.core:jackson-annotations</exclude> <exclude>com.fasterxml.jackson.core:jackson-annotations</exclude>
</excludes> </excludes>
</artifactSet> </artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
</filters>
<relocations> <relocations>
<relocation> <relocation>
<pattern>org.objectweb.asm</pattern> <pattern>org.objectweb.asm</pattern>

View File

@ -79,6 +79,13 @@ public class InMemoryMethodNodeCache implements MethodNodeCache {
newAsyncItems.clear(); newAsyncItems.clear();
} }
public void invalidate() {
cache.clear();
newItems.clear();
asyncCache.clear();
newAsyncItems.clear();
}
static final class RegularItem { static final class RegularItem {
final RegularMethodNode node; final RegularMethodNode node;
final String[] dependencies; final String[] dependencies;

View File

@ -59,6 +59,11 @@ public class InMemoryProgramCache implements ProgramCache {
newItems.clear(); newItems.clear();
} }
public void invalidate() {
cache.clear();
newItems.clear();
}
static final class Item { static final class Item {
final Program program; final Program program;
final String[] dependencies; final String[] dependencies;

View File

@ -62,4 +62,9 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
cache.keySet().removeAll(classes); cache.keySet().removeAll(classes);
freshClasses.removeAll(classes); freshClasses.removeAll(classes);
} }
public void invalidate() {
cache.clear();
freshClasses.clear();
}
} }

View File

@ -365,7 +365,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
dependencyAnalyzer.setAsyncSupported(target.isAsyncSupported()); dependencyAnalyzer.setAsyncSupported(target.isAsyncSupported());
dependencyAnalyzer.setInterruptor(() -> { dependencyAnalyzer.setInterruptor(() -> {
int progress = lastKnownClasses > 0 ? dependencyAnalyzer.getReachableClasses().size() : 0; int progress = lastKnownClasses > 0 ? dependencyAnalyzer.getReachableClasses().size() : 0;
return progressListener.progressReached(progress) == TeaVMProgressFeedback.CONTINUE; cancelled |= progressListener.progressReached(progress) != TeaVMProgressFeedback.CONTINUE;
return !cancelled;
}); });
target.contributeDependencies(dependencyAnalyzer); target.contributeDependencies(dependencyAnalyzer);
dependencyAnalyzer.processDependencies(); dependencyAnalyzer.processDependencies();

View File

@ -34,17 +34,6 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>

View File

@ -43,17 +43,6 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>

View File

@ -38,17 +38,6 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>

View File

@ -57,17 +57,6 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -46,6 +46,7 @@
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
<optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.ow2.asm</groupId> <groupId>org.ow2.asm</groupId>
@ -82,17 +83,6 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>

10
pom.xml
View File

@ -197,11 +197,6 @@
<artifactId>rhino</artifactId> <artifactId>rhino</artifactId>
<version>${rhino.version}</version> <version>${rhino.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
@ -286,6 +281,11 @@
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version> <version>3.0.2</version>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
</plugin>
</plugins> </plugins>
</pluginManagement> </pluginManagement>
</build> </build>

View File

@ -86,7 +86,7 @@ public final class BenchmarkStarter {
private static void render() { private static void render() {
CanvasRenderingContext2D context = (CanvasRenderingContext2D) canvas.getContext("2d"); CanvasRenderingContext2D context = (CanvasRenderingContext2D) canvas.getContext("2d");
context.setFillStyle("white"); context.setFillStyle("white");
context.setStrokeStyle("grey"); context.setStrokeStyle("red");
context.fillRect(0, 0, 600, 600); context.fillRect(0, 0, 600, 600);
context.save(); context.save();
context.translate(0, 600); context.translate(0, 600);

View File

@ -48,13 +48,6 @@
<artifactId>teavm-jso-apis</artifactId> <artifactId>teavm-jso-apis</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso-apis</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-metaprogramming-impl</artifactId> <artifactId>teavm-metaprogramming-impl</artifactId>

View File

@ -110,18 +110,6 @@
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.teavm.cli.TeaVMRunner</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
@ -134,7 +122,16 @@
<configuration> <configuration>
<transformers> <transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.teavm.cli.TeaVMRunner</mainClass>
</transformer>
</transformers> </transformers>
<relocations>
<relocation>
<pattern>org.apache.commons</pattern>
<shadedPattern>org.teavm.apachecommons</shadedPattern>
</relocation>
</relocations>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>

View File

@ -114,7 +114,7 @@ public final class TeaVMDevServerRunner {
devServer.setIndicator(commandLine.hasOption("indicator")); devServer.setIndicator(commandLine.hasOption("indicator"));
devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload")); devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload"));
devServer.setVerbose(commandLine.hasOption('v')); devServer.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v')));
if (commandLine.hasOption("port")) { if (commandLine.hasOption("port")) {
try { try {
devServer.setPort(Integer.parseInt(commandLine.getOptionValue("port"))); devServer.setPort(Integer.parseInt(commandLine.getOptionValue("port")));

View File

@ -15,11 +15,18 @@
*/ */
package org.teavm.tooling; package org.teavm.tooling;
import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ProgramReader;
import org.teavm.model.TextLocation; import org.teavm.model.TextLocation;
import org.teavm.model.instructions.AbstractInstructionReader; import org.teavm.model.instructions.AbstractInstructionReader;
import org.teavm.vm.TeaVM;
class InstructionLocationReader extends AbstractInstructionReader { public class InstructionLocationReader extends AbstractInstructionReader {
private Set<String> resources; private Set<String> resources;
public InstructionLocationReader(Set<String> resources) { public InstructionLocationReader(Set<String> resources) {
@ -32,4 +39,32 @@ class InstructionLocationReader extends AbstractInstructionReader {
resources.add(location.getFileName()); resources.add(location.getFileName());
} }
} }
public static Set<String> extractUsedResources(TeaVM vm) {
Set<String> resources = new LinkedHashSet<>();
ClassReaderSource classSource = vm.getDependencyClassSource();
InstructionLocationReader reader = new InstructionLocationReader(resources);
for (MethodReference methodRef : vm.getMethods()) {
ClassReader cls = classSource.get(methodRef.getClassName());
if (cls == null) {
continue;
}
MethodReader method = cls.getMethod(methodRef.getDescriptor());
if (method == null) {
continue;
}
ProgramReader program = method.getProgram();
if (program == null) {
continue;
}
for (int i = 0; i < program.basicBlockCount(); ++i) {
program.basicBlockAt(i).readAllInstructions(reader);
}
}
return resources;
}
} }

View File

@ -52,11 +52,7 @@ import org.teavm.diagnostics.ProblemProvider;
import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderSource;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.PreOptimizingClassHolderSource; import org.teavm.model.PreOptimizingClassHolderSource;
import org.teavm.model.ProgramReader;
import org.teavm.parsing.ClasspathClassHolderSource; import org.teavm.parsing.ClasspathClassHolderSource;
import org.teavm.tooling.sources.SourceFileProvider; import org.teavm.tooling.sources.SourceFileProvider;
import org.teavm.tooling.sources.SourceFilesCopier; import org.teavm.tooling.sources.SourceFilesCopier;
@ -268,31 +264,7 @@ public class TeaVMTool {
return Collections.emptyList(); return Collections.emptyList();
} }
Set<String> resources = new HashSet<>(); return InstructionLocationReader.extractUsedResources(vm);
ClassReaderSource classSource = vm.getDependencyClassSource();
InstructionLocationReader reader = new InstructionLocationReader(resources);
for (MethodReference methodRef : vm.getMethods()) {
ClassReader cls = classSource.get(methodRef.getClassName());
if (cls == null) {
continue;
}
MethodReader method = cls.getMethod(methodRef.getDescriptor());
if (method == null) {
continue;
}
ProgramReader program = method.getProgram();
if (program == null) {
continue;
}
for (int i = 0; i < program.basicBlockCount(); ++i) {
program.basicBlockAt(i).readAllInstructions(reader);
}
}
return resources;
} }
public void addSourceFileProvider(SourceFileProvider sourceFileProvider) { public void addSourceFileProvider(SourceFileProvider sourceFileProvider) {

View File

@ -66,24 +66,6 @@
<artifactId>teavm-tooling</artifactId> <artifactId>teavm-tooling</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-classlib</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-metaprogramming-impl</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso-impl</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>

View File

@ -96,8 +96,12 @@ public class CodeServlet extends HttpServlet {
private final Set<CodeWsEndpoint> wsEndpoints = new LinkedHashSet<>(); private final Set<CodeWsEndpoint> wsEndpoints = new LinkedHashSet<>();
private final Object statusLock = new Object(); private final Object statusLock = new Object();
private volatile boolean cancelRequested;
private boolean compiling; private boolean compiling;
private double progress; private double progress;
private boolean waiting;
private Thread buildThread;
private List<DevServerListener> listeners = new ArrayList<>();
public CodeServlet(String mainClass, String[] classPath) { public CodeServlet(String mainClass, String[] classPath) {
this.mainClass = mainClass; this.mainClass = mainClass;
@ -160,6 +164,37 @@ public class CodeServlet extends HttpServlet {
} }
} }
public void addListener(DevServerListener listener) {
listeners.add(listener);
}
public void invalidateCache() {
synchronized (statusLock) {
if (compiling) {
return;
}
astCache.invalidate();
programCache.invalidate();
classSource.invalidate();
}
}
public void buildProject() {
synchronized (statusLock) {
if (waiting) {
buildThread.interrupt();
}
}
}
public void cancelBuild() {
synchronized (statusLock) {
if (compiling) {
cancelRequested = true;
}
}
}
@Override @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String path = req.getPathInfo(); String path = req.getPathInfo();
@ -206,15 +241,20 @@ public class CodeServlet extends HttpServlet {
public void destroy() { public void destroy() {
super.destroy(); super.destroy();
stopped = true; stopped = true;
synchronized (statusLock) {
if (waiting) {
buildThread.interrupt();
}
}
} }
@Override @Override
public void init() throws ServletException { public void init() throws ServletException {
super.init(); super.init();
Thread thread = new Thread(this::runTeaVM); Thread thread = new Thread(this::runTeaVM);
thread.setDaemon(true);
thread.setName("TeaVM compiler"); thread.setName("TeaVM compiler");
thread.start(); thread.start();
buildThread = thread;
} }
private boolean serveSourceFile(String fileName, HttpServletResponse resp) throws IOException { private boolean serveSourceFile(String fileName, HttpServletResponse resp) throws IOException {
@ -306,12 +346,20 @@ public class CodeServlet extends HttpServlet {
} }
try { try {
synchronized (statusLock) {
waiting = true;
}
watcher.waitForChange(750); watcher.waitForChange(750);
} catch (InterruptedException e) { synchronized (statusLock) {
log.info("Build thread interrupted"); waiting = false;
break;
} }
log.info("Changes detected. Recompiling."); log.info("Changes detected. Recompiling.");
} catch (InterruptedException e) {
if (stopped) {
break;
}
log.info("Build triggered by user");
}
List<String> staleClasses = getChangedClasses(watcher.grabChangedFiles()); List<String> staleClasses = getChangedClasses(watcher.grabChangedFiles());
if (staleClasses.size() > 15) { if (staleClasses.size() > 15) {
@ -325,6 +373,7 @@ public class CodeServlet extends HttpServlet {
classSource.evict(staleClasses); classSource.evict(staleClasses);
} }
log.info("Build process stopped");
} catch (Throwable e) { } catch (Throwable e) {
log.error("Compile server crashed", e); log.error("Compile server crashed", e);
} finally { } finally {
@ -359,6 +408,9 @@ public class CodeServlet extends HttpServlet {
} }
private void buildOnce() { private void buildOnce() {
fireBuildStarted();
reportProgress(0);
DebugInformationBuilder debugInformationBuilder = new DebugInformationBuilder(); DebugInformationBuilder debugInformationBuilder = new DebugInformationBuilder();
ClassLoader classLoader = initClassLoader(); ClassLoader classLoader = initClassLoader();
classSource.setUnderlyingSource(new PreOptimizingClassHolderSource( classSource.setUnderlyingSource(new PreOptimizingClassHolderSource(
@ -390,7 +442,6 @@ public class CodeServlet extends HttpServlet {
log.info("Starting build"); log.info("Starting build");
progressListener.last = 0; progressListener.last = 0;
progressListener.lastTime = System.currentTimeMillis(); progressListener.lastTime = System.currentTimeMillis();
reportProgress(0);
vm.build(buildTarget, fileName); vm.build(buildTarget, fileName);
addIndicator(); addIndicator();
generateDebug(debugInformationBuilder); generateDebug(debugInformationBuilder);
@ -450,6 +501,7 @@ public class CodeServlet extends HttpServlet {
private void postBuild(TeaVM vm, long startTime) { private void postBuild(TeaVM vm, long startTime) {
if (!vm.wasCancelled()) { if (!vm.wasCancelled()) {
log.info("Recompiled stale methods: " + programCache.getPendingItemsCount()); log.info("Recompiled stale methods: " + programCache.getPendingItemsCount());
fireBuildComplete(vm);
if (vm.getProblemProvider().getSevereProblems().isEmpty()) { if (vm.getProblemProvider().getSevereProblems().isEmpty()) {
log.info("Build complete successfully"); log.info("Build complete successfully");
saveNewResult(); saveNewResult();
@ -466,11 +518,13 @@ public class CodeServlet extends HttpServlet {
TeaVMProblemRenderer.describeProblems(vm, log); TeaVMProblemRenderer.describeProblems(vm, log);
} else { } else {
log.info("Build cancelled"); log.info("Build cancelled");
fireBuildCancelled();
} }
astCache.discard(); astCache.discard();
programCache.discard(); programCache.discard();
buildTarget.clear(); buildTarget.clear();
cancelRequested = false;
} }
private void printStats(TeaVM vm, long startTime) { private void printStats(TeaVM vm, long startTime) {
@ -553,6 +607,10 @@ public class CodeServlet extends HttpServlet {
for (CodeWsEndpoint endpoint : endpoints) { for (CodeWsEndpoint endpoint : endpoints) {
endpoint.progress(progress); endpoint.progress(progress);
} }
for (DevServerListener listener : listeners) {
listener.compilationProgress(progress);
}
} }
private void reportCompilationComplete(boolean success) { private void reportCompilationComplete(boolean success) {
@ -573,6 +631,25 @@ public class CodeServlet extends HttpServlet {
} }
} }
private void fireBuildStarted() {
for (DevServerListener listener : listeners) {
listener.compilationStarted();
}
}
private void fireBuildCancelled() {
for (DevServerListener listener : listeners) {
listener.compilationCancelled();
}
}
private void fireBuildComplete(TeaVM vm) {
CodeServletBuildResult result = new CodeServletBuildResult(vm, new ArrayList<>(buildTarget.getNames()));
for (DevServerListener listener : listeners) {
listener.compilationComplete(result);
}
}
private final ProgressListenerImpl progressListener = new ProgressListenerImpl(); private final ProgressListenerImpl progressListener = new ProgressListenerImpl();
class ProgressListenerImpl implements TeaVMProgressListener { class ProgressListenerImpl implements TeaVMProgressListener {
@ -622,6 +699,11 @@ public class CodeServlet extends HttpServlet {
} }
private TeaVMProgressFeedback getResult() { private TeaVMProgressFeedback getResult() {
if (cancelRequested) {
log.info("Trying to cancel compilation due to user request");
return TeaVMProgressFeedback.CANCEL;
}
if (stopped) { if (stopped) {
log.info("Trying to cancel compilation due to server stopping"); log.info("Trying to cancel compilation due to server stopping");
return TeaVMProgressFeedback.CANCEL; return TeaVMProgressFeedback.CANCEL;

View File

@ -0,0 +1,63 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.devserver;
import java.util.Collection;
import java.util.List;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.ProblemProvider;
import org.teavm.tooling.InstructionLocationReader;
import org.teavm.tooling.builder.BuildResult;
import org.teavm.vm.TeaVM;
class CodeServletBuildResult implements BuildResult {
private TeaVM vm;
private List<String> generatedFiles;
private Collection<String> usedResources;
public CodeServletBuildResult(TeaVM vm, List<String> generatedFiles) {
this.vm = vm;
this.generatedFiles = generatedFiles;
}
@Override
public CallGraph getCallGraph() {
return vm.getDependencyInfo().getCallGraph();
}
@Override
public ProblemProvider getProblems() {
return vm.getProblemProvider();
}
@Override
public Collection<String> getUsedResources() {
if (usedResources == null) {
usedResources = InstructionLocationReader.extractUsedResources(vm);
}
return usedResources;
}
@Override
public Collection<String> getClasses() {
return vm.getClasses();
}
@Override
public Collection<String> getGeneratedFiles() {
return generatedFiles;
}
}

View File

@ -30,7 +30,6 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
import org.teavm.tooling.ConsoleTeaVMToolLog;
import org.teavm.tooling.TeaVMToolLog; import org.teavm.tooling.TeaVMToolLog;
public class DevServer { public class DevServer {
@ -41,8 +40,9 @@ public class DevServer {
private List<String> sourcePath = new ArrayList<>(); private List<String> sourcePath = new ArrayList<>();
private boolean indicator; private boolean indicator;
private boolean reloadedAutomatically; private boolean reloadedAutomatically;
private boolean verbose;
private TeaVMToolLog log; private TeaVMToolLog log;
private CodeServlet servlet;
private List<DevServerListener> listeners = new ArrayList<>();
private Server server; private Server server;
private int port = 9090; private int port = 9090;
@ -85,16 +85,27 @@ public class DevServer {
this.reloadedAutomatically = reloadedAutomatically; this.reloadedAutomatically = reloadedAutomatically;
} }
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public List<String> getSourcePath() { public List<String> getSourcePath() {
return sourcePath; return sourcePath;
} }
public void invalidateCache() {
servlet.invalidateCache();
}
public void buildProject() {
servlet.buildProject();
}
public void cancelBuild() {
servlet.cancelBuild();
}
public void addListener(DevServerListener listener) {
listeners.add(listener);
}
public void start() { public void start() {
log = new ConsoleTeaVMToolLog(verbose);
server = new Server(); server = new Server();
ServerConnector connector = new ServerConnector(server); ServerConnector connector = new ServerConnector(server);
connector.setPort(port); connector.setPort(port);
@ -103,7 +114,7 @@ public class DevServer {
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/"); context.setContextPath("/");
server.setHandler(context); server.setHandler(context);
CodeServlet servlet = new CodeServlet(mainClass, classPath); servlet = new CodeServlet(mainClass, classPath);
servlet.setFileName(fileName); servlet.setFileName(fileName);
servlet.setPathToFile(pathToFile); servlet.setPathToFile(pathToFile);
servlet.setLog(log); servlet.setLog(log);
@ -111,6 +122,9 @@ public class DevServer {
servlet.setIndicator(indicator); servlet.setIndicator(indicator);
servlet.setAutomaticallyReloaded(reloadedAutomatically); servlet.setAutomaticallyReloaded(reloadedAutomatically);
servlet.setPort(port); servlet.setPort(port);
for (DevServerListener listener : listeners) {
servlet.addListener(listener);
}
context.addServlet(new ServletHolder(servlet), "/*"); context.addServlet(new ServletHolder(servlet), "/*");
try { try {
@ -129,6 +143,8 @@ public class DevServer {
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
server = null;
servlet = null;
} }
private class DevServerEndpointConfig implements ServerEndpointConfig { private class DevServerEndpointConfig implements ServerEndpointConfig {

View File

@ -0,0 +1,28 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.devserver;
import org.teavm.tooling.builder.BuildResult;
public interface DevServerListener {
void compilationStarted();
void compilationProgress(double progress);
void compilationComplete(BuildResult result);
void compilationCancelled();
}

View File

@ -44,23 +44,21 @@
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-tooling</artifactId> <artifactId>teavm-tooling</artifactId>
<version>${teavm.version}</version> <version>${teavm.version}</version>
<exclusions> </dependency>
<exclusion> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.teavm</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>teavm-devserver</artifactId>
</exclusion> <version>${teavm.version}</version>
</exclusions> </dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-classlib</artifactId>
<version>${teavm.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-chrome-rdp</artifactId> <artifactId>teavm-chrome-rdp</artifactId>
<version>${teavm.version}</version> <version>${teavm.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
</dependencies> </dependencies>
@ -125,6 +123,36 @@
</atrifactSet> </atrifactSet>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
<outputFile>dependencies/teavm.jar</outputFile> <outputFile>dependencies/teavm.jar</outputFile>
<relocations>
<relocation>
<pattern>com.google.gson</pattern>
<shadedPattern>org.teavm.shade.gson</shadedPattern>
</relocation>
<relocation>
<pattern>com.jcraft.jzlib</pattern>
<shadedPattern>org.teavm.shade.jzlib</shadedPattern>
</relocation>
<relocation>
<pattern>org.eclipse.jetty</pattern>
<shadedPattern>org.teavm.shade.jetty</shadedPattern>
</relocation>
<relocation>
<pattern>org.joda.time</pattern>
<shadedPattern>org.teavm.shade.jodatime</shadedPattern>
</relocation>
<relocation>
<pattern>org.objectweb.asm</pattern>
<shadedPattern>org.teavm.shade.jetty.asm</shadedPattern>
</relocation>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>org.teavm.shade.slf4j</shadedPattern>
</relocation>
<relocation>
<pattern>org.apache.commons</pattern>
<shadedPattern>org.teavm.apachecommons</shadedPattern>
</relocation>
</relocations>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>

View File

@ -24,13 +24,16 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.teavm.idea.devserver.DevServerRunner;
import org.teavm.tooling.daemon.BuildDaemon; import org.teavm.tooling.daemon.BuildDaemon;
public final class DaemonUtil { public final class DaemonUtil {
private static final Set<String> PLUGIN_FILES = new HashSet<>(Arrays.asList("teavm-jps-common.jar", private static final Set<String> PLUGIN_FILES = new HashSet<>(Arrays.asList("teavm-jps-common.jar",
"teavm-plugin.jar", "teavm.jar")); "teavm-plugin.jar", "teavm.jar"));
private static final String DAEMON_CLASS = BuildDaemon.class.getName().replace('.', '/') + ".class"; private static final String DAEMON_CLASS = BuildDaemon.class.getName().replace('.', '/') + ".class";
private static final String DEV_SERVER_CLASS = DevServerRunner.class.getName().replace('.', '/') + ".class";
private static final int DAEMON_CLASS_DEPTH; private static final int DAEMON_CLASS_DEPTH;
private static final int DEV_SERVER_CLASS_DEPTH;
static { static {
int depth = 0; int depth = 0;
@ -40,6 +43,14 @@ public final class DaemonUtil {
} }
} }
DAEMON_CLASS_DEPTH = depth; DAEMON_CLASS_DEPTH = depth;
depth = 0;
for (int i = 0; i < DEV_SERVER_CLASS.length(); ++i) {
if (DEV_SERVER_CLASS.charAt(i) == '/') {
depth++;
}
}
DEV_SERVER_CLASS_DEPTH = depth;
} }
private DaemonUtil() { private DaemonUtil() {
@ -64,6 +75,11 @@ public final class DaemonUtil {
file = file.getParentFile(); file = file.getParentFile();
} }
targetFiles.add(file.getAbsolutePath()); targetFiles.add(file.getAbsolutePath());
} else if (file.getPath().endsWith(DEV_SERVER_CLASS)) {
for (int i = 0; i <= DEV_SERVER_CLASS_DEPTH; ++i) {
file = file.getParentFile();
}
targetFiles.add(file.getAbsolutePath());
} else if (file.isDirectory()) { } else if (file.isDirectory()) {
for (File childFile : file.listFiles()) { for (File childFile : file.listFiles()) {
findInHierarchy(childFile, targetFiles, visited); findInHierarchy(childFile, targetFiles, visited);

View File

@ -0,0 +1,24 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea;
public interface DevServerRunnerListener {
void error(String text);
void info(String text);
void stopped(int code);
}

View File

@ -15,7 +15,6 @@
*/ */
package org.teavm.idea.debug; package org.teavm.idea.debug;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor; import com.intellij.execution.Executor;
import com.intellij.execution.configurations.ConfigurationFactory; import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.LocatableConfigurationBase; import com.intellij.execution.configurations.LocatableConfigurationBase;
@ -45,8 +44,7 @@ public class TeaVMDebugConfiguration extends LocatableConfigurationBase
@Nullable @Nullable
@Override @Override
public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
ExecutionException {
return new TeaVMRunState(environment, port); return new TeaVMRunState(environment, port);
} }

View File

@ -0,0 +1,27 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.Problem;
public class DevServerBuildResult implements Serializable {
public CallGraph callGraph;
public final List<Problem> problems = new ArrayList<>();
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
public class DevServerConfiguration {
public String javaHome;
public int maxHeap;
public String mainClass;
public String[] classPath;
public String[] sourcePath;
public boolean indicator;
public boolean autoReload;
public int port;
public String pathToFile;
public String fileName;
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
public class DevServerInfo {
public final int port;
public final DevServerManager server;
public final Process process;
DevServerInfo(int port, DevServerManager server, Process process) {
this.port = port;
this.server = server;
this.process = process;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface DevServerManager extends Remote {
String ID = "TeaVM-BuildServer";
void stop() throws RemoteException;
void invalidateCache() throws RemoteException;
void buildProject() throws RemoteException;
void cancelBuild() throws RemoteException;
void addListener(DevServerManagerListener listener) throws RemoteException;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface DevServerManagerListener extends Remote {
void compilationStarted() throws RemoteException;
void compilationProgress(double progress) throws RemoteException;
void compilationComplete(DevServerBuildResult result) throws RemoteException;
void compilationCancelled() throws RemoteException;
}

View File

@ -0,0 +1,308 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.rmi.AlreadyBoundException;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import org.teavm.devserver.DevServer;
import org.teavm.devserver.DevServerListener;
import org.teavm.idea.DevServerRunnerListener;
import org.teavm.tooling.ConsoleTeaVMToolLog;
import org.teavm.tooling.builder.BuildResult;
public class DevServerRunner extends UnicastRemoteObject implements DevServerManager {
private static final int MIN_PORT = 10000;
private static final int MAX_PORT = 1 << 16;
private static final String PORT_MESSAGE_PREFIX = "Build server port: ";
private static final String DEBUG_PORT_PROPERTY = "teavm.server.debug.port";
private int port;
private Registry registry;
private DevServer server;
private List<DevServerManagerListener> listeners = new ArrayList<>();
private DevServerRunner(DevServer server) throws RemoteException {
super();
Random random = new Random();
for (int i = 0; i < 20; ++i) {
port = random.nextInt(MAX_PORT - MIN_PORT) + MIN_PORT;
try {
registry = LocateRegistry.createRegistry(port);
} catch (RemoteException e) {
continue;
}
try {
registry.bind(ID, this);
} catch (RemoteException | AlreadyBoundException e) {
throw new IllegalStateException("Could not bind remote service", e);
}
this.server = server;
server.addListener(devServerListener);
return;
}
throw new IllegalStateException("Could not create RMI registry");
}
@Override
public void stop() {
server.stop();
}
@Override
public void invalidateCache() {
server.invalidateCache();
}
@Override
public void buildProject() {
server.buildProject();
}
@Override
public void cancelBuild() {
server.cancelBuild();
}
@Override
public void addListener(DevServerManagerListener listener) {
listeners.add(listener);
}
public static void main(String[] args) throws Exception {
DevServer server = new DevServer();
server.setLog(new ConsoleTeaVMToolLog(true));
server.setMainClass(args[0]);
List<String> classPath = new ArrayList<>();
for (int i = 1; i < args.length; ++i) {
switch (args[i]) {
case "-c":
classPath.add(args[++i]);
break;
case "-s":
server.getSourcePath().add(args[++i]);
break;
case "-i":
server.setIndicator(true);
break;
case "-a":
server.setReloadedAutomatically(true);
break;
case "-p":
server.setPort(Integer.parseInt(args[++i]));
break;
case "-d":
server.setPathToFile(args[++i]);
break;
case "-f":
server.setFileName(args[++i]);
break;
}
}
server.setClassPath(classPath.toArray(new String[0]));
DevServerRunner daemon = new DevServerRunner(server);
System.out.println(PORT_MESSAGE_PREFIX + daemon.port);
server.start();
try {
daemon.registry.unbind(ID);
UnicastRemoteObject.unexportObject(daemon, true);
} catch (NoSuchObjectException e) {
throw new IllegalStateException("Could not shutdown RMI registry", e);
}
}
public static DevServerInfo start(String[] classPathEntries, DevServerConfiguration options,
DevServerRunnerListener listener) throws IOException {
String javaCommand = options.javaHome + "/bin/java";
String classPath = String.join(File.pathSeparator, classPathEntries);
List<String> arguments = new ArrayList<>();
arguments.addAll(Arrays.asList(javaCommand, "-cp", classPath, "-Xmx" + options.maxHeap + "m"));
String debugPort = System.getProperty(DEBUG_PORT_PROPERTY);
if (debugPort != null) {
arguments.add("-agentlib:jdwp=transport=dt_socket,quiet=y,server=y,address=" + debugPort + ",suspend=y");
}
arguments.add(DevServerRunner.class.getName());
arguments.add(options.mainClass);
if (options.indicator) {
arguments.add("-i");
}
if (options.autoReload) {
arguments.add("-a");
}
arguments.add("-d");
arguments.add(options.pathToFile);
arguments.add("-f");
arguments.add(options.fileName);
arguments.add("-p");
arguments.add(Integer.toString(options.port));
for (String entry : options.classPath) {
arguments.add("-c");
arguments.add(entry);
}
for (String entry : options.sourcePath) {
arguments.add("-s");
arguments.add(entry);
}
ProcessBuilder builder = new ProcessBuilder(arguments.toArray(new String[0]));
Process process = builder.start();
BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream(),
StandardCharsets.UTF_8));
BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream(),
StandardCharsets.UTF_8));
String line = stdoutReader.readLine();
if (line == null || !line.startsWith(PORT_MESSAGE_PREFIX)) {
StringBuilder sb = new StringBuilder();
while (true) {
line = stderrReader.readLine();
if (line == null) {
break;
}
sb.append(line).append('\n');
}
stderrReader.close();
stdoutReader.close();
process.destroy();
throw new IllegalStateException("Could not start daemon. Stderr: " + sb);
}
int port = Integer.parseInt(line.substring(PORT_MESSAGE_PREFIX.length()));
daemonThread("TeaVM devserver stdout", new ProcessOutputWatcher(stdoutReader, listener::info)).start();
daemonThread("TeaVM devserver stderr", new ProcessOutputWatcher(stderrReader, listener::error)).start();
daemonThread("TeaVM devserver monitor", () -> {
int exitCode;
try {
exitCode = process.waitFor();
} catch (InterruptedException e) {
return;
}
listener.stopped(exitCode);
}).start();
DevServerManager service;
try {
Registry registry = LocateRegistry.getRegistry(port);
service = (DevServerManager) registry.lookup(ID);
} catch (RemoteException | NotBoundException e) {
throw new RuntimeException("Error connecting TeaVM process", e);
}
return new DevServerInfo(port, service, process);
}
private static Thread daemonThread(String name, Runnable runnable) {
Thread thread = new Thread(runnable);
thread.setName(name);
thread.setDaemon(true);
return thread;
}
static class ProcessOutputWatcher implements Runnable {
private BufferedReader reader;
private Consumer<String> consumer;
ProcessOutputWatcher(BufferedReader reader, Consumer<String> consumer) {
this.reader = reader;
this.consumer = consumer;
}
@Override
public void run() {
try {
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
consumer.accept(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
final DevServerListener devServerListener = new DevServerListener() {
@Override
public void compilationStarted() {
for (DevServerManagerListener listener : listeners) {
try {
listener.compilationStarted();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void compilationProgress(double v) {
for (DevServerManagerListener listener : listeners) {
try {
listener.compilationProgress(v);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void compilationComplete(BuildResult buildResult) {
DevServerBuildResult result = new DevServerBuildResult();
result.callGraph = buildResult.getCallGraph();
result.problems.addAll(buildResult.getProblems().getProblems());
for (DevServerManagerListener listener : listeners) {
try {
listener.compilationComplete(result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void compilationCancelled() {
for (DevServerManagerListener listener : listeners) {
try {
listener.compilationCancelled();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
};
}

View File

@ -0,0 +1,187 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.ModuleBasedConfiguration;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunConfigurationModule;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.facet.FacetManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.Property;
import com.intellij.util.xmlb.annotations.Tag;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.teavm.idea.TeaVMFacetType;
import org.teavm.idea.devserver.ui.TeaVMDevServerSettingsEditor;
public class TeaVMDevServerConfiguration
extends ModuleBasedConfiguration<RunConfigurationModule> {
private String mainClass = "";
private String jdkPath;
private int port = 9090;
private String pathToFile = "";
private String fileName = "classes.js";
private boolean indicator = true;
private boolean automaticallyReloaded;
private int maxHeap = 1024;
public TeaVMDevServerConfiguration(
@NotNull RunConfigurationModule configurationModule,
@NotNull ConfigurationFactory factory) {
super("TeaVM dev server", configurationModule, factory);
}
@Override
public Collection<Module> getValidModules() {
Module[] modules = ModuleManager.getInstance(getProject()).getModules();
List<Module> validModules = new ArrayList<>();
for (Module module : modules) {
FacetManager facetManager = FacetManager.getInstance(module);
if (facetManager.getFacetByType(TeaVMFacetType.TYPE_ID) != null) {
validModules.add(module);
}
}
return validModules;
}
@Override
public void readExternal(@NotNull Element element) throws InvalidDataException {
super.readExternal(element);
Element child = element.getChild("teavm");
if (child != null) {
XmlSerializer.deserializeInto(this, child);
}
}
@Override
public void writeExternal(@NotNull Element element) throws WriteExternalException {
super.writeExternal(element);
Element child = element.getChild("teavm");
if (child == null) {
child = new Element("teavm");
element.addContent(child);
}
XmlSerializer.serializeInto(this, child, (accessor, bean) ->
!accessor.getName().equals("isAllowRunningInParallel"));
}
@NotNull
@Override
public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
return new TeaVMDevServerSettingsEditor(getProject());
}
@Nullable
@Override
public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
return new TeaVMDevServerRunState(environment, this);
}
@Property
@Tag
public String getMainClass() {
return mainClass;
}
public void setMainClass(String mainClass) {
this.mainClass = mainClass;
}
@Property
@Tag
public String getJdkPath() {
return jdkPath;
}
public void setJdkPath(String jdkPath) {
this.jdkPath = jdkPath;
}
@Property
@Tag
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Property
@Tag
public String getPathToFile() {
return pathToFile;
}
public void setPathToFile(String pathToFile) {
this.pathToFile = pathToFile;
}
@Property
@Tag
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
@Property
@Tag
public boolean isIndicator() {
return indicator;
}
public void setIndicator(boolean indicator) {
this.indicator = indicator;
}
@Property
@Tag
public boolean isAutomaticallyReloaded() {
return automaticallyReloaded;
}
public void setAutomaticallyReloaded(boolean automaticallyReloaded) {
this.automaticallyReloaded = automaticallyReloaded;
}
@Property
@Tag
public int getMaxHeap() {
return maxHeap;
}
public void setMaxHeap(int maxHeap) {
this.maxHeap = maxHeap;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.ConfigurationTypeBase;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunConfigurationModule;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import org.jetbrains.annotations.NotNull;
public class TeaVMDevServerConfigurationType extends ConfigurationTypeBase {
public TeaVMDevServerConfigurationType() {
super("TeaVMDevServerConfiguration", "TeaVM development server", "TeaVM development server"
+ "agent", IconLoader.getIcon("/teavm-16.png"));
}
private final ConfigurationFactory factory = new ConfigurationFactory(this) {
@NotNull
@Override
public RunConfiguration createTemplateConfiguration(@NotNull Project project) {
return new TeaVMDevServerConfiguration(new RunConfigurationModule(project), this);
}
};
@Override
public ConfigurationFactory[] getConfigurationFactories() {
return new ConfigurationFactory[] { factory };
}
}

View File

@ -0,0 +1,162 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.SearchScopeProvider;
import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.util.JavaParametersUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.OrderEnumerator;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.GlobalSearchScope;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Objects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.teavm.idea.DaemonUtil;
import org.teavm.idea.DevServerRunnerListener;
public class TeaVMDevServerRunState implements RunProfileState {
private final TeaVMDevServerConfiguration configuration;
private final TextConsoleBuilder consoleBuilder;
public TeaVMDevServerRunState(@NotNull ExecutionEnvironment environment,
@NotNull TeaVMDevServerConfiguration configuration) {
this.configuration = configuration;
Project project = environment.getProject();
GlobalSearchScope searchScope = SearchScopeProvider.createSearchScope(project, environment.getRunProfile());
consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project, searchScope);
}
@Nullable
@Override
public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
DevServerConfiguration config = new DevServerConfiguration();
Module module = configuration.getConfigurationModule().getModule();
Sdk moduleSdk = JavaParametersUtil.createModuleJdk(module, true, configuration.getJdkPath());
config.javaHome = moduleSdk.getHomePath();
OrderEnumerator enumerator = OrderEnumerator.orderEntries(module).withoutSdk().recursively();
config.classPath = Arrays.stream(enumerator.getClassesRoots())
.map(this::path)
.filter(Objects::nonNull)
.toArray(String[]::new);
config.sourcePath = Arrays.stream(enumerator.getSourceRoots())
.map(this::path)
.filter(Objects::nonNull)
.toArray(String[]::new);
config.pathToFile = configuration.getPathToFile();
config.fileName = configuration.getFileName();
config.port = configuration.getPort();
config.indicator = configuration.isIndicator();
config.autoReload = configuration.isAutomaticallyReloaded();
config.mainClass = configuration.getMainClass();
config.maxHeap = configuration.getMaxHeap();
try {
ConsoleView console = consoleBuilder.getConsole();
ProcessHandlerImpl processHandler = new ProcessHandlerImpl(config, console);
console.attachToProcess(processHandler);
processHandler.start();
return new DefaultExecutionResult(console, processHandler);
} catch (IOException e) {
throw new ExecutionException(e);
}
}
private String path(VirtualFile file) {
while (file.getFileSystem() instanceof JarFileSystem) {
file = ((JarFileSystem) file.getFileSystem()).getLocalByEntry(file);
if (file == null) {
return null;
}
}
return file.getCanonicalPath();
}
class ProcessHandlerImpl extends ProcessHandler implements DevServerRunnerListener {
private DevServerConfiguration config;
private ConsoleView console;
private DevServerInfo info;
ProcessHandlerImpl(DevServerConfiguration config, ConsoleView console) {
this.config = config;
this.console = console;
}
void start() throws IOException {
info = DevServerRunner.start(DaemonUtil.detectClassPath().toArray(new String[0]), config, this);
}
@Override
protected void destroyProcessImpl() {
info.process.destroy();
}
@Override
protected void detachProcessImpl() {
try {
info.server.stop();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean detachIsDefault() {
return true;
}
@Nullable
@Override
public OutputStream getProcessInput() {
return null;
}
@Override
public void error(String text) {
console.print(text + System.lineSeparator(), ConsoleViewContentType.ERROR_OUTPUT);
}
@Override
public void info(String text) {
console.print(text + System.lineSeparator(), ConsoleViewContentType.NORMAL_OUTPUT);
}
@Override
public void stopped(int code) {
notifyProcessTerminated(code);
}
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.RunnerSettings;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.GenericProgramRunner;
import com.intellij.execution.ui.RunContentDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class TeaVMDevServerRunner extends GenericProgramRunner<RunnerSettings> {
@NotNull
@Override
public String getRunnerId() {
return "TeaVMDevServerRunner";
}
@Override
public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
return profile instanceof TeaVMDevServerConfiguration;
}
@Nullable
@Override
protected RunContentDescriptor doExecute(@NotNull RunProfileState state,
@NotNull ExecutionEnvironment environment) throws ExecutionException {
ExecutionResult executionResult = state.execute(environment.getExecutor(), environment.getRunner());
if (executionResult == null) {
return null;
}
return null;
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver.ui;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import javax.swing.JComponent;
import org.jetbrains.annotations.NotNull;
import org.teavm.idea.devserver.TeaVMDevServerConfiguration;
public class TeaVMDevServerSettingsEditor extends SettingsEditor<TeaVMDevServerConfiguration> {
private final Project project;
private TeaVMDevServerSettingsPanel panel;
public TeaVMDevServerSettingsEditor(Project project) {
this.project = project;
}
@Override
protected void resetEditorFrom(@NotNull TeaVMDevServerConfiguration s) {
if (panel == null) {
return;
}
panel.load(s);
}
@Override
protected void applyEditorTo(@NotNull TeaVMDevServerConfiguration s) {
if (panel == null) {
return;
}
panel.save(s);
}
@Override
protected void disposeEditor() {
if (panel != null) {
panel = null;
}
super.disposeEditor();
}
@NotNull
@Override
protected JComponent createEditor() {
if (panel == null) {
panel = new TeaVMDevServerSettingsPanel(project);
}
return panel;
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright 2018 Alexey Andreev.
*
* 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 org.teavm.idea.devserver.ui;
import com.intellij.application.options.ModuleDescriptionsComboBox;
import com.intellij.execution.configurations.ConfigurationUtil;
import com.intellij.execution.ui.ConfigurationModuleSelector;
import com.intellij.execution.ui.DefaultJreSelector;
import com.intellij.execution.ui.JrePathEditor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaCodeFragment;
import com.intellij.psi.PsiClass;
import com.intellij.psi.util.PsiMethodUtil;
import com.intellij.ui.EditorTextFieldWithBrowseButton;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.text.DecimalFormat;
import javax.swing.JCheckBox;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import org.teavm.idea.devserver.TeaVMDevServerConfiguration;
public class TeaVMDevServerSettingsPanel extends JPanel {
private final JrePathEditor jrePathEditor;
private final ModuleDescriptionsComboBox moduleField;
private final ConfigurationModuleSelector moduleSelector;
private EditorTextFieldWithBrowseButton mainClassField;
private JFormattedTextField portField;
private JTextField pathToFileField;
private JTextField fileNameField;
private JCheckBox indicatorField;
private JCheckBox autoReloadField;
private JFormattedTextField maxHeapField;
public TeaVMDevServerSettingsPanel(Project project) {
moduleField = new ModuleDescriptionsComboBox();
moduleSelector = new ConfigurationModuleSelector(project, moduleField);
JavaCodeFragment.VisibilityChecker visibilityChecker = (declaration, place) -> {
if (declaration instanceof PsiClass) {
PsiClass cls = (PsiClass) declaration;
if (ConfigurationUtil.MAIN_CLASS.value(cls) && PsiMethodUtil.findMainMethod(cls) != null
|| place.getParent() != null && moduleSelector.findClass(cls.getQualifiedName()) != null) {
return JavaCodeFragment.VisibilityChecker.Visibility.VISIBLE;
}
}
return JavaCodeFragment.VisibilityChecker.Visibility.NOT_VISIBLE;
};
mainClassField = new EditorTextFieldWithBrowseButton(project, true, visibilityChecker);
mainClassField.setButtonEnabled(true);
jrePathEditor = new JrePathEditor(DefaultJreSelector.fromSourceRootsDependencies(moduleField, mainClassField));
portField = new JFormattedTextField(new DecimalFormat("#0"));
fileNameField = new JTextField();
pathToFileField = new JTextField();
indicatorField = new JCheckBox("Display indicator on a web page:");
autoReloadField = new JCheckBox("Reload page automatically:");
maxHeapField = new JFormattedTextField(new DecimalFormat("#0"));
initLayout();
}
private void initLayout() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(10, 10, 10, 10));
GridBagConstraints labelConstraints = new GridBagConstraints();
labelConstraints.insets.right = 5;
labelConstraints.anchor = GridBagConstraints.LINE_START;
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1;
constraints.insets.top = 4;
constraints.insets.bottom = 4;
add(new JLabel("Main class:"), labelConstraints);
add(mainClassField, constraints);
add(new JLabel("Use classpath of module:"), labelConstraints);
add(moduleField, constraints);
add(jrePathEditor, constraints);
add(new JLabel("Port:"), labelConstraints);
add(portField, constraints);
add(new JLabel("File name:"), labelConstraints);
add(fileNameField, constraints);
add(new JLabel("Path to file:"), labelConstraints);
add(pathToFileField, constraints);
add(indicatorField, constraints);
add(autoReloadField, constraints);
add(new JLabel("Server heap limit:"), labelConstraints);
add(maxHeapField, constraints);
}
public void load(TeaVMDevServerConfiguration configuration) {
mainClassField.setText(configuration.getMainClass());
moduleSelector.reset(configuration);
jrePathEditor.setPathOrName(configuration.getJdkPath(), false);
fileNameField.setText(configuration.getFileName());
pathToFileField.setText(configuration.getPathToFile());
indicatorField.setSelected(configuration.isIndicator());
autoReloadField.setSelected(configuration.isAutomaticallyReloaded());
maxHeapField.setText(Integer.toString(configuration.getMaxHeap()));
portField.setText(Integer.toString(configuration.getPort()));
}
public void save(TeaVMDevServerConfiguration configuration) {
configuration.setMainClass(mainClassField.getText());
moduleSelector.applyTo(configuration);
configuration.setJdkPath(jrePathEditor.getJrePathOrName());
configuration.setFileName(fileNameField.getText());
configuration.setPathToFile(pathToFileField.getText());
configuration.setIndicator(indicatorField.isSelected());
configuration.setAutomaticallyReloaded(autoReloadField.isSelected());
configuration.setMaxHeap(Integer.parseInt(maxHeapField.getText()));
configuration.setPort(Integer.parseInt(portField.getText()));
}
}

View File

@ -110,6 +110,9 @@ public class TeaVMMavenImporter extends MavenImporter {
} }
TeaVMJpsConfiguration configuration = facet.getConfiguration().getState(); TeaVMJpsConfiguration configuration = facet.getConfiguration().getState();
if (justCreated) {
configuration.setSkipped(true);
}
for (Element child : source.getChildren()) { for (Element child : source.getChildren()) {
switch (child.getName()) { switch (child.getName()) {

View File

@ -17,7 +17,6 @@ package org.teavm.idea.ui;
import com.intellij.openapi.module.Module; import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import javax.swing.JComponent; import javax.swing.JComponent;
import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -60,7 +59,7 @@ public class TeaVMConfigurable implements Configurable {
} }
@Override @Override
public void apply() throws ConfigurationException { public void apply() {
panel.save(configuration); panel.save(configuration);
} }

View File

@ -17,7 +17,6 @@ package org.teavm.idea.ui;
import com.intellij.facet.ui.FacetEditorTab; import com.intellij.facet.ui.FacetEditorTab;
import com.intellij.openapi.module.Module; import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import javax.swing.JComponent; import javax.swing.JComponent;
import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -48,7 +47,7 @@ public class TeaVMFacetEditorTab extends FacetEditorTab {
} }
@Override @Override
public void apply() throws ConfigurationException { public void apply() {
configurable.apply(); configurable.apply();
} }

View File

@ -36,6 +36,9 @@
<configurationType implementation="org.teavm.idea.debug.TeaVMDebugConfigurationType"/> <configurationType implementation="org.teavm.idea.debug.TeaVMDebugConfigurationType"/>
<programRunner implementation="org.teavm.idea.debug.TeaVMDebugRunner"/> <programRunner implementation="org.teavm.idea.debug.TeaVMDebugRunner"/>
<configurationType implementation="org.teavm.idea.devserver.TeaVMDevServerConfigurationType"/>
<programRunner implementation="org.teavm.idea.devserver.TeaVMDevServerRunner"/>
<applicationConfigurable instance="org.teavm.idea.ui.TeaVMSettingsEditorTab" <applicationConfigurable instance="org.teavm.idea.ui.TeaVMSettingsEditorTab"
id="project.teavm.settings" id="project.teavm.settings"
displayName="TeaVM compiler" displayName="TeaVM compiler"