js: add ability so refer to local files in source maps. Add copying of source files to Gradle plugin.

This commit is contained in:
Alexey Andreev 2023-11-24 21:28:37 +01:00
parent 6543e68f8a
commit ac2c084290
18 changed files with 297 additions and 40 deletions

View File

@ -0,0 +1,22 @@
/*
* Copyright 2023 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.debugging.information;
import java.io.IOException;
public interface SourceFileResolver {
String resolveFile(String fileName) throws IOException;
}

View File

@ -17,9 +17,11 @@ package org.teavm.debugging.information;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import org.teavm.common.JsonUtil;
class SourceMapsWriter {
public class SourceMapsWriter {
private static final String BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private Writer output;
private int lastLine;
@ -27,11 +29,16 @@ class SourceMapsWriter {
private int lastSourceLine;
private int lastSourceFile;
private boolean first;
private List<SourceFileResolver> sourceFileResolvers = new ArrayList<>();
public SourceMapsWriter(Writer output) {
this.output = output;
}
public void addSourceResolver(SourceFileResolver sourceFileResolver) {
sourceFileResolvers.add(sourceFileResolver);
}
public void write(String generatedFile, String sourceRoot, DebugInformation debugInfo) throws IOException {
output.write("{\"version\":3");
output.write(",\"file\":\"");
@ -46,7 +53,15 @@ class SourceMapsWriter {
output.write(',');
}
output.write("\"");
JsonUtil.writeEscapedString(output, debugInfo.fileNames[i]);
var name = debugInfo.fileNames[i];
for (var resolver : sourceFileResolvers) {
var resolvedName = resolver.resolveFile(name);
if (resolvedName != null) {
name = resolvedName;
break;
}
}
JsonUtil.writeEscapedString(output, name);
output.write("\"");
}
output.write("]");

View File

@ -1,3 +1,5 @@
import org.teavm.gradle.api.SourceFilePolicy
/*
* Copyright 2023 Alexey Andreev.
*
@ -28,4 +30,6 @@ dependencies {
teavm.js {
addedToWebApp = true
mainClass = "org.teavm.samples.hello.Client"
sourceMap = true
sourceFilePolicy = SourceFilePolicy.LINK_LOCAL_FILES
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2023 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.tooling;
public enum TeaVMSourceFilePolicy {
DO_NOTHING,
COPY,
LINK_LOCAL_FILES
}

View File

@ -49,6 +49,7 @@ import org.teavm.cache.EmptyProgramCache;
import org.teavm.cache.FileSymbolTable;
import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.information.DebugInformationBuilder;
import org.teavm.debugging.information.SourceMapsWriter;
import org.teavm.dependency.DependencyInfo;
import org.teavm.dependency.FastDependencyAnalyzer;
import org.teavm.dependency.PreciseDependencyAnalyzer;
@ -61,7 +62,6 @@ import org.teavm.model.ReferenceCache;
import org.teavm.model.transformation.AssertionRemoval;
import org.teavm.parsing.ClasspathClassHolderSource;
import org.teavm.tooling.sources.SourceFileProvider;
import org.teavm.tooling.sources.SourceFilesCopier;
import org.teavm.vm.BuildTarget;
import org.teavm.vm.DirectoryBuildTarget;
import org.teavm.vm.TeaVM;
@ -81,7 +81,7 @@ public class TeaVMTool {
private Properties properties = new Properties();
private boolean debugInformationGenerated;
private boolean sourceMapsFileGenerated;
private boolean sourceFilesCopied;
private TeaVMSourceFilePolicy sourceFilePolicy = TeaVMSourceFilePolicy.DO_NOTHING;
private boolean incremental;
private File cacheDirectory = new File("./teavm-cache");
private List<String> transformers = new ArrayList<>();
@ -177,12 +177,23 @@ public class TeaVMTool {
this.sourceMapsFileGenerated = sourceMapsFileGenerated;
}
@Deprecated
public boolean isSourceFilesCopied() {
return sourceFilesCopied;
return sourceFilePolicy == TeaVMSourceFilePolicy.COPY;
}
@Deprecated
public void setSourceFilesCopied(boolean sourceFilesCopied) {
this.sourceFilesCopied = sourceFilesCopied;
if (isSourceFilesCopied() == sourceFilesCopied) {
return;
}
sourceFilePolicy = sourceFilesCopied
? TeaVMSourceFilePolicy.COPY
: TeaVMSourceFilePolicy.DO_NOTHING;
}
public void setSourceFilePolicy(TeaVMSourceFilePolicy sourceFilePolicy) {
this.sourceFilePolicy = sourceFilePolicy;
}
public Properties getProperties() {
@ -527,14 +538,48 @@ public class TeaVMTool {
File sourceMapsFile = new File(targetDirectory, sourceMapsFileName);
try (Writer sourceMapsOut = new OutputStreamWriter(new FileOutputStream(sourceMapsFile),
StandardCharsets.UTF_8)) {
debugInfo.writeAsSourceMaps(sourceMapsOut, "src", getResolvedTargetFileName());
writeSourceMaps(sourceMapsOut, debugInfo);
}
generatedFiles.add(sourceMapsFile);
log.info("Source maps successfully written");
}
if (sourceFilesCopied) {
copySourceFiles();
log.info("Source files successfully written");
}
private void writeSourceMaps(Writer out, DebugInformation debugInfo) throws IOException {
var sourceMapWriter = new SourceMapsWriter(out);
for (var provider : sourceFileProviders) {
provider.open();
}
var targetDir = new File(targetDirectory, "src");
if (sourceFilePolicy != TeaVMSourceFilePolicy.DO_NOTHING) {
sourceMapWriter.addSourceResolver(fileName -> {
for (var provider : sourceFileProviders) {
var sourceFile = provider.getSourceFile(fileName);
if (sourceFile != null) {
if (sourceFilePolicy == TeaVMSourceFilePolicy.COPY || sourceFile.getFile() == null) {
var outputFile = new File(targetDir, fileName);
outputFile.getParentFile().mkdirs();
try (var input = sourceFile.open();
var output = new FileOutputStream(outputFile)) {
input.transferTo(output);
}
if (sourceFilePolicy == TeaVMSourceFilePolicy.LINK_LOCAL_FILES) {
return "file://" + outputFile.getCanonicalPath();
}
} else {
return "file://" + sourceFile.getFile().getCanonicalPath();
}
break;
}
}
return null;
});
}
sourceMapWriter.write(getResolvedTargetFileName(), "src", debugInfo);
for (var provider : sourceFileProviders) {
provider.close();
}
}
@ -554,16 +599,6 @@ public class TeaVMTool {
log.info("Methods compiled: " + methodCount);
}
private void copySourceFiles() {
if (vm.getWrittenClasses() == null) {
return;
}
SourceFilesCopier copier = new SourceFilesCopier(sourceFileProviders, generatedFiles::add);
copier.addClasses(vm.getWrittenClasses());
copier.setLog(log);
copier.copy(new File(targetDirectory, "src"));
}
private List<ClassHolderTransformer> resolveTransformers() {
List<ClassHolderTransformer> transformerInstances = new ArrayList<>();
if (transformers == null) {

View File

@ -18,6 +18,7 @@ package org.teavm.tooling.builder;
import java.util.List;
import java.util.Properties;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.TeaVMSourceFilePolicy;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.TeaVMToolLog;
import org.teavm.vm.TeaVMOptimizationLevel;
@ -46,8 +47,11 @@ public interface BuildStrategy {
void setDebugInformationGenerated(boolean debugInformationGenerated);
@Deprecated
void setSourceFilesCopied(boolean sourceFilesCopied);
void setSourceFilePolicy(TeaVMSourceFilePolicy sourceFilePolicy);
void setProgressListener(TeaVMProgressListener progressListener);
void setIncremental(boolean incremental);

View File

@ -30,6 +30,7 @@ import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.ProblemProvider;
import org.teavm.tooling.EmptyTeaVMToolLog;
import org.teavm.tooling.TeaVMSourceFilePolicy;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.TeaVMTool;
import org.teavm.tooling.TeaVMToolException;
@ -55,7 +56,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
private boolean strict;
private boolean sourceMapsFileGenerated;
private boolean debugInformationGenerated;
private boolean sourceFilesCopied;
private TeaVMSourceFilePolicy sourceMapsSourcePolicy;
private String[] transformers = new String[0];
private String[] classesToPreserve = new String[0];
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
@ -122,7 +123,17 @@ public class InProcessBuildStrategy implements BuildStrategy {
@Override
public void setSourceFilesCopied(boolean sourceFilesCopied) {
this.sourceFilesCopied = sourceFilesCopied;
if ((sourceMapsSourcePolicy == TeaVMSourceFilePolicy.COPY) == sourceFilesCopied) {
return;
}
sourceMapsSourcePolicy = sourceFilesCopied
? TeaVMSourceFilePolicy.COPY
: TeaVMSourceFilePolicy.DO_NOTHING;
}
@Override
public void setSourceFilePolicy(TeaVMSourceFilePolicy sourceFilePolicy) {
this.sourceMapsSourcePolicy = sourceFilePolicy;
}
@Override
@ -233,7 +244,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
tool.setSourceMapsFileGenerated(sourceMapsFileGenerated);
tool.setDebugInformationGenerated(debugInformationGenerated);
tool.setSourceFilesCopied(sourceFilesCopied);
tool.setSourceFilePolicy(sourceMapsSourcePolicy);
tool.setObfuscated(obfuscated);
tool.setStrict(strict);

View File

@ -25,6 +25,7 @@ import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.Problem;
import org.teavm.diagnostics.ProblemProvider;
import org.teavm.tooling.EmptyTeaVMToolLog;
import org.teavm.tooling.TeaVMSourceFilePolicy;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.TeaVMToolLog;
import org.teavm.tooling.daemon.RemoteBuildCallback;
@ -100,7 +101,14 @@ public class RemoteBuildStrategy implements BuildStrategy {
@Override
public void setSourceFilesCopied(boolean sourceFilesCopied) {
request.sourceFilesCopied = sourceFilesCopied;
request.sourceFilePolicy = sourceFilesCopied
? TeaVMSourceFilePolicy.COPY.name()
: TeaVMSourceFilePolicy.DO_NOTHING.name();
}
@Override
public void setSourceFilePolicy(TeaVMSourceFilePolicy sourceFilePolicy) {
request.sourceFilePolicy = sourceFilePolicy.name();
}
@Override

View File

@ -37,6 +37,7 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.teavm.tooling.TeaVMSourceFilePolicy;
import org.teavm.tooling.TeaVMTool;
import org.teavm.tooling.TeaVMToolException;
import org.teavm.tooling.sources.DirectorySourceFileProvider;
@ -147,7 +148,9 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
tool.setSourceMapsFileGenerated(request.sourceMapsFileGenerated);
tool.setDebugInformationGenerated(request.debugInformationGenerated);
tool.setSourceFilesCopied(request.sourceFilesCopied);
if (request.sourceFilePolicy != null) {
tool.setSourceFilePolicy(TeaVMSourceFilePolicy.valueOf(request.sourceFilePolicy));
}
if (request.properties != null) {
tool.getProperties().putAll(request.properties);
}

View File

@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.TeaVMSourceFilePolicy;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.vm.TeaVMOptimizationLevel;
@ -36,7 +37,7 @@ public class RemoteBuildRequest implements Serializable {
public String tagetFileName = "";
public boolean sourceMapsFileGenerated;
public boolean debugInformationGenerated;
public boolean sourceFilesCopied;
public String sourceFilePolicy = TeaVMSourceFilePolicy.DO_NOTHING.name();
public boolean incremental;
public String cacheDirectory;
public boolean obfuscated;

View File

@ -38,7 +38,7 @@ public class DirectorySourceFileProvider implements SourceFileProvider {
@Override
public SourceFileInfo getSourceFile(String fullPath) throws IOException {
File file = new File(baseDirectory, fullPath);
return file.exists() ? new DirectorySourceFile(file) : null;
return file.isFile() ? new DirectorySourceFile(file) : null;
}
static class DirectorySourceFile implements SourceFileInfo {
@ -57,5 +57,10 @@ public class DirectorySourceFileProvider implements SourceFileProvider {
public InputStream open() throws IOException {
return new FileInputStream(file);
}
@Override
public File getFile() {
return file;
}
}
}

View File

@ -80,5 +80,10 @@ public class JarSourceFileProvider implements SourceFileProvider {
public InputStream open() throws IOException {
return file.getInputStream(entry);
}
@Override
public File getFile() {
return null;
}
}
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.tooling.sources;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -22,4 +23,6 @@ public interface SourceFileInfo {
long lastModified();
InputStream open() throws IOException;
File getFile();
}

View File

@ -16,10 +16,16 @@
package org.teavm.gradle;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.artifacts.result.ResolvedDependencyResult;
import org.gradle.api.file.DuplicatesStrategy;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaPlugin;
@ -83,7 +89,8 @@ public class TeaVMPlugin implements Plugin<Project> {
}
private void registerSourceSet(Project project) {
var sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
var sourceSets = project.getExtensions().findByType(SourceSetContainer.class);
if (sourceSets != null) {
var sourceSet = sourceSets.create(SOURCE_SET_NAME);
var main = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput();
sourceSet.setRuntimeClasspath(sourceSet.getRuntimeClasspath().plus(main)
@ -93,6 +100,7 @@ public class TeaVMPlugin implements Plugin<Project> {
sourceSet.java(java -> { });
project.getDependencies().add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, sourceSet.getOutput());
}
}
private void registerTasks(Project project) {
var compilerConfig = project.getConfigurations().detachedConfiguration(
@ -113,9 +121,59 @@ public class TeaVMPlugin implements Plugin<Project> {
task.getTargetFileName().convention(js.getTargetFileName());
task.getStrict().convention(js.getStrict());
task.getEntryPointName().convention(js.getEntryPointName());
task.getSourceFilePolicy().convention(js.getSourceFilePolicy());
task.getSourceFiles().from(project.provider(() -> {
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>();
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);
result.addAll(tmpConfig.getFiles());
}
}
return result;
}));
});
}
private void addSourceDirs(Project project, List<File> result) {
var sourceSets = project.getExtensions().findByType(SourceSetContainer.class);
if (sourceSets != null) {
for (var sourceSet : sourceSets) {
result.addAll(sourceSet.getAllJava().getSourceDirectories().getFiles());
}
}
}
private void registerWasmTask(Project project, Configuration configuration) {
var extension = project.getExtensions().getByType(TeaVMExtension.class);
project.getTasks().create(WASM_TASK_NAME, GenerateWasmTask.class, task -> {
@ -202,8 +260,10 @@ public class TeaVMPlugin implements Plugin<Project> {
var project = task.getProject();
var sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
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());
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2023 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;
public enum SourceFilePolicy {
DO_NOTHING,
COPY,
LINK_LOCAL_FILES
}

View File

@ -27,4 +27,6 @@ public interface TeaVMJSConfiguration extends TeaVMWebConfiguration {
Property<String> getEntryPointName();
Property<String> getTargetFileName();
Property<SourceFilePolicy> getSourceFilePolicy();
}

View File

@ -15,9 +15,13 @@
*/
package org.teavm.gradle.tasks;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Optional;
import org.teavm.gradle.api.SourceFilePolicy;
import org.teavm.tooling.TeaVMSourceFilePolicy;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.builder.BuildStrategy;
@ -26,6 +30,7 @@ public abstract class GenerateJavaScriptTask extends TeaVMTask {
getObfuscated().convention(true);
getStrict().convention(false);
getSourceMap().convention(false);
getSourceFilePolicy().convention(SourceFilePolicy.DO_NOTHING);
getEntryPointName().convention("main");
}
@ -45,6 +50,13 @@ public abstract class GenerateJavaScriptTask extends TeaVMTask {
@Optional
public abstract Property<String> getEntryPointName();
@InputFiles
public abstract ConfigurableFileCollection getSourceFiles();
@Input
@Optional
public abstract Property<SourceFilePolicy> getSourceFilePolicy();
@Override
protected void setupBuilder(BuildStrategy builder) {
builder.setTargetType(TeaVMTargetType.JAVASCRIPT);
@ -52,5 +64,25 @@ public abstract class GenerateJavaScriptTask extends TeaVMTask {
builder.setStrict(getStrict().get());
builder.setSourceMapsFileGenerated(getSourceMap().get());
builder.setEntryPointName(getEntryPointName().get());
for (var file : getSourceFiles()) {
if (file.isFile()) {
if (file.getName().endsWith(".jar") || file.getName().endsWith(".zip")) {
builder.addSourcesJar(file.getAbsolutePath());
}
} else if (file.isDirectory()) {
builder.addSourcesDirectory(file.getAbsolutePath());
}
}
switch (getSourceFilePolicy().get()) {
case DO_NOTHING:
builder.setSourceFilePolicy(TeaVMSourceFilePolicy.DO_NOTHING);
break;
case COPY:
builder.setSourceFilePolicy(TeaVMSourceFilePolicy.COPY);
break;
case LINK_LOCAL_FILES:
builder.setSourceFilePolicy(TeaVMSourceFilePolicy.LINK_LOCAL_FILES);
break;
}
}
}

View File

@ -39,6 +39,7 @@ import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.TeaVMProblemRenderer;
import org.teavm.tooling.TeaVMSourceFilePolicy;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.builder.BuildException;
import org.teavm.tooling.builder.BuildResult;
@ -177,7 +178,9 @@ public class TeaVMCompileMojo extends AbstractMojo {
builder.setIncremental(incremental);
builder.setDebugInformationGenerated(debugInformationGenerated);
builder.setSourceMapsFileGenerated(sourceMapsGenerated);
builder.setSourceFilesCopied(sourceFilesCopied);
builder.setSourceFilePolicy(sourceFilesCopied
? TeaVMSourceFilePolicy.COPY
: TeaVMSourceFilePolicy.DO_NOTHING);
builder.setMinHeapSize(minHeapSize * 1024 * 1024);
builder.setMaxHeapSize(maxHeapSize * 1024 * 1024);
builder.setShortFileNames(shortFileNames);