mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-04 22:24:11 -08:00
gradle: implement dev server task
This commit is contained in:
parent
7341fb38a6
commit
bbd02b0067
|
@ -3,7 +3,6 @@
|
||||||
<component name="CheckStyle-IDEA" serialisationVersion="2">
|
<component name="CheckStyle-IDEA" serialisationVersion="2">
|
||||||
<checkstyleVersion>8.41.1</checkstyleVersion>
|
<checkstyleVersion>8.41.1</checkstyleVersion>
|
||||||
<scanScope>JavaOnlyWithTests</scanScope>
|
<scanScope>JavaOnlyWithTests</scanScope>
|
||||||
<option name="thirdPartyClasspath" />
|
|
||||||
<option name="activeLocationIds">
|
<option name="activeLocationIds">
|
||||||
<option value="c65b2ab0-cb40-4423-befb-a37d514deee5" />
|
<option value="c65b2ab0-cb40-4423-befb-a37d514deee5" />
|
||||||
</option>
|
</option>
|
||||||
|
|
|
@ -16,5 +16,4 @@
|
||||||
package org.teavm.common.json;
|
package org.teavm.common.json;
|
||||||
|
|
||||||
public abstract class JsonNumericValue extends JsonValue {
|
public abstract class JsonNumericValue extends JsonValue {
|
||||||
public abstract double asNumber();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class JsonParser {
|
public class JsonParser {
|
||||||
private JsonConsumer consumer;
|
private JsonConsumer consumer;
|
||||||
|
@ -32,6 +33,8 @@ public class JsonParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void parse(Reader reader) throws IOException {
|
public void parse(Reader reader) throws IOException {
|
||||||
|
lineNumber = 0;
|
||||||
|
columnNumber = 0;
|
||||||
lastChar = reader.read();
|
lastChar = reader.read();
|
||||||
skipWhitespaces(reader);
|
skipWhitespaces(reader);
|
||||||
if (lastChar == -1) {
|
if (lastChar == -1) {
|
||||||
|
@ -412,4 +415,8 @@ public class JsonParser {
|
||||||
private static boolean isDigit(int c) {
|
private static boolean isDigit(int c) {
|
||||||
return c >= '0' && c <= '9';
|
return c >= '0' && c <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JsonParser ofValue(Consumer<JsonValue> consumer) {
|
||||||
|
return new JsonParser(new JsonVisitingConsumer(JsonValueParserVisitor.create(consumer)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,4 +27,8 @@ public abstract class JsonValue {
|
||||||
public long asIntNumber() {
|
public long asIntNumber() {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double asNumber() {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ package org.teavm.common.json;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public abstract class JsonValueParserVisitor extends JsonAllErrorVisitor {
|
public abstract class JsonValueParserVisitor extends JsonAllErrorVisitor {
|
||||||
|
private JsonValue deferred;
|
||||||
|
|
||||||
public abstract void consume(JsonValue value);
|
public abstract void consume(JsonValue value);
|
||||||
|
|
||||||
public static JsonValueParserVisitor create(Consumer<JsonValue> consumer) {
|
public static JsonValueParserVisitor create(Consumer<JsonValue> consumer) {
|
||||||
|
@ -32,7 +34,7 @@ public abstract class JsonValueParserVisitor extends JsonAllErrorVisitor {
|
||||||
@Override
|
@Override
|
||||||
public JsonVisitor object(JsonErrorReporter reporter) {
|
public JsonVisitor object(JsonErrorReporter reporter) {
|
||||||
var jsonObject = new JsonObjectValue();
|
var jsonObject = new JsonObjectValue();
|
||||||
consume(jsonObject);
|
deferred = jsonObject;
|
||||||
return new JsonAllErrorVisitor() {
|
return new JsonAllErrorVisitor() {
|
||||||
@Override
|
@Override
|
||||||
public JsonVisitor property(JsonErrorReporter reporter, String name) {
|
public JsonVisitor property(JsonErrorReporter reporter, String name) {
|
||||||
|
@ -47,12 +49,17 @@ public abstract class JsonValueParserVisitor extends JsonAllErrorVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonVisitor array(JsonErrorReporter reporter) {
|
public void end(JsonErrorReporter reporter) {
|
||||||
var jsonArray = new JsonArrayValue();
|
super.end(reporter);
|
||||||
consume(jsonArray);
|
var value = deferred;
|
||||||
return new JsonAllErrorVisitor() {
|
deferred = null;
|
||||||
|
consume(value);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonVisitor array(JsonErrorReporter reporter) {
|
public JsonVisitor array(JsonErrorReporter reporter) {
|
||||||
|
var jsonArray = new JsonArrayValue();
|
||||||
|
deferred = jsonArray;
|
||||||
return new JsonValueParserVisitor() {
|
return new JsonValueParserVisitor() {
|
||||||
@Override
|
@Override
|
||||||
public void consume(JsonValue value) {
|
public void consume(JsonValue value) {
|
||||||
|
@ -60,8 +67,6 @@ public abstract class JsonValueParserVisitor extends JsonAllErrorVisitor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||||
|
|
|
@ -22,9 +22,17 @@ plugins {
|
||||||
id("org.teavm")
|
id("org.teavm")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
create("teavmCli")
|
||||||
|
create("teavmClasslib")
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
teavm(teavm.libs.jsoApis)
|
teavm(teavm.libs.jsoApis)
|
||||||
compileOnly("jakarta.servlet:jakarta.servlet-api:6.0.0")
|
compileOnly("jakarta.servlet:jakarta.servlet-api:6.0.0")
|
||||||
|
|
||||||
|
"teavmCli"("org.teavm:teavm-cli:0.10.0-SNAPSHOT")
|
||||||
|
"teavmClasslib"("org.teavm:teavm-classlib:0.10.0-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
|
||||||
teavm.js {
|
teavm.js {
|
||||||
|
@ -33,3 +41,14 @@ teavm.js {
|
||||||
sourceMap = true
|
sourceMap = true
|
||||||
sourceFilePolicy = SourceFilePolicy.LINK_LOCAL_FILES
|
sourceFilePolicy = SourceFilePolicy.LINK_LOCAL_FILES
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register<JavaExec>("runCli") {
|
||||||
|
classpath(configurations["teavmCli"])
|
||||||
|
mainClass = "org.teavm.cli.devserver.TeaVMDevServerRunner"
|
||||||
|
args = listOf("--json-interface", "--no-watch", "-p",
|
||||||
|
layout.buildDirectory.dir("classes/java/teavm").get().asFile.absolutePath,
|
||||||
|
) + configurations["teavmClasslib"].flatMap { listOf("-p", it.absolutePath) } + listOf(
|
||||||
|
"--", "org.teavm.samples.hello.Client"
|
||||||
|
)
|
||||||
|
println(args)
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ public final class PiCalculator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
System.out.println("hello1");
|
||||||
var start = System.currentTimeMillis();
|
var start = System.currentTimeMillis();
|
||||||
int n = Integer.parseInt(args[0]);
|
int n = Integer.parseInt(args[0]);
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
|
|
@ -26,7 +26,6 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -530,7 +529,7 @@ public class IncrementalCBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireBuildComplete(TeaVM vm) {
|
private void fireBuildComplete(TeaVM vm) {
|
||||||
SimpleBuildResult result = new SimpleBuildResult(vm, Collections.emptyList());
|
SimpleBuildResult result = new SimpleBuildResult(vm);
|
||||||
for (BuilderListener listener : listeners) {
|
for (BuilderListener listener : listeners) {
|
||||||
listener.compilationComplete(result);
|
listener.compilationComplete(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ dependencies {
|
||||||
implementation(project(":tools:devserver"))
|
implementation(project(":tools:devserver"))
|
||||||
implementation(project(":tools:c-incremental"))
|
implementation(project(":tools:c-incremental"))
|
||||||
implementation(libs.commons.cli)
|
implementation(libs.commons.cli)
|
||||||
|
implementation(libs.jetty.server)
|
||||||
|
|
||||||
runtimeOnly(project(":classlib"))
|
runtimeOnly(project(":classlib"))
|
||||||
runtimeOnly(project(":metaprogramming:impl"))
|
runtimeOnly(project(":metaprogramming:impl"))
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.cli.devserver;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.teavm.common.json.JsonValue;
|
||||||
|
import org.teavm.devserver.DevServer;
|
||||||
|
|
||||||
|
public class JsonCommandReader implements Consumer<JsonValue> {
|
||||||
|
private DevServer devServer;
|
||||||
|
|
||||||
|
public JsonCommandReader(DevServer devServer) {
|
||||||
|
this.devServer = devServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(JsonValue jsonValue) {
|
||||||
|
var obj = jsonValue.asObject();
|
||||||
|
var type = obj.get("type").asString();
|
||||||
|
switch (type) {
|
||||||
|
case "build":
|
||||||
|
devServer.buildProject();
|
||||||
|
break;
|
||||||
|
case "cancel":
|
||||||
|
devServer.cancelBuild();
|
||||||
|
break;
|
||||||
|
case "stop":
|
||||||
|
System.exit(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.cli.devserver;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.teavm.common.JsonUtil;
|
||||||
|
import org.teavm.devserver.DevServerListener;
|
||||||
|
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
||||||
|
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||||
|
import org.teavm.tooling.TeaVMToolLog;
|
||||||
|
import org.teavm.tooling.builder.BuildResult;
|
||||||
|
|
||||||
|
public class JsonCommandWriter implements TeaVMToolLog, DevServerListener, Logger {
|
||||||
|
private PrintWriter writer = new PrintWriter(System.out, false, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String text) {
|
||||||
|
writeMessage("info", text, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String text) {
|
||||||
|
writeMessage("debug", text, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warning(String text) {
|
||||||
|
writeMessage("warning", text, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String text) {
|
||||||
|
writeMessage("error", text, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String text, Throwable e) {
|
||||||
|
writeMessage("info", text, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String text, Throwable e) {
|
||||||
|
writeMessage("debug", text, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warning(String text, Throwable e) {
|
||||||
|
writeMessage("warning", text, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String text, Throwable e) {
|
||||||
|
writeMessage("error", text, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "dev-server";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(String s, Object... objects) {
|
||||||
|
writeMessage("warning", format(s, objects), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(Throwable throwable) {
|
||||||
|
writeMessage("warning", "", throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(String s, Throwable throwable) {
|
||||||
|
writeMessage("warning", s, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String s, Object... objects) {
|
||||||
|
writeMessage("info", format(s, objects), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(Throwable throwable) {
|
||||||
|
writeMessage("info", "", throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDebugEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDebugEnabled(boolean b) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String s, Object... objects) {
|
||||||
|
writeMessage("debug", format(s, objects), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String s, long l) {
|
||||||
|
writeMessage("debug", format(s, new Object[] { l }), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(Throwable throwable) {
|
||||||
|
writeMessage("debug", "", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Logger getLogger(String s) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ignore(Throwable throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private String format(String message, Object[] args) {
|
||||||
|
var index = 0;
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
for (var i = 0; i < args.length; ++i) {
|
||||||
|
var next = message.indexOf("{}", index);
|
||||||
|
if (next < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.append(message, index, next);
|
||||||
|
sb.append(args[i]);
|
||||||
|
index = next + 2;
|
||||||
|
}
|
||||||
|
sb.append(message, index, message.length());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void writeMessage(String level, String message, Throwable throwable) {
|
||||||
|
try {
|
||||||
|
writer.append("{\"type\":\"log\",\"level\":\"").append(level).append("\",\"message\":\"");
|
||||||
|
JsonUtil.writeEscapedString(writer, message);
|
||||||
|
writer.append("\"");
|
||||||
|
if (throwable != null) {
|
||||||
|
writer.append(",\"throwable\":\"");
|
||||||
|
var throwableBuffer = new StringWriter();
|
||||||
|
var throwableWriter = new PrintWriter(throwableBuffer);
|
||||||
|
throwable.printStackTrace(throwableWriter);
|
||||||
|
JsonUtil.writeEscapedString(writer, throwableBuffer.toString());
|
||||||
|
writer.append("\"");
|
||||||
|
}
|
||||||
|
writer.append("}");
|
||||||
|
writer.println();
|
||||||
|
writer.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void compilationStarted() {
|
||||||
|
writer.append("{\"type\":\"compilation-started\"}");
|
||||||
|
writer.println();
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void compilationProgress(double progress) {
|
||||||
|
writer.append("{\"type\":\"compilation-progress\",\"progress\":").append(String.valueOf(progress))
|
||||||
|
.append("}");
|
||||||
|
writer.println();
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void compilationComplete(BuildResult result) {
|
||||||
|
var consumer = new DefaultProblemTextConsumer();
|
||||||
|
try {
|
||||||
|
writer.append("{\"type\":\"compilation-complete\"");
|
||||||
|
if (result != null && !result.getProblems().getProblems().isEmpty()) {
|
||||||
|
writer.append(",\"problems\":[");
|
||||||
|
for (var i = 0; i < result.getProblems().getProblems().size(); ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
writer.append(",");
|
||||||
|
}
|
||||||
|
var problem = result.getProblems().getProblems().get(i);
|
||||||
|
writer.append("{\"severity\":");
|
||||||
|
switch (problem.getSeverity()) {
|
||||||
|
case ERROR:
|
||||||
|
writer.append("\"error\"");
|
||||||
|
break;
|
||||||
|
case WARNING:
|
||||||
|
writer.append("\"warning\"");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
writer.append(",\"location\":\"");
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
TeaVMProblemRenderer.renderCallStack(result.getCallGraph(), problem.getLocation(), sb);
|
||||||
|
JsonUtil.writeEscapedString(writer, sb.toString());
|
||||||
|
writer.append("\",\"message\":\"");
|
||||||
|
problem.render(consumer);
|
||||||
|
JsonUtil.writeEscapedString(writer, consumer.getText());
|
||||||
|
writer.append("\"}");
|
||||||
|
}
|
||||||
|
writer.append("]");
|
||||||
|
}
|
||||||
|
writer.append("}");
|
||||||
|
writer.println();
|
||||||
|
writer.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void compilationCancelled() {
|
||||||
|
writer.append("{\"type\":\"compilation-cancelled\"}");
|
||||||
|
writer.println();
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2018 Alexey Andreev.
|
* Copyright 2024 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,9 +13,15 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.cli;
|
package org.teavm.cli.devserver;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.CommandLineParser;
|
import org.apache.commons.cli.CommandLineParser;
|
||||||
import org.apache.commons.cli.DefaultParser;
|
import org.apache.commons.cli.DefaultParser;
|
||||||
|
@ -23,6 +29,8 @@ import org.apache.commons.cli.HelpFormatter;
|
||||||
import org.apache.commons.cli.Option;
|
import org.apache.commons.cli.Option;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.teavm.common.json.JsonParser;
|
||||||
import org.teavm.devserver.DevServer;
|
import org.teavm.devserver.DevServer;
|
||||||
import org.teavm.tooling.ConsoleTeaVMToolLog;
|
import org.teavm.tooling.ConsoleTeaVMToolLog;
|
||||||
|
|
||||||
|
@ -30,6 +38,7 @@ public final class TeaVMDevServerRunner {
|
||||||
private static Options options = new Options();
|
private static Options options = new Options();
|
||||||
private DevServer devServer;
|
private DevServer devServer;
|
||||||
private CommandLine commandLine;
|
private CommandLine commandLine;
|
||||||
|
private JsonCommandWriter jsonWriter;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
setupOptions();
|
setupOptions();
|
||||||
|
@ -56,10 +65,21 @@ public final class TeaVMDevServerRunner {
|
||||||
.build());
|
.build());
|
||||||
options.addOption(Option.builder("s")
|
options.addOption(Option.builder("s")
|
||||||
.argName("sourcepath")
|
.argName("sourcepath")
|
||||||
.hasArg()
|
.hasArgs()
|
||||||
.desc("source path (either directory or jar file which contains source code)")
|
.desc("source path (either directory or jar file which contains source code)")
|
||||||
.longOpt("sourcepath")
|
.longOpt("sourcepath")
|
||||||
.build());
|
.build());
|
||||||
|
options.addOption(Option.builder()
|
||||||
|
.argName("classnames")
|
||||||
|
.hasArgs()
|
||||||
|
.desc("list of classes that should be preserved during the build (e.g. to use with reflection)")
|
||||||
|
.longOpt("preserved-classes")
|
||||||
|
.build());
|
||||||
|
options.addOption(Option.builder()
|
||||||
|
.valueSeparator()
|
||||||
|
.hasArgs()
|
||||||
|
.longOpt("property")
|
||||||
|
.build());
|
||||||
options.addOption(Option.builder()
|
options.addOption(Option.builder()
|
||||||
.argName("number")
|
.argName("number")
|
||||||
.hasArg()
|
.hasArg()
|
||||||
|
@ -94,6 +114,14 @@ public final class TeaVMDevServerRunner {
|
||||||
.desc("delegate requests from path")
|
.desc("delegate requests from path")
|
||||||
.longOpt("proxy-path")
|
.longOpt("proxy-path")
|
||||||
.build());
|
.build());
|
||||||
|
options.addOption(Option.builder()
|
||||||
|
.desc("don't watch file system changes")
|
||||||
|
.longOpt("no-watch")
|
||||||
|
.build());
|
||||||
|
options.addOption(Option.builder()
|
||||||
|
.desc("JSON interface over stdout")
|
||||||
|
.longOpt("json-interface")
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private TeaVMDevServerRunner(CommandLine commandLine) {
|
private TeaVMDevServerRunner(CommandLine commandLine) {
|
||||||
|
@ -117,7 +145,16 @@ public final class TeaVMDevServerRunner {
|
||||||
|
|
||||||
TeaVMDevServerRunner runner = new TeaVMDevServerRunner(commandLine);
|
TeaVMDevServerRunner runner = new TeaVMDevServerRunner(commandLine);
|
||||||
runner.parseArguments();
|
runner.parseArguments();
|
||||||
runner.runAll();
|
runner.devServer.start();
|
||||||
|
if (runner.jsonWriter != null) {
|
||||||
|
runner.readStdinCommands();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
runner.devServer.awaitServer();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseArguments() {
|
private void parseArguments() {
|
||||||
|
@ -128,7 +165,6 @@ public final class TeaVMDevServerRunner {
|
||||||
devServer.setIndicator(commandLine.hasOption("indicator"));
|
devServer.setIndicator(commandLine.hasOption("indicator"));
|
||||||
devServer.setDeobfuscateStack(commandLine.hasOption("deobfuscate-stack"));
|
devServer.setDeobfuscateStack(commandLine.hasOption("deobfuscate-stack"));
|
||||||
devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload"));
|
devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload"));
|
||||||
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")));
|
||||||
|
@ -138,12 +174,29 @@ public final class TeaVMDevServerRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var properties = commandLine.getOptionProperties("property");
|
||||||
|
for (var property : properties.stringPropertyNames()) {
|
||||||
|
devServer.getProperties().put(property, properties.getProperty(property));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandLine.hasOption("preserved-classes")) {
|
||||||
|
devServer.getPreservedClasses().addAll(List.of(commandLine.getOptionValues("preserved-classes")));
|
||||||
|
}
|
||||||
|
|
||||||
if (commandLine.hasOption("proxy-url")) {
|
if (commandLine.hasOption("proxy-url")) {
|
||||||
devServer.setProxyUrl(commandLine.getOptionValue("proxy-url"));
|
devServer.setProxyUrl(commandLine.getOptionValue("proxy-url"));
|
||||||
}
|
}
|
||||||
if (commandLine.hasOption("proxy-path")) {
|
if (commandLine.hasOption("proxy-path")) {
|
||||||
devServer.setProxyPath(commandLine.getOptionValue("proxy-path"));
|
devServer.setProxyPath(commandLine.getOptionValue("proxy-path"));
|
||||||
}
|
}
|
||||||
|
if (commandLine.hasOption("no-watch")) {
|
||||||
|
devServer.setFileSystemWatched(false);
|
||||||
|
}
|
||||||
|
if (commandLine.hasOption("json-interface")) {
|
||||||
|
setupJsonInterface(devServer);
|
||||||
|
} else {
|
||||||
|
devServer.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v')));
|
||||||
|
}
|
||||||
|
|
||||||
String[] args = commandLine.getArgs();
|
String[] args = commandLine.getArgs();
|
||||||
if (args.length != 1) {
|
if (args.length != 1) {
|
||||||
|
@ -175,8 +228,29 @@ public final class TeaVMDevServerRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runAll() {
|
private void setupJsonInterface(DevServer devServer) {
|
||||||
devServer.start();
|
jsonWriter = new JsonCommandWriter();
|
||||||
|
devServer.setLog(jsonWriter);
|
||||||
|
devServer.addListener(jsonWriter);
|
||||||
|
devServer.setCompileOnStartup(false);
|
||||||
|
devServer.setLogBuildErrors(false);
|
||||||
|
Log.setLog(jsonWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readStdinCommands() {
|
||||||
|
var commandReader = new JsonCommandReader(devServer);
|
||||||
|
var parser = JsonParser.ofValue(commandReader);
|
||||||
|
try (var reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8))) {
|
||||||
|
while (true) {
|
||||||
|
var command = reader.readLine();
|
||||||
|
if (command == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parser.parse(new StringReader(command));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printUsage() {
|
private static void printUsage() {
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.tooling.builder;
|
package org.teavm.tooling.builder;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
import org.teavm.diagnostics.ProblemProvider;
|
import org.teavm.diagnostics.ProblemProvider;
|
||||||
|
|
||||||
|
@ -23,10 +22,4 @@ public interface BuildResult {
|
||||||
CallGraph getCallGraph();
|
CallGraph getCallGraph();
|
||||||
|
|
||||||
ProblemProvider getProblems();
|
ProblemProvider getProblems();
|
||||||
|
|
||||||
Collection<String> getUsedResources();
|
|
||||||
|
|
||||||
Collection<String> getClasses();
|
|
||||||
|
|
||||||
Collection<String> getGeneratedFiles();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,8 @@ import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.teavm.backend.javascript.JSModuleType;
|
import org.teavm.backend.javascript.JSModuleType;
|
||||||
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
|
@ -287,12 +285,8 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||||
throw new BuildException(e);
|
throw new BuildException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
var generatedFiles = tool.getGeneratedFiles().stream()
|
|
||||||
.map(File::getAbsolutePath)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
return new InProcessBuildResult(tool.getDependencyInfo().getCallGraph(),
|
return new InProcessBuildResult(tool.getDependencyInfo().getCallGraph(),
|
||||||
tool.getProblemProvider(), tool.getClasses(), tool.getUsedResources(), generatedFiles);
|
tool.getProblemProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
private URLClassLoader buildClassLoader() {
|
private URLClassLoader buildClassLoader() {
|
||||||
|
@ -310,17 +304,10 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||||
static class InProcessBuildResult implements BuildResult {
|
static class InProcessBuildResult implements BuildResult {
|
||||||
private CallGraph callGraph;
|
private CallGraph callGraph;
|
||||||
private ProblemProvider problemProvider;
|
private ProblemProvider problemProvider;
|
||||||
private Collection<String> classes;
|
|
||||||
private Collection<String> usedResources;
|
|
||||||
private Collection<String> generatedFiles;
|
|
||||||
|
|
||||||
InProcessBuildResult(CallGraph callGraph, ProblemProvider problemProvider,
|
InProcessBuildResult(CallGraph callGraph, ProblemProvider problemProvider) {
|
||||||
Collection<String> classes, Collection<String> usedResources, Collection<String> generatedFiles) {
|
|
||||||
this.callGraph = callGraph;
|
this.callGraph = callGraph;
|
||||||
this.problemProvider = problemProvider;
|
this.problemProvider = problemProvider;
|
||||||
this.classes = classes;
|
|
||||||
this.usedResources = usedResources;
|
|
||||||
this.generatedFiles = generatedFiles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -332,20 +319,5 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||||
public ProblemProvider getProblems() {
|
public ProblemProvider getProblems() {
|
||||||
return problemProvider;
|
return problemProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getClasses() {
|
|
||||||
return classes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getUsedResources() {
|
|
||||||
return usedResources;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getGeneratedFiles() {
|
|
||||||
return generatedFiles;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package org.teavm.tooling.builder;
|
||||||
|
|
||||||
import java.rmi.RemoteException;
|
import java.rmi.RemoteException;
|
||||||
import java.rmi.server.UnicastRemoteObject;
|
import java.rmi.server.UnicastRemoteObject;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import org.teavm.backend.javascript.JSModuleType;
|
import org.teavm.backend.javascript.JSModuleType;
|
||||||
|
@ -246,21 +245,6 @@ public class RemoteBuildStrategy implements BuildStrategy {
|
||||||
public ProblemProvider getProblems() {
|
public ProblemProvider getProblems() {
|
||||||
return problems;
|
return problems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getUsedResources() {
|
|
||||||
return response.usedResources;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getClasses() {
|
|
||||||
return response.classes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getGeneratedFiles() {
|
|
||||||
return response.generatedFiles;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,21 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.tooling.builder;
|
package org.teavm.tooling.builder;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
import org.teavm.diagnostics.ProblemProvider;
|
import org.teavm.diagnostics.ProblemProvider;
|
||||||
import org.teavm.tooling.InstructionLocationReader;
|
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
|
|
||||||
public class SimpleBuildResult implements BuildResult {
|
public class SimpleBuildResult implements BuildResult {
|
||||||
private TeaVM vm;
|
private TeaVM vm;
|
||||||
private List<String> generatedFiles;
|
|
||||||
private Collection<String> usedResources;
|
|
||||||
|
|
||||||
public SimpleBuildResult(TeaVM vm, List<String> generatedFiles) {
|
public SimpleBuildResult(TeaVM vm) {
|
||||||
this.vm = vm;
|
this.vm = vm;
|
||||||
this.generatedFiles = generatedFiles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -41,22 +35,4 @@ public class SimpleBuildResult implements BuildResult {
|
||||||
public ProblemProvider getProblems() {
|
public ProblemProvider getProblems() {
|
||||||
return vm.getProblemProvider();
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,11 +186,6 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
|
||||||
response.callGraph = tool.getDependencyInfo().getCallGraph();
|
response.callGraph = tool.getDependencyInfo().getCallGraph();
|
||||||
response.problems.addAll(tool.getProblemProvider().getProblems());
|
response.problems.addAll(tool.getProblemProvider().getProblems());
|
||||||
response.severeProblems.addAll(tool.getProblemProvider().getSevereProblems());
|
response.severeProblems.addAll(tool.getProblemProvider().getSevereProblems());
|
||||||
response.classes.addAll(tool.getClasses());
|
|
||||||
response.usedResources.addAll(tool.getUsedResources());
|
|
||||||
response.generatedFiles.addAll(tool.getGeneratedFiles().stream()
|
|
||||||
.map(File::getAbsolutePath)
|
|
||||||
.collect(Collectors.toSet()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|
|
@ -17,9 +17,7 @@ package org.teavm.tooling.daemon;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
import org.teavm.diagnostics.Problem;
|
import org.teavm.diagnostics.Problem;
|
||||||
|
|
||||||
|
@ -27,8 +25,5 @@ public class RemoteBuildResponse implements Serializable {
|
||||||
public CallGraph callGraph;
|
public CallGraph callGraph;
|
||||||
public final List<Problem> problems = new ArrayList<>();
|
public final List<Problem> problems = new ArrayList<>();
|
||||||
public final List<Problem> severeProblems = new ArrayList<>();
|
public final List<Problem> severeProblems = new ArrayList<>();
|
||||||
public final Set<String> usedResources = new HashSet<>();
|
|
||||||
public final Set<String> classes = new HashSet<>();
|
|
||||||
public final Set<String> generatedFiles = new HashSet<>();
|
|
||||||
public Throwable exception;
|
public Throwable exception;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,16 +98,22 @@ public class FileSystemWatcher {
|
||||||
return !changedFiles.isEmpty() || pollNow();
|
return !changedFiles.isEmpty() || pollNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void pollChanges() throws IOException {
|
||||||
|
while (pollNow()) {
|
||||||
|
// continue polling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void waitForChange(int timeout) throws InterruptedException, IOException {
|
public void waitForChange(int timeout) throws InterruptedException, IOException {
|
||||||
if (!hasChanges()) {
|
if (!hasChanges()) {
|
||||||
take();
|
take();
|
||||||
}
|
}
|
||||||
|
if (timeout > 0) {
|
||||||
while (poll(timeout)) {
|
while (poll(timeout)) {
|
||||||
// continue polling
|
// continue polling
|
||||||
}
|
}
|
||||||
while (pollNow()) {
|
|
||||||
// continue polling
|
|
||||||
}
|
}
|
||||||
|
pollChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<File> grabChangedFiles() {
|
public List<File> grabChangedFiles() {
|
||||||
|
|
|
@ -86,6 +86,7 @@ import org.teavm.parsing.resource.ResourceClassHolderMapper;
|
||||||
import org.teavm.tooling.EmptyTeaVMToolLog;
|
import org.teavm.tooling.EmptyTeaVMToolLog;
|
||||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||||
import org.teavm.tooling.TeaVMToolLog;
|
import org.teavm.tooling.TeaVMToolLog;
|
||||||
|
import org.teavm.tooling.builder.BuildResult;
|
||||||
import org.teavm.tooling.builder.SimpleBuildResult;
|
import org.teavm.tooling.builder.SimpleBuildResult;
|
||||||
import org.teavm.tooling.util.FileSystemWatcher;
|
import org.teavm.tooling.util.FileSystemWatcher;
|
||||||
import org.teavm.vm.MemoryBuildTarget;
|
import org.teavm.vm.MemoryBuildTarget;
|
||||||
|
@ -119,6 +120,8 @@ public class CodeServlet extends HttpServlet {
|
||||||
private String proxyProtocol;
|
private String proxyProtocol;
|
||||||
private int proxyPort;
|
private int proxyPort;
|
||||||
private String proxyBaseUrl;
|
private String proxyBaseUrl;
|
||||||
|
private Map<String, String> properties = new LinkedHashMap<>();
|
||||||
|
private List<String> preservedClasses = new ArrayList<>();
|
||||||
|
|
||||||
private Map<String, Supplier<InputStream>> sourceFileCache = new HashMap<>();
|
private Map<String, Supplier<InputStream>> sourceFileCache = new HashMap<>();
|
||||||
|
|
||||||
|
@ -148,6 +151,9 @@ public class CodeServlet extends HttpServlet {
|
||||||
private InMemorySymbolTable fileSymbolTable = new InMemorySymbolTable();
|
private InMemorySymbolTable fileSymbolTable = new InMemorySymbolTable();
|
||||||
private InMemorySymbolTable variableSymbolTable = new InMemorySymbolTable();
|
private InMemorySymbolTable variableSymbolTable = new InMemorySymbolTable();
|
||||||
private ReferenceCache referenceCache = new ReferenceCache();
|
private ReferenceCache referenceCache = new ReferenceCache();
|
||||||
|
private boolean fileSystemWatched = true;
|
||||||
|
private boolean compileOnStartup = true;
|
||||||
|
private boolean logBuildErrors = true;
|
||||||
|
|
||||||
public CodeServlet(String mainClass, String[] classPath) {
|
public CodeServlet(String mainClass, String[] classPath) {
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
|
@ -201,6 +207,26 @@ public class CodeServlet extends HttpServlet {
|
||||||
this.proxyPath = normalizePath(proxyPath);
|
this.proxyPath = normalizePath(proxyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFileSystemWatched(boolean fileSystemWatched) {
|
||||||
|
this.fileSystemWatched = fileSystemWatched;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompileOnStartup(boolean compileOnStartup) {
|
||||||
|
this.compileOnStartup = compileOnStartup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPreservedClasses() {
|
||||||
|
return preservedClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogBuildErrors(boolean logBuildErrors) {
|
||||||
|
this.logBuildErrors = logBuildErrors;
|
||||||
|
}
|
||||||
|
|
||||||
public void addProgressHandler(ProgressHandler handler) {
|
public void addProgressHandler(ProgressHandler handler) {
|
||||||
synchronized (progressHandlers) {
|
synchronized (progressHandlers) {
|
||||||
progressHandlers.add(handler);
|
progressHandlers.add(handler);
|
||||||
|
@ -241,12 +267,16 @@ public class CodeServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void buildProject() {
|
public void buildProject() {
|
||||||
|
if (buildThread == null) {
|
||||||
|
runCompilerThread();
|
||||||
|
} else {
|
||||||
synchronized (statusLock) {
|
synchronized (statusLock) {
|
||||||
if (waiting) {
|
if (waiting) {
|
||||||
buildThread.interrupt();
|
buildThread.interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void cancelBuild() {
|
public void cancelBuild() {
|
||||||
synchronized (statusLock) {
|
synchronized (statusLock) {
|
||||||
|
@ -617,7 +647,7 @@ public class CodeServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
stopped = true;
|
stopped = true;
|
||||||
synchronized (statusLock) {
|
synchronized (statusLock) {
|
||||||
if (waiting) {
|
if (buildThread != null && waiting) {
|
||||||
buildThread.interrupt();
|
buildThread.interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -626,7 +656,13 @@ public class CodeServlet extends HttpServlet {
|
||||||
@Override
|
@Override
|
||||||
public void init() throws ServletException {
|
public void init() throws ServletException {
|
||||||
super.init();
|
super.init();
|
||||||
Thread thread = new Thread(this::runTeaVM);
|
if (compileOnStartup) {
|
||||||
|
runCompilerThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runCompilerThread() {
|
||||||
|
var thread = new Thread(this::runTeaVM);
|
||||||
thread.setName("TeaVM compiler");
|
thread.setName("TeaVM compiler");
|
||||||
thread.start();
|
thread.start();
|
||||||
buildThread = thread;
|
buildThread = thread;
|
||||||
|
@ -726,8 +762,13 @@ public class CodeServlet extends HttpServlet {
|
||||||
try {
|
try {
|
||||||
initBuilder();
|
initBuilder();
|
||||||
|
|
||||||
|
var hasJob = true;
|
||||||
while (!stopped) {
|
while (!stopped) {
|
||||||
|
if (hasJob) {
|
||||||
buildOnce();
|
buildOnce();
|
||||||
|
} else {
|
||||||
|
emptyBuild();
|
||||||
|
}
|
||||||
|
|
||||||
if (stopped) {
|
if (stopped) {
|
||||||
break;
|
break;
|
||||||
|
@ -737,11 +778,22 @@ public class CodeServlet extends HttpServlet {
|
||||||
synchronized (statusLock) {
|
synchronized (statusLock) {
|
||||||
waiting = true;
|
waiting = true;
|
||||||
}
|
}
|
||||||
|
if (fileSystemWatched) {
|
||||||
watcher.waitForChange(750);
|
watcher.waitForChange(750);
|
||||||
|
log.info("Changes detected. Recompiling.");
|
||||||
|
} else {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watcher.pollChanges();
|
||||||
|
}
|
||||||
synchronized (statusLock) {
|
synchronized (statusLock) {
|
||||||
waiting = false;
|
waiting = false;
|
||||||
}
|
}
|
||||||
log.info("Changes detected. Recompiling.");
|
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
if (stopped) {
|
if (stopped) {
|
||||||
break;
|
break;
|
||||||
|
@ -760,6 +812,7 @@ public class CodeServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
classSource.evict(staleClasses);
|
classSource.evict(staleClasses);
|
||||||
|
hasJob = !staleClasses.isEmpty();
|
||||||
}
|
}
|
||||||
log.info("Build process stopped");
|
log.info("Build process stopped");
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -836,6 +889,10 @@ public class CodeServlet extends HttpServlet {
|
||||||
vm.setProgressListener(progressListener);
|
vm.setProgressListener(progressListener);
|
||||||
vm.setProgramCache(programCache);
|
vm.setProgramCache(programCache);
|
||||||
vm.installPlugins();
|
vm.installPlugins();
|
||||||
|
for (var className : preservedClasses) {
|
||||||
|
vm.preserveType(className);
|
||||||
|
}
|
||||||
|
vm.getProperties().putAll(properties);
|
||||||
|
|
||||||
vm.setLastKnownClasses(lastReachedClasses);
|
vm.setLastKnownClasses(lastReachedClasses);
|
||||||
vm.setEntryPoint(mainClass);
|
vm.setEntryPoint(mainClass);
|
||||||
|
@ -850,6 +907,12 @@ public class CodeServlet extends HttpServlet {
|
||||||
postBuild(vm, startTime);
|
postBuild(vm, startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void emptyBuild() {
|
||||||
|
fireBuildStarted();
|
||||||
|
log.info("No files changed, nothing to do");
|
||||||
|
fireBuildCompleteWithResult(null);
|
||||||
|
}
|
||||||
|
|
||||||
private ClassReaderSource packClasses(ClassReaderSource source, Collection<? extends String> classNames) {
|
private ClassReaderSource packClasses(ClassReaderSource source, Collection<? extends String> classNames) {
|
||||||
MemoryCachedClassReaderSource packedSource = createCachedSource();
|
MemoryCachedClassReaderSource packedSource = createCachedSource();
|
||||||
packedSource.setProvider(source::get);
|
packedSource.setProvider(source::get);
|
||||||
|
@ -912,7 +975,6 @@ 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();
|
||||||
|
@ -926,7 +988,10 @@ public class CodeServlet extends HttpServlet {
|
||||||
reportCompilationComplete(false);
|
reportCompilationComplete(false);
|
||||||
}
|
}
|
||||||
printStats(vm, startTime);
|
printStats(vm, startTime);
|
||||||
|
if (logBuildErrors) {
|
||||||
TeaVMProblemRenderer.describeProblems(vm, log);
|
TeaVMProblemRenderer.describeProblems(vm, log);
|
||||||
|
}
|
||||||
|
fireBuildComplete(vm);
|
||||||
} else {
|
} else {
|
||||||
log.info("Build cancelled");
|
log.info("Build cancelled");
|
||||||
fireBuildCancelled();
|
fireBuildCancelled();
|
||||||
|
@ -1056,9 +1121,12 @@ public class CodeServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireBuildComplete(TeaVM vm) {
|
private void fireBuildComplete(TeaVM vm) {
|
||||||
SimpleBuildResult result = new SimpleBuildResult(vm, new ArrayList<>(buildTarget.getNames()));
|
fireBuildCompleteWithResult(new SimpleBuildResult(vm));
|
||||||
for (DevServerListener listener : listeners) {
|
}
|
||||||
listener.compilationComplete(result);
|
|
||||||
|
private void fireBuildCompleteWithResult(BuildResult buildResult) {
|
||||||
|
for (var listener : listeners) {
|
||||||
|
listener.compilationComplete(buildResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1089,7 +1157,7 @@ public class CodeServlet extends HttpServlet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TeaVMProgressFeedback progressReached(int progress) {
|
public TeaVMProgressFeedback progressReached(int progress) {
|
||||||
if (indicator) {
|
if (indicator || !listeners.isEmpty()) {
|
||||||
int current = start + Math.min(progress, phaseLimit) * (end - start) / phaseLimit;
|
int current = start + Math.min(progress, phaseLimit) * (end - start) / phaseLimit;
|
||||||
if (current != last) {
|
if (current != last) {
|
||||||
if (current - last > 10 || System.currentTimeMillis() - lastTime > 100) {
|
if (current - last > 10 || System.currentTimeMillis() - lastTime > 100) {
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
package org.teavm.devserver;
|
package org.teavm.devserver;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
|
@ -32,9 +34,14 @@ public class DevServer {
|
||||||
private boolean indicator;
|
private boolean indicator;
|
||||||
private boolean deobfuscateStack;
|
private boolean deobfuscateStack;
|
||||||
private boolean reloadedAutomatically;
|
private boolean reloadedAutomatically;
|
||||||
|
private boolean fileSystemWatched = true;
|
||||||
private TeaVMToolLog log;
|
private TeaVMToolLog log;
|
||||||
private CodeServlet servlet;
|
private CodeServlet servlet;
|
||||||
private List<DevServerListener> listeners = new ArrayList<>();
|
private List<DevServerListener> listeners = new ArrayList<>();
|
||||||
|
private Map<String, String> properties = new LinkedHashMap<>();
|
||||||
|
private List<String> preservedClasses = new ArrayList<>();
|
||||||
|
private boolean compileOnStartup;
|
||||||
|
private boolean logBuildErrors = true;
|
||||||
|
|
||||||
private Server server;
|
private Server server;
|
||||||
private int port = 9090;
|
private int port = 9090;
|
||||||
|
@ -88,6 +95,10 @@ public class DevServer {
|
||||||
this.reloadedAutomatically = reloadedAutomatically;
|
this.reloadedAutomatically = reloadedAutomatically;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFileSystemWatched(boolean fileSystemWatched) {
|
||||||
|
this.fileSystemWatched = fileSystemWatched;
|
||||||
|
}
|
||||||
|
|
||||||
public void setProxyUrl(String proxyUrl) {
|
public void setProxyUrl(String proxyUrl) {
|
||||||
this.proxyUrl = proxyUrl;
|
this.proxyUrl = proxyUrl;
|
||||||
}
|
}
|
||||||
|
@ -100,6 +111,18 @@ public class DevServer {
|
||||||
return sourcePath;
|
return sourcePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCompileOnStartup(boolean compileOnStartup) {
|
||||||
|
this.compileOnStartup = compileOnStartup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPreservedClasses() {
|
||||||
|
return preservedClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
public void invalidateCache() {
|
public void invalidateCache() {
|
||||||
servlet.invalidateCache();
|
servlet.invalidateCache();
|
||||||
}
|
}
|
||||||
|
@ -116,6 +139,10 @@ public class DevServer {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLogBuildErrors(boolean logBuildErrors) {
|
||||||
|
this.logBuildErrors = logBuildErrors;
|
||||||
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
server = new Server();
|
server = new Server();
|
||||||
ServerConnector connector = new ServerConnector(server);
|
ServerConnector connector = new ServerConnector(server);
|
||||||
|
@ -138,6 +165,11 @@ public class DevServer {
|
||||||
servlet.setDebugPort(debugPort);
|
servlet.setDebugPort(debugPort);
|
||||||
servlet.setProxyUrl(proxyUrl);
|
servlet.setProxyUrl(proxyUrl);
|
||||||
servlet.setProxyPath(proxyPath);
|
servlet.setProxyPath(proxyPath);
|
||||||
|
servlet.setFileSystemWatched(fileSystemWatched);
|
||||||
|
servlet.setCompileOnStartup(compileOnStartup);
|
||||||
|
servlet.setLogBuildErrors(logBuildErrors);
|
||||||
|
servlet.getProperties().putAll(properties);
|
||||||
|
servlet.getPreservedClasses().addAll(preservedClasses);
|
||||||
for (DevServerListener listener : listeners) {
|
for (DevServerListener listener : listeners) {
|
||||||
servlet.addListener(listener);
|
servlet.addListener(listener);
|
||||||
}
|
}
|
||||||
|
@ -147,12 +179,15 @@ public class DevServer {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
server.start();
|
server.start();
|
||||||
server.join();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void awaitServer() throws InterruptedException {
|
||||||
|
server.join();
|
||||||
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
try {
|
try {
|
||||||
server.stop();
|
server.stop();
|
||||||
|
|
|
@ -13,14 +13,14 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
(function (window) {
|
(function () {
|
||||||
var boot = BOOT_FLAG;
|
let boot = BOOT_FLAG;
|
||||||
var reload = RELOAD_FLAG;
|
let reload = RELOAD_FLAG;
|
||||||
var indicatorVisible = INDICATOR_FLAG;
|
let indicatorVisible = INDICATOR_FLAG;
|
||||||
var debugPort = DEBUG_PORT;
|
let debugPort = DEBUG_PORT;
|
||||||
var deobfuscate = DEOBFUSCATE_FLAG;
|
let deobfuscate = DEOBFUSCATE_FLAG;
|
||||||
var fileName = FILE_NAME;
|
let fileName = FILE_NAME;
|
||||||
var pathToFile = PATH_TO_FILE;
|
let pathToFile = PATH_TO_FILE;
|
||||||
|
|
||||||
function createWebSocket() {
|
function createWebSocket() {
|
||||||
return new WebSocket("ws://WS_PATH");
|
return new WebSocket("ws://WS_PATH");
|
||||||
|
@ -28,18 +28,18 @@
|
||||||
|
|
||||||
function createIndicator() {
|
function createIndicator() {
|
||||||
function createMainElement() {
|
function createMainElement() {
|
||||||
var element = document.createElement("div");
|
let element = document.createElement("div");
|
||||||
element.style.position = "fixed";
|
element.style.position = "fixed";
|
||||||
element.style.left = "0";
|
element.style.left = "0";
|
||||||
element.style.bottom = "0";
|
element.style.bottom = "0";
|
||||||
element.style.backgroundColor = "black";
|
element.style.backgroundColor = "black";
|
||||||
element.style.color = "white";
|
element.style.color = "white";
|
||||||
element.style.opacity = 0.4;
|
element.style.opacity = "0.4";
|
||||||
element.style.padding = "5px";
|
element.style.padding = "5px";
|
||||||
element.style.fontSize = "18px";
|
element.style.fontSize = "18px";
|
||||||
element.style.fontWeight = "bold";
|
element.style.fontWeight = "bold";
|
||||||
element.style.pointerEvents = "none";
|
element.style.pointerEvents = "none";
|
||||||
element.style.zIndex = 1000;
|
element.style.zIndex = "1000";
|
||||||
element.style.display = "none";
|
element.style.display = "none";
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProgressElements() {
|
function createProgressElements() {
|
||||||
var element = document.createElement("span");
|
const element = document.createElement("span");
|
||||||
element.style.display = "none";
|
element.style.display = "none";
|
||||||
element.style.marginLeft = "10px";
|
element.style.marginLeft = "10px";
|
||||||
element.style.width = "150px";
|
element.style.width = "150px";
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
element.style.backgroundColor = "white";
|
element.style.backgroundColor = "white";
|
||||||
element.style.position = "relative";
|
element.style.position = "relative";
|
||||||
|
|
||||||
var progress = document.createElement("span");
|
const progress = document.createElement("span");
|
||||||
progress.style.display = "block";
|
progress.style.display = "block";
|
||||||
progress.style.position = "absolute";
|
progress.style.position = "absolute";
|
||||||
progress.style.left = "0";
|
progress.style.left = "0";
|
||||||
|
@ -80,9 +80,9 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var container = createMainElement();
|
const container = createMainElement();
|
||||||
var label = createLabelElement();
|
const label = createLabelElement();
|
||||||
var progress = createProgressElements();
|
const progress = createProgressElements();
|
||||||
container.appendChild(label);
|
container.appendChild(label);
|
||||||
container.appendChild(progress.container);
|
container.appendChild(progress.container);
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@
|
||||||
progress: progress,
|
progress: progress,
|
||||||
timer: void 0,
|
timer: void 0,
|
||||||
|
|
||||||
show: function(text, timeout) {
|
show(text, timeout) {
|
||||||
this.container.style.display = "block";
|
this.container.style.display = "block";
|
||||||
this.label.innerText = text;
|
this.label.innerText = text;
|
||||||
if (this.timer) {
|
if (this.timer) {
|
||||||
|
@ -118,7 +118,7 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var indicator = createIndicator();
|
let indicator = createIndicator();
|
||||||
function onLoad() {
|
function onLoad() {
|
||||||
document.body.appendChild(indicator.container);
|
document.body.appendChild(indicator.container);
|
||||||
}
|
}
|
||||||
|
@ -137,15 +137,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof main === 'function') {
|
if (typeof main === 'function') {
|
||||||
var oldMain = main;
|
let oldMain = main;
|
||||||
main = function() {
|
main = function() {
|
||||||
var args = arguments;
|
const args = arguments;
|
||||||
window.$teavm_deobfuscator_callback = function() {
|
window.$teavm_deobfuscator_callback = () => {
|
||||||
oldMain.apply(window, args);
|
oldMain.apply(window, args);
|
||||||
};
|
};
|
||||||
var elem = document.createElement("script");
|
const elem = document.createElement("script");
|
||||||
elem.src = pathToFile + fileName + ".deobfuscator.js";
|
elem.src = pathToFile + fileName + ".deobfuscator.js";
|
||||||
elem.onload = function() {
|
elem.onload = () => {
|
||||||
$teavm_deobfuscator([pathToFile + fileName + ".teavmdbg", pathToFile + fileName]);
|
$teavm_deobfuscator([pathToFile + fileName + ".teavmdbg", pathToFile + fileName]);
|
||||||
};
|
};
|
||||||
document.head.append(elem);
|
document.head.append(elem);
|
||||||
|
@ -165,9 +165,9 @@
|
||||||
main();
|
main();
|
||||||
}
|
}
|
||||||
|
|
||||||
var ws = createWebSocket();
|
let ws = createWebSocket();
|
||||||
ws.onmessage = function(event) {
|
ws.onmessage = function(event) {
|
||||||
var message = JSON.parse(event.data);
|
const message = JSON.parse(event.data);
|
||||||
switch (message.command) {
|
switch (message.command) {
|
||||||
case "compiling":
|
case "compiling":
|
||||||
indicator.show("Compiling...");
|
indicator.show("Compiling...");
|
||||||
|
@ -177,9 +177,9 @@
|
||||||
if (message.success) {
|
if (message.success) {
|
||||||
indicator.show("Compilation complete", 10);
|
indicator.show("Compilation complete", 10);
|
||||||
if (reload) {
|
if (reload) {
|
||||||
window.location.reload(true);
|
window.location.reload();
|
||||||
} else if (boot) {
|
} else if (boot) {
|
||||||
var scriptElem = document.createElement("script");
|
const scriptElem = document.createElement("script");
|
||||||
scriptElem.src = pathToFile + fileName;
|
scriptElem.src = pathToFile + fileName;
|
||||||
scriptElem.onload = startMain;
|
scriptElem.onload = startMain;
|
||||||
document.head.appendChild(scriptElem);
|
document.head.appendChild(scriptElem);
|
||||||
|
@ -197,12 +197,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debugPort > 0) {
|
if (debugPort > 0) {
|
||||||
var connected = false;
|
let connected = false;
|
||||||
function connectDebugAgent(event) {
|
function connectDebugAgent(event) {
|
||||||
if (event.source !== window) {
|
if (event.source !== window) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var data = event.data;
|
const data = event.data;
|
||||||
if (typeof data.teavmDebuggerRequest !== "undefined" && !connected) {
|
if (typeof data.teavmDebuggerRequest !== "undefined" && !connected) {
|
||||||
connected = true;
|
connected = true;
|
||||||
window.postMessage({teavmDebugger: {port: debugPort}}, "*");
|
window.postMessage({teavmDebugger: {port: debugPort}}, "*");
|
||||||
|
@ -211,4 +211,4 @@
|
||||||
window.addEventListener("message", connectDebugAgent);
|
window.addEventListener("message", connectDebugAgent);
|
||||||
window.postMessage({teavmDebugger: {port: debugPort}}, "*");
|
window.postMessage({teavmDebugger: {port: debugPort}}, "*");
|
||||||
}
|
}
|
||||||
})(this);
|
})();
|
|
@ -74,6 +74,7 @@ val createConfig by tasks.registering {
|
||||||
val jsoImpl = findArtifactCoordinates(":jso:impl")
|
val jsoImpl = findArtifactCoordinates(":jso:impl")
|
||||||
val metaprogrammingImpl = findArtifactCoordinates(":metaprogramming:impl")
|
val metaprogrammingImpl = findArtifactCoordinates(":metaprogramming:impl")
|
||||||
val tools = findArtifactCoordinates(":tools:core")
|
val tools = findArtifactCoordinates(":tools:core")
|
||||||
|
val cli = findArtifactCoordinates(":tools:cli")
|
||||||
val junit = findArtifactCoordinates(":tools:junit")
|
val junit = findArtifactCoordinates(":tools:junit")
|
||||||
doLast {
|
doLast {
|
||||||
val file = File(baseDir, "org/teavm/gradle/config/ArtifactCoordinates.java")
|
val file = File(baseDir, "org/teavm/gradle/config/ArtifactCoordinates.java")
|
||||||
|
@ -93,6 +94,7 @@ val createConfig by tasks.registering {
|
||||||
public static final String JUNIT = "$junit";
|
public static final String JUNIT = "$junit";
|
||||||
|
|
||||||
public static final String TOOLS = "$tools";
|
public static final String TOOLS = "$tools";
|
||||||
|
public static final String CLI = "$cli";
|
||||||
|
|
||||||
private ArtifactCoordinates() {
|
private ArtifactCoordinates() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.gradle;
|
package org.teavm.gradle;
|
||||||
|
|
||||||
import groovy.lang.Closure;
|
import groovy.lang.Closure;
|
||||||
|
import javax.inject.Inject;
|
||||||
import org.gradle.api.Action;
|
import org.gradle.api.Action;
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
import org.gradle.api.model.ObjectFactory;
|
import org.gradle.api.model.ObjectFactory;
|
||||||
|
@ -24,6 +25,7 @@ import org.teavm.gradle.api.OptimizationLevel;
|
||||||
import org.teavm.gradle.api.SourceFilePolicy;
|
import org.teavm.gradle.api.SourceFilePolicy;
|
||||||
import org.teavm.gradle.api.TeaVMCConfiguration;
|
import org.teavm.gradle.api.TeaVMCConfiguration;
|
||||||
import org.teavm.gradle.api.TeaVMCommonConfiguration;
|
import org.teavm.gradle.api.TeaVMCommonConfiguration;
|
||||||
|
import org.teavm.gradle.api.TeaVMDevServerConfiguration;
|
||||||
import org.teavm.gradle.api.TeaVMExtension;
|
import org.teavm.gradle.api.TeaVMExtension;
|
||||||
import org.teavm.gradle.api.TeaVMJSConfiguration;
|
import org.teavm.gradle.api.TeaVMJSConfiguration;
|
||||||
import org.teavm.gradle.api.TeaVMWasiConfiguration;
|
import org.teavm.gradle.api.TeaVMWasiConfiguration;
|
||||||
|
@ -38,7 +40,7 @@ class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtensio
|
||||||
|
|
||||||
TeaVMExtensionImpl(Project project, ObjectFactory objectFactory) {
|
TeaVMExtensionImpl(Project project, ObjectFactory objectFactory) {
|
||||||
super(project, objectFactory);
|
super(project, objectFactory);
|
||||||
js = objectFactory.newInstance(TeaVMJSConfiguration.class);
|
js = objectFactory.newInstance(JsConfigImpl.class);
|
||||||
wasm = objectFactory.newInstance(TeaVMWasmConfiguration.class);
|
wasm = objectFactory.newInstance(TeaVMWasmConfiguration.class);
|
||||||
wasi = objectFactory.newInstance(TeaVMWasiConfiguration.class);
|
wasi = objectFactory.newInstance(TeaVMWasiConfiguration.class);
|
||||||
c = objectFactory.newInstance(TeaVMCConfiguration.class);
|
c = objectFactory.newInstance(TeaVMCConfiguration.class);
|
||||||
|
@ -72,6 +74,14 @@ class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtensio
|
||||||
js.getSourceFilePolicy().convention(property("js.sourceFilePolicy")
|
js.getSourceFilePolicy().convention(property("js.sourceFilePolicy")
|
||||||
.map(SourceFilePolicy::valueOf)
|
.map(SourceFilePolicy::valueOf)
|
||||||
.orElse(SourceFilePolicy.DO_NOTHING));
|
.orElse(SourceFilePolicy.DO_NOTHING));
|
||||||
|
js.getDevServer().getStackDeobfuscated().convention(property("js.devServer.stackDeobfuscated")
|
||||||
|
.map(Boolean::parseBoolean));
|
||||||
|
js.getDevServer().getIndicator().convention(property("js.devServer.indicator").map(Boolean::parseBoolean));
|
||||||
|
js.getDevServer().getAutoReload().convention(property("js.devServer.autoReload").map(Boolean::parseBoolean));
|
||||||
|
js.getDevServer().getPort().convention(property("js.devServer.port").map(Integer::parseInt));
|
||||||
|
js.getDevServer().getProxyUrl().convention(property("js.devServer.proxy.url"));
|
||||||
|
js.getDevServer().getProxyPath().convention(property("js.devServer.proxy.path"));
|
||||||
|
js.getDevServer().getProcessMemory().convention(property("js.devServer.memory").map(Integer::parseInt));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupWasmDefaults() {
|
private void setupWasmDefaults() {
|
||||||
|
@ -199,4 +209,28 @@ class TeaVMExtensionImpl extends TeaVMBaseExtensionImpl implements TeaVMExtensio
|
||||||
target.getOutOfProcess().convention(source.getOutOfProcess());
|
target.getOutOfProcess().convention(source.getOutOfProcess());
|
||||||
target.getProcessMemory().convention(source.getProcessMemory());
|
target.getProcessMemory().convention(source.getProcessMemory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static abstract class JsConfigImpl implements TeaVMJSConfiguration {
|
||||||
|
private TeaVMDevServerConfiguration devServer;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public JsConfigImpl(Project project) {
|
||||||
|
devServer = project.getObjects().newInstance(TeaVMDevServerConfiguration.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void devServer(Action<TeaVMDevServerConfiguration> action) {
|
||||||
|
action.execute(devServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TeaVMDevServerConfiguration getDevServer() {
|
||||||
|
return devServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void devServer(Closure<?> action) {
|
||||||
|
action.rehydrate(getDevServer(), action.getOwner(), action.getThisObject()).call();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.gradle.api.artifacts.Configuration;
|
||||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
||||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
|
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
|
||||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult;
|
import org.gradle.api.artifacts.result.ResolvedDependencyResult;
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
import org.gradle.api.file.DuplicatesStrategy;
|
import org.gradle.api.file.DuplicatesStrategy;
|
||||||
import org.gradle.api.model.ObjectFactory;
|
import org.gradle.api.model.ObjectFactory;
|
||||||
import org.gradle.api.plugins.JavaPlugin;
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
@ -40,17 +41,22 @@ import org.teavm.gradle.tasks.GenerateCTask;
|
||||||
import org.teavm.gradle.tasks.GenerateJavaScriptTask;
|
import org.teavm.gradle.tasks.GenerateJavaScriptTask;
|
||||||
import org.teavm.gradle.tasks.GenerateWasiTask;
|
import org.teavm.gradle.tasks.GenerateWasiTask;
|
||||||
import org.teavm.gradle.tasks.GenerateWasmTask;
|
import org.teavm.gradle.tasks.GenerateWasmTask;
|
||||||
|
import org.teavm.gradle.tasks.JavaScriptDevServerTask;
|
||||||
|
import org.teavm.gradle.tasks.StopJavaScriptDevServerTask;
|
||||||
import org.teavm.gradle.tasks.TeaVMTask;
|
import org.teavm.gradle.tasks.TeaVMTask;
|
||||||
|
|
||||||
public class TeaVMPlugin implements Plugin<Project> {
|
public class TeaVMPlugin implements Plugin<Project> {
|
||||||
public static final String EXTENSION_NAME = "teavm";
|
public static final String EXTENSION_NAME = "teavm";
|
||||||
public static final String SOURCE_SET_NAME = "teavm";
|
public static final String SOURCE_SET_NAME = "teavm";
|
||||||
public static final String JS_TASK_NAME = "generateJavaScript";
|
public static final String JS_TASK_NAME = "generateJavaScript";
|
||||||
|
public static final String JS_DEV_SERVER_TASK_NAME = "javaScriptDevServer";
|
||||||
|
public static final String STOP_JS_DEV_SERVER_TASK_NAME = "stopJavaScriptDevServer";
|
||||||
public static final String WASM_TASK_NAME = "generateWasm";
|
public static final String WASM_TASK_NAME = "generateWasm";
|
||||||
public static final String WASI_TASK_NAME = "generateWasi";
|
public static final String WASI_TASK_NAME = "generateWasi";
|
||||||
public static final String C_TASK_NAME = "generateC";
|
public static final String C_TASK_NAME = "generateC";
|
||||||
public static final String CONFIGURATION_NAME = "teavm";
|
public static final String CONFIGURATION_NAME = "teavm";
|
||||||
public static final String CLASSPATH_CONFIGURATION_NAME = "teavmClasspath";
|
public static final String CLASSPATH_CONFIGURATION_NAME = "teavmClasspath";
|
||||||
|
public static final String TASK_GROUP = "TeaVM";
|
||||||
private ObjectFactory objectFactory;
|
private ObjectFactory objectFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -105,7 +111,11 @@ public class TeaVMPlugin implements Plugin<Project> {
|
||||||
private void registerTasks(Project project) {
|
private void registerTasks(Project project) {
|
||||||
var compilerConfig = project.getConfigurations().detachedConfiguration(
|
var compilerConfig = project.getConfigurations().detachedConfiguration(
|
||||||
project.getDependencies().create(ArtifactCoordinates.TOOLS));
|
project.getDependencies().create(ArtifactCoordinates.TOOLS));
|
||||||
|
var cliConfig = project.getConfigurations().detachedConfiguration(
|
||||||
|
project.getDependencies().create(ArtifactCoordinates.CLI));
|
||||||
registerJsTask(project, compilerConfig);
|
registerJsTask(project, compilerConfig);
|
||||||
|
registerJsDevServerTask(project, cliConfig);
|
||||||
|
registerStopJsDevServerTask(project);
|
||||||
registerWasmTask(project, compilerConfig);
|
registerWasmTask(project, compilerConfig);
|
||||||
registerWasiTask(project, compilerConfig);
|
registerWasiTask(project, compilerConfig);
|
||||||
registerCTask(project, compilerConfig);
|
registerCTask(project, compilerConfig);
|
||||||
|
@ -125,47 +135,44 @@ public class TeaVMPlugin implements Plugin<Project> {
|
||||||
task.getSourceFilePolicy().convention(js.getSourceFilePolicy());
|
task.getSourceFilePolicy().convention(js.getSourceFilePolicy());
|
||||||
task.getMaxTopLevelNames().convention(js.getMaxTopLevelNames());
|
task.getMaxTopLevelNames().convention(js.getMaxTopLevelNames());
|
||||||
|
|
||||||
task.getSourceFiles().from(project.provider(() -> {
|
setupSources(task.getSourceFiles(), project);
|
||||||
var result = new ArrayList<File>();
|
});
|
||||||
addSourceDirs(project, result);
|
}
|
||||||
return result;
|
|
||||||
}));
|
|
||||||
task.getSourceFiles().from(project.provider(() -> {
|
|
||||||
var dependencies = project.getConfigurations()
|
|
||||||
.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)
|
|
||||||
.getIncoming()
|
|
||||||
.getResolutionResult()
|
|
||||||
.getAllDependencies();
|
|
||||||
|
|
||||||
var result = new ArrayList<File>();
|
private void registerJsDevServerTask(Project project, Configuration configuration) {
|
||||||
for (var dependencyResult : dependencies) {
|
var extension = project.getExtensions().getByType(TeaVMExtension.class);
|
||||||
if (!(dependencyResult instanceof ResolvedDependencyResult)) {
|
project.getTasks().create(JS_DEV_SERVER_TASK_NAME, JavaScriptDevServerTask.class, task -> {
|
||||||
continue;
|
var js = extension.getJs();
|
||||||
|
task.setGroup(TASK_GROUP);
|
||||||
|
task.getMainClass().convention(js.getMainClass());
|
||||||
|
task.getClasspath().from(task.getProject().getConfigurations().getByName(CLASSPATH_CONFIGURATION_NAME));
|
||||||
|
task.getPreservedClasses().addAll(js.getPreservedClasses());
|
||||||
|
task.getProcessMemory().convention(js.getDevServer().getProcessMemory());
|
||||||
|
task.getProperties().putAll(js.getProperties());
|
||||||
|
task.getServerClasspath().from(configuration);
|
||||||
|
task.getTargetFilePath().convention(js.getRelativePathInOutputDir());
|
||||||
|
task.getTargetFileName().convention(js.getTargetFileName());
|
||||||
|
task.getStackDeobfuscated().convention(js.getDevServer().getStackDeobfuscated());
|
||||||
|
task.getIndicator().convention(js.getDevServer().getIndicator());
|
||||||
|
task.getAutoReload().convention(js.getDevServer().getAutoReload());
|
||||||
|
task.getPort().convention(js.getDevServer().getPort());
|
||||||
|
task.getProxyUrl().convention(js.getDevServer().getProxyUrl());
|
||||||
|
task.getProxyPath().convention(js.getDevServer().getProxyPath());
|
||||||
|
task.getProcessMemory().convention(js.getDevServer().getProcessMemory());
|
||||||
|
|
||||||
|
var sourceSets = project.getExtensions().findByType(SourceSetContainer.class);
|
||||||
|
if (sourceSets != null) {
|
||||||
|
task.getClasspath().from(sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput());
|
||||||
|
task.getClasspath().from(sourceSets.getByName(SOURCE_SET_NAME).getOutput());
|
||||||
}
|
}
|
||||||
var id = ((ResolvedDependencyResult) dependencyResult).getSelected().getId();
|
|
||||||
if (id instanceof ProjectComponentIdentifier) {
|
setupSources(task.getSourceFiles(), project);
|
||||||
var path = ((ProjectComponentIdentifier) id).getProjectPath();
|
});
|
||||||
var refProject = project.getRootProject().findProject(path);
|
|
||||||
if (refProject != null) {
|
|
||||||
addSourceDirs(refProject, result);
|
|
||||||
}
|
}
|
||||||
} else if (id instanceof ModuleComponentIdentifier) {
|
|
||||||
var moduleId = (ModuleComponentIdentifier) id;
|
private void registerStopJsDevServerTask(Project project) {
|
||||||
var sourcesDep = project.getDependencies().create(Map.of(
|
project.getTasks().create(STOP_JS_DEV_SERVER_TASK_NAME, StopJavaScriptDevServerTask.class, task -> {
|
||||||
"group", moduleId.getGroup(),
|
task.setGroup(TASK_GROUP);
|
||||||
"name", moduleId.getModuleIdentifier().getName(),
|
|
||||||
"version", moduleId.getVersion(),
|
|
||||||
"classifier", "sources"
|
|
||||||
));
|
|
||||||
var tmpConfig = project.getConfigurations().detachedConfiguration(sourcesDep);
|
|
||||||
tmpConfig.setTransitive(false);
|
|
||||||
if (!tmpConfig.getResolvedConfiguration().hasError()) {
|
|
||||||
result.addAll(tmpConfig.getResolvedConfiguration().getLenientConfiguration().getFiles());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,5 +277,51 @@ public class TeaVMPlugin implements Plugin<Project> {
|
||||||
task.getClasspath().from(sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput());
|
task.getClasspath().from(sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput());
|
||||||
task.getClasspath().from(sourceSets.getByName(SOURCE_SET_NAME).getOutput());
|
task.getClasspath().from(sourceSets.getByName(SOURCE_SET_NAME).getOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task.setGroup(TASK_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSources(ConfigurableFileCollection sources, Project project) {
|
||||||
|
sources.from(project.provider(() -> {
|
||||||
|
var result = new ArrayList<File>();
|
||||||
|
addSourceDirs(project, result);
|
||||||
|
return result;
|
||||||
|
}));
|
||||||
|
sources.from(project.provider(() -> {
|
||||||
|
var dependencies = project.getConfigurations()
|
||||||
|
.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)
|
||||||
|
.getIncoming()
|
||||||
|
.getResolutionResult()
|
||||||
|
.getAllDependencies();
|
||||||
|
|
||||||
|
var result = new ArrayList<File>();
|
||||||
|
for (var dependencyResult : dependencies) {
|
||||||
|
if (!(dependencyResult instanceof ResolvedDependencyResult)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var id = ((ResolvedDependencyResult) dependencyResult).getSelected().getId();
|
||||||
|
if (id instanceof ProjectComponentIdentifier) {
|
||||||
|
var path = ((ProjectComponentIdentifier) id).getProjectPath();
|
||||||
|
var refProject = project.getRootProject().findProject(path);
|
||||||
|
if (refProject != null) {
|
||||||
|
addSourceDirs(refProject, result);
|
||||||
|
}
|
||||||
|
} else if (id instanceof ModuleComponentIdentifier) {
|
||||||
|
var moduleId = (ModuleComponentIdentifier) id;
|
||||||
|
var sourcesDep = project.getDependencies().create(Map.of(
|
||||||
|
"group", moduleId.getGroup(),
|
||||||
|
"name", moduleId.getModuleIdentifier().getName(),
|
||||||
|
"version", moduleId.getVersion(),
|
||||||
|
"classifier", "sources"
|
||||||
|
));
|
||||||
|
var tmpConfig = project.getConfigurations().detachedConfiguration(sourcesDep);
|
||||||
|
tmpConfig.setTransitive(false);
|
||||||
|
if (!tmpConfig.getResolvedConfiguration().hasError()) {
|
||||||
|
result.addAll(tmpConfig.getResolvedConfiguration().getLenientConfiguration().getFiles());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.gradle.api;
|
||||||
|
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
|
||||||
|
public interface TeaVMDevServerConfiguration {
|
||||||
|
Property<Boolean> getStackDeobfuscated();
|
||||||
|
|
||||||
|
Property<Boolean> getIndicator();
|
||||||
|
|
||||||
|
Property<Boolean> getAutoReload();
|
||||||
|
|
||||||
|
Property<Integer> getPort();
|
||||||
|
|
||||||
|
Property<String> getProxyUrl();
|
||||||
|
|
||||||
|
Property<String> getProxyPath();
|
||||||
|
|
||||||
|
Property<Integer> getProcessMemory();
|
||||||
|
}
|
|
@ -15,6 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.gradle.api;
|
package org.teavm.gradle.api;
|
||||||
|
|
||||||
|
import groovy.lang.Closure;
|
||||||
|
import groovy.lang.DelegatesTo;
|
||||||
|
import org.gradle.api.Action;
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
|
|
||||||
public interface TeaVMJSConfiguration extends TeaVMWebConfiguration {
|
public interface TeaVMJSConfiguration extends TeaVMWebConfiguration {
|
||||||
|
@ -33,4 +36,10 @@ public interface TeaVMJSConfiguration extends TeaVMWebConfiguration {
|
||||||
Property<SourceFilePolicy> getSourceFilePolicy();
|
Property<SourceFilePolicy> getSourceFilePolicy();
|
||||||
|
|
||||||
Property<Integer> getMaxTopLevelNames();
|
Property<Integer> getMaxTopLevelNames();
|
||||||
|
|
||||||
|
TeaVMDevServerConfiguration getDevServer();
|
||||||
|
|
||||||
|
void devServer(Action<TeaVMDevServerConfiguration> action);
|
||||||
|
|
||||||
|
void devServer(@DelegatesTo(TeaVMDevServerConfiguration.class) Closure<?> action);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.gradle.tasks;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.invocation.Gradle;
|
||||||
|
|
||||||
|
public final class DevServerManager {
|
||||||
|
private static DevServerManager instance;
|
||||||
|
private final ConcurrentMap<String, ProjectDevServerManager> projectManagers = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private DevServerManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectDevServerManager getProjectManager(String path) {
|
||||||
|
return projectManagers.computeIfAbsent(path, this::createProjectManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProjectDevServerManager createProjectManager(String path) {
|
||||||
|
return new ProjectDevServerManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup(Gradle gradle) {
|
||||||
|
var allProjectPaths = new HashSet<String>();
|
||||||
|
collectProjects(gradle.getRootProject(), allProjectPaths);
|
||||||
|
var keysToRemove = new HashSet<>(projectManagers.keySet());
|
||||||
|
keysToRemove.removeAll(allProjectPaths);
|
||||||
|
for (var path : keysToRemove) {
|
||||||
|
var pm = projectManagers.remove(path);
|
||||||
|
if (pm != null) {
|
||||||
|
pm.stop(gradle.getRootProject().getLogger());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DevServerManager instance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new DevServerManager();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void collectProjects(Project project, Set<String> collector) {
|
||||||
|
if (!collector.add(project.getPath())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var child : project.getChildProjects().values()) {
|
||||||
|
collectProjects(child, collector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.gradle.tasks;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
|
import org.gradle.api.provider.ListProperty;
|
||||||
|
import org.gradle.api.provider.MapProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
|
import org.gradle.api.tasks.Classpath;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.Internal;
|
||||||
|
import org.gradle.api.tasks.Optional;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.internal.logging.progress.ProgressLoggerFactory;
|
||||||
|
|
||||||
|
public abstract class JavaScriptDevServerTask extends DefaultTask {
|
||||||
|
@Classpath
|
||||||
|
public abstract ConfigurableFileCollection getClasspath();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<String> getTargetFileName();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<String> getTargetFilePath();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract MapProperty<String, String> getProperties();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract ListProperty<String> getPreservedClasses();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract Property<String> getMainClass();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<Boolean> getStackDeobfuscated();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<Boolean> getIndicator();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<Integer> getPort();
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
public abstract ConfigurableFileCollection getSourceFiles();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<Boolean> getAutoReload();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<String> getProxyUrl();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<String> getProxyPath();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<Integer> getProcessMemory();
|
||||||
|
|
||||||
|
@Classpath
|
||||||
|
public abstract ConfigurableFileCollection getServerClasspath();
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public abstract Property<Integer> getServerDebugPort();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected abstract ProgressLoggerFactory getProgressLoggerFactory();
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void compileInCodeServer() throws IOException {
|
||||||
|
var codeServerManager = DevServerManager.instance();
|
||||||
|
codeServerManager.cleanup(getProject().getGradle());
|
||||||
|
var pm = codeServerManager.getProjectManager(getProject().getPath());
|
||||||
|
|
||||||
|
pm.setClasspath(getClasspath().getFiles());
|
||||||
|
pm.setSources(getSourceFiles().getFiles());
|
||||||
|
if (getTargetFileName().isPresent()) {
|
||||||
|
pm.setTargetFileName(getTargetFileName().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getTargetFilePath().isPresent()) {
|
||||||
|
pm.setTargetFilePath(getTargetFilePath().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.setProperties(getProperties().get());
|
||||||
|
pm.setPreservedClasses(getPreservedClasses().get());
|
||||||
|
|
||||||
|
pm.setServerClasspath(getServerClasspath().getFiles());
|
||||||
|
pm.setMainClass(getMainClass().get());
|
||||||
|
|
||||||
|
pm.setStackDeobfuscated(!getStackDeobfuscated().isPresent() || getStackDeobfuscated().get());
|
||||||
|
pm.setIndicator(getIndicator().isPresent() && getIndicator().get());
|
||||||
|
pm.setAutoReload(getAutoReload().isPresent() && getAutoReload().get());
|
||||||
|
|
||||||
|
if (getPort().isPresent()) {
|
||||||
|
pm.setPort(getPort().get());
|
||||||
|
}
|
||||||
|
if (getProxyUrl().isPresent()) {
|
||||||
|
pm.setProxyUrl(getProxyUrl().get());
|
||||||
|
}
|
||||||
|
if (getProxyPath().isPresent()) {
|
||||||
|
pm.setProxyPath(getProxyPath().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getProcessMemory().isPresent()) {
|
||||||
|
pm.setProcessMemory(getProcessMemory().get());
|
||||||
|
}
|
||||||
|
if (getServerDebugPort().isPresent()) {
|
||||||
|
pm.setDebugPort(getServerDebugPort().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
var progress = getProgressLoggerFactory().newOperation(getClass());
|
||||||
|
progress.start("Compilation", getName());
|
||||||
|
pm.runBuild(getLogger(), progress);
|
||||||
|
progress.completed();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,589 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.gradle.tasks;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.logging.Logger;
|
||||||
|
import org.gradle.internal.logging.progress.ProgressLogger;
|
||||||
|
import org.teavm.common.json.JsonArrayValue;
|
||||||
|
import org.teavm.common.json.JsonObjectValue;
|
||||||
|
import org.teavm.common.json.JsonParser;
|
||||||
|
import org.teavm.common.json.JsonValue;
|
||||||
|
|
||||||
|
public class ProjectDevServerManager {
|
||||||
|
private Set<File> serverClasspath = new LinkedHashSet<>();
|
||||||
|
private Set<File> classpath = new LinkedHashSet<>();
|
||||||
|
private String targetFileName;
|
||||||
|
private String targetFilePath;
|
||||||
|
private Map<String, String> properties = new LinkedHashMap<>();
|
||||||
|
private Set<String> preservedClasses = new LinkedHashSet<>();
|
||||||
|
private String mainClass;
|
||||||
|
private boolean stackDeobfuscated;
|
||||||
|
private boolean indicator;
|
||||||
|
private int port;
|
||||||
|
private Set<File> sources = new HashSet<>();
|
||||||
|
private boolean autoReload;
|
||||||
|
private String proxyUrl;
|
||||||
|
private String proxyPath;
|
||||||
|
private int processMemory;
|
||||||
|
private int debugPort;
|
||||||
|
|
||||||
|
private Process process;
|
||||||
|
private Thread processKillHook;
|
||||||
|
private Thread commandInputThread;
|
||||||
|
private Thread stderrThread;
|
||||||
|
private BufferedWriter commandOutput;
|
||||||
|
private JsonParser jsonParser;
|
||||||
|
private BlockingQueue<Runnable> eventQueue = new LinkedBlockingQueue<>();
|
||||||
|
private boolean eventQueueDone;
|
||||||
|
private Logger logger;
|
||||||
|
private ProgressLogger progressLogger;
|
||||||
|
|
||||||
|
private Set<File> runningServerClasspath = new HashSet<>();
|
||||||
|
private Set<File> runningClasspath = new HashSet<>();
|
||||||
|
private String runningTargetFileName;
|
||||||
|
private String runningTargetFilePath;
|
||||||
|
private Map<String, String> runningProperties = new HashMap<>();
|
||||||
|
private Set<String> runningPreservedClasses = new HashSet<>();
|
||||||
|
private String runningMainClass;
|
||||||
|
private boolean runningStackDeobfuscated;
|
||||||
|
private boolean runningIndicator;
|
||||||
|
private int runningPort;
|
||||||
|
private Set<File> runningSources = new HashSet<>();
|
||||||
|
private boolean runningAutoReload;
|
||||||
|
private String runningProxyUrl;
|
||||||
|
private String runningProxyPath;
|
||||||
|
private int runningProcessMemory;
|
||||||
|
private int runningDebugPort;
|
||||||
|
|
||||||
|
ProjectDevServerManager() {
|
||||||
|
jsonParser = JsonParser.ofValue(this::parseCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerClasspath(Set<File> serverClasspath) {
|
||||||
|
this.serverClasspath.clear();
|
||||||
|
this.serverClasspath.addAll(serverClasspath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClasspath(Set<File> classpath) {
|
||||||
|
this.classpath.clear();
|
||||||
|
this.classpath.addAll(classpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProperties(Map<String, String> properties) {
|
||||||
|
this.properties.clear();
|
||||||
|
this.properties.putAll(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreservedClasses(Collection<String> preservedClasses) {
|
||||||
|
this.preservedClasses.clear();
|
||||||
|
this.preservedClasses.addAll(preservedClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetFileName(String targetFileName) {
|
||||||
|
this.targetFileName = targetFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetFilePath(String targetFilePath) {
|
||||||
|
this.targetFilePath = targetFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMainClass(String mainClass) {
|
||||||
|
this.mainClass = mainClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStackDeobfuscated(boolean stackDeobfuscated) {
|
||||||
|
this.stackDeobfuscated = stackDeobfuscated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndicator(boolean indicator) {
|
||||||
|
this.indicator = indicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSources(Set<File> sources) {
|
||||||
|
this.sources.clear();
|
||||||
|
this.sources.addAll(sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoReload(boolean autoReload) {
|
||||||
|
this.autoReload = autoReload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyUrl(String proxyUrl) {
|
||||||
|
this.proxyUrl = proxyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyPath(String proxyPath) {
|
||||||
|
this.proxyPath = proxyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProcessMemory(int processMemory) {
|
||||||
|
this.processMemory = processMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebugPort(int debugPort) {
|
||||||
|
this.debugPort = debugPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runBuild(Logger logger, ProgressLogger progressLogger) throws IOException {
|
||||||
|
restartIfNecessary(logger);
|
||||||
|
try {
|
||||||
|
schedule(() -> {
|
||||||
|
try {
|
||||||
|
commandOutput.write("{\"type\":\"build\"}\n");
|
||||||
|
commandOutput.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processQueue(logger, progressLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processQueue(Logger logger, ProgressLogger progressLogger) {
|
||||||
|
eventQueueDone = false;
|
||||||
|
this.logger = logger;
|
||||||
|
this.progressLogger = progressLogger;
|
||||||
|
var stoppedUnexpectedly = new boolean[1];
|
||||||
|
var processMonitorThread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
process.waitFor();
|
||||||
|
schedule(() -> {
|
||||||
|
stoppedUnexpectedly[0] = true;
|
||||||
|
});
|
||||||
|
stopEventQueue();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
processMonitorThread.setDaemon(true);
|
||||||
|
processMonitorThread.setName("Dev server process crash monitor");
|
||||||
|
processMonitorThread.start();
|
||||||
|
try {
|
||||||
|
while (!eventQueueDone || !eventQueue.isEmpty()) {
|
||||||
|
Runnable command;
|
||||||
|
try {
|
||||||
|
command = eventQueue.take();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
command.run();
|
||||||
|
}
|
||||||
|
if (stoppedUnexpectedly[0]) {
|
||||||
|
logger.error("Dev server process stopped unexpectedly");
|
||||||
|
throw new GradleException();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.logger = null;
|
||||||
|
this.progressLogger = null;
|
||||||
|
processMonitorThread.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restartIfNecessary(Logger logger) throws IOException {
|
||||||
|
if (process != null && !checkProcess()) {
|
||||||
|
logger.info("Changes detected in TeaVM development server config, restarting server");
|
||||||
|
stop(logger);
|
||||||
|
}
|
||||||
|
if (process == null || !process.isAlive()) {
|
||||||
|
start(logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(Logger logger) {
|
||||||
|
if (process != null) {
|
||||||
|
logger.info("Stopping TeaVM development server, PID = {}", process.pid());
|
||||||
|
if (process.isAlive()) {
|
||||||
|
try {
|
||||||
|
commandOutput.write("{\"type\":\"stop\"}\n");
|
||||||
|
commandOutput.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
process.destroy();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
process.waitFor();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("Process was dead");
|
||||||
|
}
|
||||||
|
process = null;
|
||||||
|
Runtime.getRuntime().removeShutdownHook(processKillHook);
|
||||||
|
processKillHook = null;
|
||||||
|
commandInputThread.interrupt();
|
||||||
|
commandInputThread = null;
|
||||||
|
stderrThread.interrupt();
|
||||||
|
stderrThread = null;
|
||||||
|
commandOutput = null;
|
||||||
|
} else {
|
||||||
|
logger.info("No development server running, doing nothing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start(Logger logger) throws IOException {
|
||||||
|
logger.info("Starting TeaVM development server");
|
||||||
|
|
||||||
|
var pb = new ProcessBuilder();
|
||||||
|
pb.command(getBuilderCommand().toArray(new String[0]));
|
||||||
|
|
||||||
|
process = pb.start();
|
||||||
|
processKillHook = new Thread(() -> process.destroy());
|
||||||
|
Runtime.getRuntime().addShutdownHook(processKillHook);
|
||||||
|
commandOutput = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
commandInputThread = new Thread(this::readCommandsFromProcess);
|
||||||
|
commandInputThread.setName("TeaVM development server command reader");
|
||||||
|
commandInputThread.setDaemon(true);
|
||||||
|
commandInputThread.start();
|
||||||
|
|
||||||
|
stderrThread = new Thread(this::readStderrFromProcess);
|
||||||
|
stderrThread.setName("TeaVM development server stderr reader");
|
||||||
|
stderrThread.setDaemon(true);
|
||||||
|
stderrThread.start();
|
||||||
|
|
||||||
|
logger.info("Development server started");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readCommandsFromProcess() {
|
||||||
|
try (var input = new BufferedReader(new InputStreamReader(process.getInputStream(),
|
||||||
|
StandardCharsets.UTF_8))) {
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
var command = input.readLine();
|
||||||
|
if (command == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
schedule(() -> readCommand(command));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
try {
|
||||||
|
stopEventQueue();
|
||||||
|
if (logger != null) {
|
||||||
|
logger.error("IO error occurred reading stdout of development server process", e);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e2) {
|
||||||
|
if (logger != null) {
|
||||||
|
logger.info("Development server process input thread interrupted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
if (logger != null) {
|
||||||
|
logger.info("Development server process input thread interrupted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readStderrFromProcess() {
|
||||||
|
try (var input = new BufferedReader(new InputStreamReader(process.getErrorStream(),
|
||||||
|
StandardCharsets.UTF_8))) {
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
var line = input.readLine();
|
||||||
|
if (line == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
schedule(() -> logger.warn("server stderr: {}", line));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (logger != null) {
|
||||||
|
logger.error("IO error occurred reading stderr of development server process", e);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
if (logger != null) {
|
||||||
|
logger.info("Development server process input thread interrupted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopEventQueue() throws InterruptedException {
|
||||||
|
schedule(() -> eventQueueDone = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void schedule(Runnable command) throws InterruptedException {
|
||||||
|
eventQueue.put(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readCommand(String command) {
|
||||||
|
try {
|
||||||
|
jsonParser.parse(new StringReader(command));
|
||||||
|
} catch (IOException e) {
|
||||||
|
// This should not happen
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw new RuntimeException("Error reading command: " + command, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseCommand(JsonValue command) {
|
||||||
|
var obj = (JsonObjectValue) command;
|
||||||
|
var type = obj.get("type").asString();
|
||||||
|
try {
|
||||||
|
switch (type) {
|
||||||
|
case "log":
|
||||||
|
logCommand(obj);
|
||||||
|
break;
|
||||||
|
case "compilation-started":
|
||||||
|
// do nothing
|
||||||
|
break;
|
||||||
|
case "compilation-progress":
|
||||||
|
progressCommand(obj);
|
||||||
|
break;
|
||||||
|
case "compilation-complete":
|
||||||
|
completeCommand(obj);
|
||||||
|
break;
|
||||||
|
case "compilation-cancelled":
|
||||||
|
stopEventQueue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logCommand(JsonObjectValue command) throws InterruptedException {
|
||||||
|
if (logger == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var level = command.get("level").asString();
|
||||||
|
var message = command.get("message").asString();
|
||||||
|
var throwable = command.get("throwable");
|
||||||
|
if (throwable != null) {
|
||||||
|
message += "\n" + throwable.asString();
|
||||||
|
}
|
||||||
|
var messageToReport = message;
|
||||||
|
switch (level) {
|
||||||
|
case "debug":
|
||||||
|
schedule(() -> logger.debug(messageToReport));
|
||||||
|
break;
|
||||||
|
case "info":
|
||||||
|
schedule(() -> logger.info(messageToReport));
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
schedule(() -> logger.warn(messageToReport));
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
schedule(() -> logger.error(messageToReport));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void progressCommand(JsonObjectValue command) throws InterruptedException {
|
||||||
|
if (progressLogger == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var progress = command.get("progress").asNumber();
|
||||||
|
var roundedResult = (int) (progress * 1000 + 5) / 10;
|
||||||
|
var result = Math.min(100, roundedResult / 10.0);
|
||||||
|
schedule(() -> progressLogger.progress(result + " %"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void completeCommand(JsonObjectValue command) throws InterruptedException {
|
||||||
|
var problemsJson = command.get("problems");
|
||||||
|
if (problemsJson != null && logger != null) {
|
||||||
|
reportProblems((JsonArrayValue) problemsJson);
|
||||||
|
}
|
||||||
|
stopEventQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reportProblems(JsonArrayValue json) throws InterruptedException {
|
||||||
|
var hasSevere = false;
|
||||||
|
for (var i = 0; i < json.size(); ++i) {
|
||||||
|
var problem = json.get(i).asObject();
|
||||||
|
var severity = problem.get("severity").asString();
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append(problem.get("message").asString());
|
||||||
|
sb.append(problem.get("location").asString());
|
||||||
|
var message = sb.toString();
|
||||||
|
switch (severity) {
|
||||||
|
case "error":
|
||||||
|
hasSevere = true;
|
||||||
|
schedule(() -> logger.error(message));
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
schedule(() -> logger.warn(message));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasSevere) {
|
||||||
|
schedule(() -> {
|
||||||
|
throw new GradleException("Errors occurred during TeaVM build");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getBuilderCommand() {
|
||||||
|
var command = new ArrayList<String>();
|
||||||
|
|
||||||
|
var javaHome = System.getProperty("java.home");
|
||||||
|
var javaExec = javaHome + "/bin/java";
|
||||||
|
if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
|
||||||
|
javaExec += ".exe";
|
||||||
|
}
|
||||||
|
command.add(javaExec);
|
||||||
|
|
||||||
|
if (!serverClasspath.isEmpty()) {
|
||||||
|
command.add("-cp");
|
||||||
|
command.add(serverClasspath.stream()
|
||||||
|
.map(File::getAbsolutePath)
|
||||||
|
.collect(Collectors.joining(File.pathSeparator)));
|
||||||
|
}
|
||||||
|
runningServerClasspath.clear();
|
||||||
|
runningServerClasspath.addAll(serverClasspath);
|
||||||
|
|
||||||
|
if (processMemory != 0) {
|
||||||
|
command.add("-Xmx" + processMemory + "m");
|
||||||
|
}
|
||||||
|
runningProcessMemory = processMemory;
|
||||||
|
|
||||||
|
if (debugPort != 0) {
|
||||||
|
command.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=*:" + debugPort);
|
||||||
|
}
|
||||||
|
runningDebugPort = debugPort;
|
||||||
|
|
||||||
|
command.add("org.teavm.cli.devserver.TeaVMDevServerRunner");
|
||||||
|
command.add("--json-interface");
|
||||||
|
command.add("--no-watch");
|
||||||
|
|
||||||
|
if (targetFileName != null) {
|
||||||
|
command.add("--targetfile");
|
||||||
|
command.add(targetFileName);
|
||||||
|
}
|
||||||
|
runningTargetFileName = targetFileName;
|
||||||
|
|
||||||
|
if (targetFilePath != null) {
|
||||||
|
command.add("--targetdir");
|
||||||
|
command.add(targetFilePath);
|
||||||
|
}
|
||||||
|
runningTargetFilePath = targetFilePath;
|
||||||
|
|
||||||
|
if (!classpath.isEmpty()) {
|
||||||
|
command.add("--classpath");
|
||||||
|
command.addAll(classpath.stream()
|
||||||
|
.map(File::getAbsolutePath)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
runningClasspath.clear();
|
||||||
|
runningClasspath.addAll(classpath);
|
||||||
|
|
||||||
|
if (!sources.isEmpty()) {
|
||||||
|
command.add("--sourcepath");
|
||||||
|
command.addAll(sources.stream()
|
||||||
|
.map(File::getAbsolutePath)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
runningSources.clear();
|
||||||
|
runningSources.addAll(sources);
|
||||||
|
|
||||||
|
if (port != 0) {
|
||||||
|
command.add("--port");
|
||||||
|
command.add(String.valueOf(port));
|
||||||
|
}
|
||||||
|
runningPort = port;
|
||||||
|
|
||||||
|
if (indicator) {
|
||||||
|
command.add("--indicator");
|
||||||
|
}
|
||||||
|
runningIndicator = indicator;
|
||||||
|
|
||||||
|
if (stackDeobfuscated) {
|
||||||
|
command.add("--deobfuscate-stack");
|
||||||
|
}
|
||||||
|
runningStackDeobfuscated = stackDeobfuscated;
|
||||||
|
|
||||||
|
if (autoReload) {
|
||||||
|
command.add("--auto-reload");
|
||||||
|
}
|
||||||
|
runningAutoReload = autoReload;
|
||||||
|
|
||||||
|
if (proxyUrl != null) {
|
||||||
|
command.add("--proxy-url");
|
||||||
|
command.add(proxyUrl);
|
||||||
|
}
|
||||||
|
runningProxyUrl = proxyUrl;
|
||||||
|
|
||||||
|
if (proxyPath != null) {
|
||||||
|
command.add("--proxy-path");
|
||||||
|
command.add(proxyPath);
|
||||||
|
}
|
||||||
|
runningProxyPath = proxyPath;
|
||||||
|
|
||||||
|
for (var entry : properties.entrySet()) {
|
||||||
|
command.add("--property");
|
||||||
|
command.add(entry.getKey() + "=" + entry.getValue());
|
||||||
|
}
|
||||||
|
runningProperties.clear();
|
||||||
|
runningProperties.putAll(properties);
|
||||||
|
|
||||||
|
if (!preservedClasses.isEmpty()) {
|
||||||
|
command.add("--preserved-classes");
|
||||||
|
command.addAll(preservedClasses);
|
||||||
|
}
|
||||||
|
runningPreservedClasses.clear();
|
||||||
|
runningPreservedClasses.addAll(preservedClasses);
|
||||||
|
|
||||||
|
command.add("--");
|
||||||
|
command.add(mainClass);
|
||||||
|
runningMainClass = mainClass;
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkProcess() {
|
||||||
|
return Objects.equals(serverClasspath, runningServerClasspath)
|
||||||
|
&& Objects.equals(classpath, runningClasspath)
|
||||||
|
&& Objects.equals(targetFileName, runningTargetFileName)
|
||||||
|
&& Objects.equals(targetFilePath, runningTargetFilePath)
|
||||||
|
&& Objects.equals(properties, runningProperties)
|
||||||
|
&& Objects.equals(preservedClasses, runningPreservedClasses)
|
||||||
|
&& Objects.equals(mainClass, runningMainClass)
|
||||||
|
&& stackDeobfuscated == runningStackDeobfuscated
|
||||||
|
&& indicator == runningIndicator
|
||||||
|
&& port == runningPort
|
||||||
|
&& Objects.equals(sources, runningSources)
|
||||||
|
&& autoReload == runningAutoReload
|
||||||
|
&& Objects.equals(proxyUrl, runningProxyUrl)
|
||||||
|
&& Objects.equals(proxyPath, runningProxyPath)
|
||||||
|
&& processMemory == runningProcessMemory
|
||||||
|
&& debugPort == runningDebugPort;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.gradle.tasks;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
public abstract class StopJavaScriptDevServerTask extends DefaultTask {
|
||||||
|
@TaskAction
|
||||||
|
public void stopServer() {
|
||||||
|
var codeServerManager = DevServerManager.instance();
|
||||||
|
codeServerManager.cleanup(getProject().getGradle());
|
||||||
|
var pm = codeServerManager.getProjectManager(getProject().getPath());
|
||||||
|
pm.stop(getLogger());
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,7 +52,6 @@ import org.teavm.vm.TeaVMProgressListener;
|
||||||
|
|
||||||
public abstract class TeaVMTask extends DefaultTask {
|
public abstract class TeaVMTask extends DefaultTask {
|
||||||
public TeaVMTask() {
|
public TeaVMTask() {
|
||||||
setGroup("TeaVM");
|
|
||||||
getDebugInformation().convention(false);
|
getDebugInformation().convention(false);
|
||||||
getTargetFileName().convention("bundle");
|
getTargetFileName().convention("bundle");
|
||||||
getOptimization().convention(OptimizationLevel.BALANCED);
|
getOptimization().convention(OptimizationLevel.BALANCED);
|
||||||
|
|
|
@ -22,10 +22,10 @@ plugins {
|
||||||
description = "All-in one JAR file that used by IDE plugins"
|
description = "All-in one JAR file that used by IDE plugins"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(path = ":tools:core"))
|
api(project(":tools:core"))
|
||||||
implementation(project(path = ":tools:devserver"))
|
api(project(":tools:devserver"))
|
||||||
implementation(project(path = ":classlib"))
|
api(project(":classlib"))
|
||||||
implementation(project(path = ":tools:chrome-rdp"))
|
api(project(":tools:chrome-rdp"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.shadowJar {
|
tasks.shadowJar {
|
||||||
|
|
|
@ -32,7 +32,8 @@ intellij {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(path = ":tools:ide-deps", configuration = "shadow").setTransitive(false))
|
compileOnly(project(":tools:ide-deps"))
|
||||||
|
runtimeOnly(project(path = ":tools:ide-deps", configuration = "shadow").setTransitive(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
|
|
@ -153,6 +153,7 @@ public class DevServerRunner extends UnicastRemoteObject implements DevServerMan
|
||||||
DevServerRunner daemon = new DevServerRunner(server);
|
DevServerRunner daemon = new DevServerRunner(server);
|
||||||
System.out.println(PORT_MESSAGE_PREFIX + daemon.port);
|
System.out.println(PORT_MESSAGE_PREFIX + daemon.port);
|
||||||
server.start();
|
server.start();
|
||||||
|
server.awaitServer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
daemon.registry.unbind(ID);
|
daemon.registry.unbind(ID);
|
||||||
|
@ -327,7 +328,9 @@ public class DevServerRunner extends UnicastRemoteObject implements DevServerMan
|
||||||
@Override
|
@Override
|
||||||
public void compilationComplete(BuildResult buildResult) {
|
public void compilationComplete(BuildResult buildResult) {
|
||||||
DevServerBuildResult result = new DevServerBuildResult();
|
DevServerBuildResult result = new DevServerBuildResult();
|
||||||
|
if (buildResult != null) {
|
||||||
result.problems.addAll(buildResult.getProblems().getProblems());
|
result.problems.addAll(buildResult.getProblems().getProblems());
|
||||||
|
}
|
||||||
for (DevServerManagerListener listener : getListeners()) {
|
for (DevServerManagerListener listener : getListeners()) {
|
||||||
try {
|
try {
|
||||||
listener.compilationComplete(result);
|
listener.compilationComplete(result);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user