diff --git a/build-caching/pom.xml b/build-caching/pom.xml index be2323bd..c6beb5cb 100644 --- a/build-caching/pom.xml +++ b/build-caching/pom.xml @@ -25,11 +25,24 @@ guava + + org.jspecify + jspecify + 0.2.0 + + + + org.javassist + javassist + 3.28.0-GA + + + io.methvin directory-watcher - 0.15.0 + 0.15.1 diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/BuildMap.java b/build-caching/src/main/java/com/vertispan/j2cl/build/BuildMap.java index 9395d3bb..b56dc48c 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/BuildMap.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/BuildMap.java @@ -1,43 +1,37 @@ package com.vertispan.j2cl.build; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; +import com.google.common.collect.Streams; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.google.common.base.Splitter; -import com.google.common.collect.Streams; public class BuildMap { - private Project project; + private Project project; - private Map typeInfos = new HashMap<>(); + private Map typeInfos = new HashMap<>(); - private Map pathToQualifiedSourceName = new HashMap<>(); - private Map qualifiedSourceNameToPath = new HashMap<>(); + private Map pathToQualifiedSourceName = new HashMap<>(); + private Map qualifiedSourceNameToPath = new HashMap<>(); - private List childrenChangedFiles = new ArrayList<>();; + private List childrenChangedFiles = new ArrayList<>(); + ; - private Set changedFiles = new HashSet<>();; + private Set changedFiles = new HashSet<>(); + ; - private Set expandedFiles = new HashSet<>(); + private Set expandedFiles = new HashSet<>(); - private List filesToDelete = new ArrayList<>(); + private List filesToDelete = new ArrayList<>(); private Map dirToprojectFiles; @@ -63,7 +57,6 @@ public Map getDirToprojectFiles() { } /** - * * @param dir */ public void build(Path dir) { @@ -72,7 +65,7 @@ public void build(Path dir) { } private void populateFilesToDelete() { - // Merge all except added - which by it's nature has nothing nothing needed deleting + // Merge all except added - which by it's nature has nothing needed deleting for (ProjectFiles p : dirToprojectFiles.values()) { filesToDelete.addAll(p.getRemoved()); filesToDelete.addAll(p.getUpdated()); @@ -86,7 +79,7 @@ private void buildAndProcessChangedFiles(Path dir) { expandChangedFiles(); // Populate the complete list of potentially changed files - for(ProjectFiles projectFiles : dirToprojectFiles.values()) { + for (ProjectFiles projectFiles : dirToprojectFiles.values()) { changedFiles.addAll(projectFiles.getUpdated()); changedFiles.addAll(projectFiles.getAdded()); } @@ -98,17 +91,18 @@ public List getFilesToDelete() { } public void expandChangedFiles() { - for(ProjectFiles projectFiles : dirToprojectFiles.values()) { + for (ProjectFiles projectFiles : dirToprojectFiles.values()) { expandChangedFiles(projectFiles.getUpdated(), expandedFiles); } expandChangedFiles(childrenChangedFiles, expandedFiles); } + public void expandChangedFiles(Collection files, Set expanded) { for (String file : files) { if (!file.endsWith(".java")) { continue; } - String typeName = pathToQualifiedSourceName.get(file); + String typeName = pathToQualifiedSourceName.get(file); TypeInfo typeInfo = typeInfos.get(typeName); expandChangedFiles(typeInfo, expanded); } @@ -116,7 +110,7 @@ public void expandChangedFiles(Collection files, Set expanded) { private void expandChangedFiles(TypeInfo typeInfo, Set changedFiles) { // Anything that extends this is added to the set, and it also recurses through the extends - for (TypeDependency dep : typeInfo.getSuperIn() ) { + for (TypeDependency dep : typeInfo.getSuperIn()) { maybeAddNativeFile(dep.outgoing); changedFiles.add(qualifiedSourceNameToPath.get(dep.outgoing.getQualifiedSourceName())); expandChangedFiles(dep.outgoing, changedFiles); @@ -124,16 +118,18 @@ private void expandChangedFiles(TypeInfo typeInfo, Set changedFiles) { // Anything that implements (or extends) this interface, is added to the set. // TODO Does this need to be done for transitive interface impl? (mdp) - for (TypeDependency dep : typeInfo.getInterfacesIn() ) { + for (TypeDependency dep : typeInfo.getInterfacesIn()) { maybeAddNativeFile(dep.outgoing); changedFiles.add(qualifiedSourceNameToPath.get(dep.outgoing.getQualifiedSourceName())); - // Recurse the ancestors, as the interface may have default methods // that changes the call hieararchy of the implementor. expandChangedFiles(dep.outgoing, changedFiles); } + // Now add all the dependencies + + for (TypeDependency dep : typeInfo.getMethodFieldIn()) { maybeAddNativeFile(dep.outgoing); changedFiles.add(qualifiedSourceNameToPath.get(dep.outgoing.getQualifiedSourceName())); @@ -189,12 +185,11 @@ private void createBuildMaps(Map typeInfoDescrs) { incoming.getInterfacesIn().add(d); } } - // Add dependencies, that are not one of the above. for (String type : typeInfoDescr.dependencies) { TypeInfo incoming = getType(type); if (incoming != null) { - TypeDependency d = new TypeDependency(incoming,outgoing); + TypeDependency d = new TypeDependency(incoming, outgoing); outgoing.getMethodFieldOut().add(d); incoming.getMethodFieldIn().add(d); } @@ -208,7 +203,7 @@ private Map readBuildMapDescrForAllFiles(Path dir, Map readBuildMapDescrForAllFiles(Path dir, Map typeInfoDescrs, Path dir) { + + if (javaFileName.endsWith(".java")) { String fileName = javaFileName.substring(0, javaFileName.lastIndexOf(".java")); String buildMapFileName = fileName + ".build.map"; - Path buildMapPath = dir.resolve("results").resolve(buildMapFileName); + Path buildMapPath = dir.resolve(buildMapFileName); + if (Files.notExists(buildMapPath)) { throw new RuntimeException("build.map files must exist for all changed .java files"); } @@ -305,7 +303,7 @@ String getAndInc() { // skip any empty lines while (lineNbr < lines.size() && - lines.get(lineNbr).trim().isEmpty()) { + lines.get(lineNbr).trim().isEmpty()) { lineNbr++; } @@ -334,7 +332,7 @@ static class TypeInfoDescr { public TypeInfoDescr(String qualifiedSourceName, String qualifiedBinaryName, String nativePathName) { this.qualifiedSourceName = qualifiedSourceName; - this.qualifiedBinaryName = qualifiedBinaryName != null && !qualifiedBinaryName.trim().isEmpty() ? qualifiedBinaryName : qualifiedSourceName; + this.qualifiedBinaryName = qualifiedBinaryName != null && !qualifiedBinaryName.trim().isEmpty() ? qualifiedBinaryName : qualifiedSourceName; this.nativePathName = nativePathName; this.innerTypes = new ArrayList<>(); @@ -346,17 +344,18 @@ public List dependencies() { return dependencies; } - @Override public String toString() { + @Override + public String toString() { return "TypeInfoDescr{" + - "qualifiedSourceName='" + qualifiedSourceName + '\'' + - ", qualifiedBinaryName='" + qualifiedBinaryName + '\'' + - ", superTypeName='" + superTypeName + '\'' + - ", nativePathName='" + nativePathName + '\'' + - ", enclosingType='" + enclosingType + '\'' + - ", innerTypes=" + innerTypes + - ", interfaces=" + interfaces + - ", dependencies=" + dependencies + - '}'; + "qualifiedSourceName='" + qualifiedSourceName + '\'' + + ", qualifiedBinaryName='" + qualifiedBinaryName + '\'' + + ", superTypeName='" + superTypeName + '\'' + + ", nativePathName='" + nativePathName + '\'' + + ", enclosingType='" + enclosingType + '\'' + + ", innerTypes=" + innerTypes + + ", interfaces=" + interfaces + + ", dependencies=" + dependencies + + '}'; } } @@ -374,7 +373,7 @@ TypeInfoDescr readBuildMapSources(LineReader reader, Map } String nativePathName = null; // optional - if (!reader.peekNext().startsWith("-") ) { + if (!reader.peekNext().startsWith("-")) { // native file specified nativePathName = reader.getAndInc(); } @@ -390,7 +389,7 @@ public void readHierarchyAndInnerTypes(LineReader reader, TypeInfoDescr typeInfo if (!line.startsWith("- hierarchy")) { throw new RuntimeException("Illegal File Format, the next element must be '-hierarchy' at line " + reader.lineNbr); } - if (!reader.peekNext().startsWith("-") ) { + if (!reader.peekNext().startsWith("-")) { line = reader.getAndInc(); String[] segments = line.split(":", -1); if (segments.length != 2) { @@ -415,7 +414,7 @@ public void readHierarchyAndInnerTypes(LineReader reader, TypeInfoDescr typeInfo if (!line.startsWith("- innerTypes")) { throw new RuntimeException("Illegal File Format, the next element must be '-innerTypes' at line " + reader.lineNbr); } - if (!reader.peekNext().startsWith("-") ) { + if (!reader.peekNext().startsWith("-")) { String[] innerTypes = reader.getAndInc().split(":", -1); String ext = ".build.map"; @@ -432,9 +431,9 @@ public void readHierarchyAndInnerTypes(LineReader reader, TypeInfoDescr typeInfo // String fileName = innerTypeName.substring(penDot+1) + "$" + innerTypeName.substring(lastDot+1) + ext; String fileName = buildMapPath.getFileName().toString(); - fileName = fileName.substring(0, fileName.length() -ext.length()) + "$" + innerTypeName.substring(lastDot+1) + ext; - - Path innerBuildMapPath = buildMapPath.getParent().resolve( fileName ); + //fileName = fileName.substring(0, fileName.length() -ext.length()) + "$" + innerTypeName.substring(lastDot+1) + ext; + fileName = innerTypeName.substring(lastDot + 1) + ext; + Path innerBuildMapPath = buildMapPath.getParent().resolve(fileName); if (!Files.exists(innerBuildMapPath)) { throw new RuntimeException("InnerType .build.map file must exist: " + innerBuildMapPath); } @@ -447,15 +446,15 @@ public void readHierarchyAndInnerTypes(LineReader reader, TypeInfoDescr typeInfo } void readBuildMapInterfaces(LineReader reader, TypeInfoDescr typeInfoDescr) { - - while( reader.hasNext() && !reader.peekNext().startsWith("- ")) { + + while (reader.hasNext() && !reader.peekNext().startsWith("- ")) { String typeName = reader.getAndInc(); typeInfoDescr.interfaces.add(typeName); } } void readBuildMapDependencies(LineReader reader, TypeInfoDescr typeInfoDescr) { - while(reader.hasNext() && !reader.peekNext().startsWith("-")) { + while (reader.hasNext() && !reader.peekNext().startsWith("-")) { String typeName = reader.getAndInc(); typeInfoDescr.dependencies.add(typeName); } @@ -475,6 +474,7 @@ private void checkFileFormat(String str, int i) { /** * Clone the SourceMap to the TargetMap. Exclude MethodField TypeDependency references, these are not relevant to the parent * projects that consume this BuildMap. + * * @param target */ public void cloneToTargetBuildMap(BuildMap target) { @@ -490,7 +490,7 @@ public void cloneToTargetBuildMap(BuildMap target) { target.pathToQualifiedSourceName.putAll(pathToQualifiedSourceName); target.qualifiedSourceNameToPath.putAll(qualifiedSourceNameToPath); - for(ProjectFiles projectFiles : dirToprojectFiles.values()) { + for (ProjectFiles projectFiles : dirToprojectFiles.values()) { target.childrenChangedFiles.addAll(projectFiles.getUpdated()); } diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/BuildService.java b/build-caching/src/main/java/com/vertispan/j2cl/build/BuildService.java index 618cc8f0..43740251 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/BuildService.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/BuildService.java @@ -1,7 +1,6 @@ package com.vertispan.j2cl.build; import com.vertispan.j2cl.build.impl.CollectedTaskInputs; -import com.vertispan.j2cl.build.task.BuildLog; import com.vertispan.j2cl.build.task.OutputTypes; import com.vertispan.j2cl.build.task.TaskFactory; import org.apache.commons.io.FileUtils; @@ -15,7 +14,14 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; @@ -24,6 +30,8 @@ import java.util.stream.Stream; public class BuildService { + + private Map strippedSources = new HashMap<>(); private final TaskRegistry taskRegistry; private final TaskScheduler taskScheduler; private final DiskCache diskCache; @@ -37,12 +45,18 @@ public class BuildService { private BlockingBuildListener prevBuild; - public BuildService(TaskRegistry taskRegistry, TaskScheduler taskScheduler, DiskCache diskCache) { + private boolean incremental; + + public BuildService(TaskRegistry taskRegistry, TaskScheduler taskScheduler, DiskCache diskCache, boolean incremental) { this.taskRegistry = taskRegistry; this.taskScheduler = taskScheduler; this.diskCache = diskCache; this.diskCache.setBuildService(this); - this.taskScheduler.setBuildService(this); + this.incremental = incremental; + } + + public void addStrippedSourcesPath(Project project, Path path) { + this.strippedSources.put(project, path); } public Map getBuildMaps() { @@ -71,7 +85,7 @@ private void collectTasksFromProject(String taskName, Project project, PropertyT // return collectedSoFar.get(newInput); return; } - CollectedTaskInputs collectedInputs = new CollectedTaskInputs(project, this); + CollectedTaskInputs collectedInputs = new CollectedTaskInputs(project); if (!taskName.equals(OutputTypes.INPUT_SOURCES)) { PropertyTrackingConfig propertyTrackingConfig = new PropertyTrackingConfig(config); @@ -180,20 +194,23 @@ public synchronized void initialHashes() { AtomicReference>> createdFilesRef = new AtomicReference<>(); AtomicReference>> changedFilesRef = new AtomicReference<>(); AtomicReference>> deletedFilesRef = new AtomicReference<>(); + /** * Marks that a file has been created, deleted, or modified in the given project. */ public synchronized void triggerChanges(Project project, Map createdFiles, Map changedFiles, Map deletedFiles) { + Map hashes = currentProjectSourceHash.computeIfAbsent(project, ignore -> new HashMap<>()); - accumulateChanges(project, createdFiles, createdFilesRef); - accumulateChanges(project, changedFiles, changedFilesRef); - accumulateChanges(project, deletedFiles, deletedFilesRef); + if(incremental) { + accumulateChanges(project, createdFiles, createdFilesRef); + accumulateChanges(project, changedFiles, changedFilesRef); + accumulateChanges(project, deletedFiles, deletedFilesRef); - System.out.println("created: " + createdFiles); - System.out.println("changed: " + changedFiles); - System.out.println("deleted: " + deletedFiles); - Map hashes = currentProjectSourceHash.computeIfAbsent(project, ignore -> new HashMap<>()); - hashes.keySet().removeAll(deletedFiles.keySet()); + System.out.println("created: " + createdFiles); + System.out.println("changed: " + changedFiles); + System.out.println("deleted: " + deletedFiles); + hashes.keySet().removeAll(deletedFiles.keySet()); + } assert hashes.keySet().stream().noneMatch(createdFiles.keySet()::contains) : "File already exists, can't be added " + createdFiles.keySet() + ", " + hashes.keySet(); hashes.putAll(createdFiles); assert hashes.keySet().containsAll(changedFiles.keySet()) : "File doesn't exist, can't be modified"; @@ -217,6 +234,7 @@ private void accumulateChanges(Project project, Map /** * Only one build can take place at a time, be sure to stop the previous build before submitting a new one, * or the new one will have to wait until the first finishes + * * @param buildListener support for notifications about the status of the work * @return an object which can cancel remaining unstarted work */ @@ -226,12 +244,14 @@ public synchronized Cancelable requestBuild(BuildListener buildListener) throws prevBuild.blockUntilFinished(); } - Map> createdFiles = createdFilesRef.getAndSet(new HashMap<>()); - Map> changedFiles = changedFilesRef.getAndSet(new HashMap<>()); - Map> deletedFiles = deletedFilesRef.getAndSet(new HashMap<>()); - buildRequested(currentProjectSourceHash, createdFiles, changedFiles, deletedFiles); + if(incremental) { + Map> createdFiles = createdFilesRef.getAndSet(new HashMap<>()); + Map> changedFiles = changedFilesRef.getAndSet(new HashMap<>()); + Map> deletedFiles = deletedFilesRef.getAndSet(new HashMap<>()); + buildRequested(currentProjectSourceHash, createdFiles, changedFiles, deletedFiles); + } // TODO update inputs with the hash changes we've seen Stream.concat(inputs.keySet().stream(), inputs.values().stream().flatMap(i -> i.getInputs().stream())) .filter(i -> i.getProject().hasSourcesMapped()) @@ -295,8 +315,8 @@ public BuildMap safeCreateBuildMap(Project project, Path dir, } private BuildMap createBuildMap(Project project, Path dir, Map> createdFiles, Map> changedFiles, Map> deletedFiles) { - BuildMap buildMap; - Map cachedFiles = currentProjectSourceHash.get(project); + BuildMap buildMap; + Map cachedFiles = currentProjectSourceHash.get(project); Map createdFilesMap = createdFiles.get(project); Map changedFilesMap = changedFiles.get(project); Map deletedFilesMap = deletedFiles.get(project); @@ -307,23 +327,21 @@ private BuildMap createBuildMap(Project project, Path dir, Map dirToProjectFiles.get(e.getAbsoluteParent().toString()) - .getAdded().add(e.getSourcePath().toString())); + .getAdded().add(e.getSourcePath().toString())); } if (changedFilesMap != null) { changedFilesMap.values().forEach(e -> dirToProjectFiles.get(e.getAbsoluteParent().toString()) - .getUpdated().add(e.getSourcePath().toString())); + .getUpdated().add(e.getSourcePath().toString())); } if (deletedFilesMap != null) { deletedFilesMap.values().forEach(e -> dirToProjectFiles.get(e.getAbsoluteParent().toString()) - .getRemoved().add(e.getSourcePath().toString())); + .getRemoved().add(e.getSourcePath().toString())); } buildMap = new BuildMap(project, dirToProjectFiles); buildMaps.put(project, buildMap); - System.out.println("BuildMap: " + project.getKey()); - try { // previous execution exists, so process .dat files // we don't know what this module will link to, so all classes need to be cloned. @@ -333,7 +351,7 @@ private BuildMap createBuildMap(Project project, Path dir, Map cachedFiles, Map dirToProjectFiles) { + + if (cachedFiles == null || cachedFiles.isEmpty()) { + return; + } + Map> dirToAll = new HashMap<>(); project.getSourceRoots().stream().forEach(s -> { dirToAll.put(s, new HashSet<>()); }); // do not include folders. - cachedFiles.values().stream().filter(entry -> !Files.isDirectory(entry.getAbsolutePath())).forEach(e ->{ + cachedFiles.values().stream().filter(entry -> !Files.isDirectory(entry.getAbsolutePath())).forEach(e -> { dirToAll.get(e.getAbsoluteParent().toString()).add(e.getSourcePath().toString()); }); - dirToAll.entrySet().stream().forEach( e -> { + dirToAll.entrySet().stream().forEach(e -> { ProjectFiles projectFiles = new ProjectFiles(e.getKey(), e.getValue()); dirToProjectFiles.put(e.getKey(), projectFiles); }); @@ -370,10 +393,10 @@ public void writeFilesDat(Project project) { writeFileMetaData(project); } - public void buildRequested(Map> currentProjectSourceHash, - Map> createdFiles, - Map> changedFiles, - Map> deletedFiles) { + private void buildRequested(Map> currentProjectSourceHash, + Map> createdFiles, + Map> changedFiles, + Map> deletedFiles) { // make sure all BuildMaps are cleared before build starts System.out.println("Clear BuildMaps"); buildMaps.clear(); @@ -383,11 +406,11 @@ public void buildRequested(Map> current for (Project p : currentProjectSourceHash.keySet()) { // null check avoids re-entrance, as createBuildMap is recursive // and the same dep can be revisited. - if ( buildMaps.get(p) == null) { + if (buildMaps.get(p) == null) { Input input = new Input(p, OutputTypes.TRANSPILED_JS); if (diskCache.lastSuccessfulTaskDir.containsKey(input)) { safeCreateBuildMap(p, diskCache.getLastSuccessfulDirectory(input), - createdFiles, changedFiles, deletedFiles); + createdFiles, changedFiles, deletedFiles); } } } @@ -396,14 +419,13 @@ public void buildRequested(Map> current public void copyAndDeleteFiles(Project project, String outputType, Path path) { Path lastPath = diskCache.lastSuccessfulTaskDir.get(new Input(project, outputType)); - if (lastPath != null) { copyFolder(lastPath.resolve("results").toFile(), - path.toFile()); + path.toFile()); BuildMap buildMap = buildMaps.get(project); - if (outputType.equals(OutputTypes.STRIPPED_SOURCES) || outputType.equals(OutputTypes.TRANSPILED_JS)) { + if (outputType.equals(OutputTypes.TRANSPILED_JS)) { Set visited = new HashSet<>(); // don't duplicate visit .native/.java pairs for (String changed : buildMap.getFilesToDelete()) { try { @@ -414,39 +436,21 @@ public void copyAndDeleteFiles(Project project, String outputType, Path path) { String binaryTypeName = firstPart.replace('/', '.'); if (visited.add(binaryTypeName)) { - Path javaPath = path.resolve( firstPart + ".java"); - boolean b1 = Files.deleteIfExists(javaPath); + Path javaPath = path.resolve(firstPart + ".java"); + boolean b1 = Files.deleteIfExists(javaPath); System.out.println("Delete: " + javaPath + ":" + b1); if (outputType.equals(OutputTypes.STRIPPED_SOURCES)) { - Path jsNativePath = path.resolve(firstPart + ".native.js"); - boolean b2 = Files.deleteIfExists(jsNativePath); - System.out.println("Delete: " + jsNativePath + ":" + b2); + Path jsNativePath = path.resolve(firstPart + ".native.js"); + Files.deleteIfExists(jsNativePath); } else { deleteInnerTypesSource(buildMap, path, binaryTypeName); } } } else { // just standard delete for anything else - Path changedPath = path.resolve(changed); - boolean b1 = Files.deleteIfExists(changedPath); - System.out.println("Delete: " + changedPath + " : " + b1); - } - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - } else if (outputType.contains(OutputTypes.BYTECODE) ) { - for (String changed : buildMap.getFilesToDelete()) { - try { - if (changed.endsWith(".java")) { - int suffixLength = changed.endsWith(".native.js") ? 10 : 5; - - String firstPart = changed.substring(0, changed.length() - suffixLength); - String binaryTypeName = firstPart.replace('/', '.'); - - deleteInnerTypesClass(buildMap, path, binaryTypeName); + Path changedPath = path.resolve(changed); + Files.deleteIfExists(changedPath); } } catch (IOException e) { e.printStackTrace(); @@ -461,8 +465,8 @@ private void deleteInnerTypesSource(BuildMap buildMap, Path path, String binaryTypeName) throws IOException { int lastDotIndex = binaryTypeName.lastIndexOf('.'); String packageName = binaryTypeName.substring(0, lastDotIndex); - String simpleName = binaryTypeName.substring(lastDotIndex+1); - String firstPart = packageName.replace('.', '/') + "/" + simpleName; + String simpleName = binaryTypeName.substring(lastDotIndex + 1); + String firstPart = packageName.replace('.', '/') + "/" + simpleName; Path javaJsPath = path.resolve(firstPart + ".java.js"); boolean b3 = Files.deleteIfExists(javaJsPath); @@ -475,34 +479,13 @@ private void deleteInnerTypesSource(BuildMap buildMap, Path path, Path nativeUndrscoreJs = path.resolve(firstPart + ".native_js"); boolean b6 = Files.deleteIfExists(nativeUndrscoreJs); - - System.out.println("Delete: " + javaJsPath + " : " + b3); - System.out.println("Delete: " + implJavaJsPath + " : " + b4); - System.out.println("Delete: " + jsMap + " : " + b5); - System.out.println("Delete: " + nativeUndrscoreJs + " : " + b6); - List innerTypes = buildMap.getInnerTypes(binaryTypeName); for (String innerType : innerTypes) { deleteInnerTypesSource(buildMap, path, innerType); } } - private void deleteInnerTypesClass(BuildMap buildMap, Path path, String binaryTypeName) throws IOException { - int lastDotIndex = binaryTypeName.lastIndexOf('.'); - String packageName = binaryTypeName.substring(0, lastDotIndex); - String simpleName = binaryTypeName.substring(lastDotIndex+1); - Path classPath = path.resolve(packageName.replace('.', '/') + "/" + simpleName + ".class"); - boolean b3 = Files.deleteIfExists(classPath); - - System.out.println("Delete: " + classPath + " : " + b3); - List innerTypes = buildMap.getInnerTypes(binaryTypeName); - - for (String innerType : innerTypes) { - deleteInnerTypesClass(buildMap, path, innerType); - } - } - - static void copyFolder(File src, File dest){ + static void copyFolder(File src, File dest) { try { FileUtils.copyDirectory(src, dest); } catch (IOException e) { @@ -514,17 +497,16 @@ static void copyFolder(File src, File dest){ public void writeFileMetaData(Project project) { Path projPath = diskCache.cacheDir.toPath().resolve(project.getKey().replaceAll("[^\\-_a-zA-Z0-9.]", "-")); Path filesDat = projPath.resolve("files.dat"); - - try(Writer out = Files.newBufferedWriter(filesDat, Charset.forName("UTF-8"))) { + try (Writer out = Files.newBufferedWriter(filesDat, Charset.forName("UTF-8"))) { // The paths for the last outputs need to be preserved, for restart. - String[] outputPaths = new String[] { OutputTypes.GENERATED_SOURCES, OutputTypes.STRIPPED_SOURCES, OutputTypes.STRIPPED_BYTECODE, - OutputTypes.BYTECODE, OutputTypes.STRIPPED_BYTECODE_HEADERS, OutputTypes.TRANSPILED_JS}; + String[] outputPaths = new String[]{OutputTypes.GENERATED_SOURCES, OutputTypes.STRIPPED_SOURCES, OutputTypes.STRIPPED_BYTECODE, + OutputTypes.BYTECODE, OutputTypes.STRIPPED_BYTECODE_HEADERS, OutputTypes.TRANSPILED_JS}; for (String outputPath : outputPaths) { Path path = diskCache.lastSuccessfulTaskDir.get(new Input(project, outputPath)); out.append(path + System.lineSeparator()); } - Map cachedFiles = currentProjectSourceHash.get(project); + Map cachedFiles = currentProjectSourceHash.get(project); Map dirToProjectFiles = new HashMap<>(); createDirToProjectFiles(project, cachedFiles, dirToProjectFiles); @@ -541,13 +523,13 @@ public void writeFileMetaData(Project project) { //sourceDirs for (String file : files) { - Path absFile = base.resolve(file); + Path absFile = base.resolve(file); FileTime newTime = Files.getLastModifiedTime(absFile); out.append(newTime.toMillis() + "," + file + System.lineSeparator()); } } - } catch ( IOException e) { + } catch (IOException e) { throw new RuntimeException(e); } } diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/ChangedAcceptor.java b/build-caching/src/main/java/com/vertispan/j2cl/build/ChangedAcceptor.java index 580bb17b..5824fb4b 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/ChangedAcceptor.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/ChangedAcceptor.java @@ -1,6 +1,5 @@ package com.vertispan.j2cl.build; -import java.nio.file.Path; import java.util.function.Predicate; import com.vertispan.j2cl.build.task.CachedPath; @@ -10,12 +9,22 @@ public class ChangedAcceptor implements Predicate { private BuildMap buildMap; public ChangedAcceptor(Project project, BuildService buildService) { + + buildMap = buildService != null ? buildService.getBuildMaps().get(project) : null; } @Override public boolean test(CachedPath cachedPath) { boolean found = true; if (buildMap != null) { + String tested = cachedPath.getSourcePath().toString(); + if(tested.endsWith(".native.js")) { + String candidate = tested.replace(".native.js",".java"); + if(buildMap.getChangedFiles().contains(candidate)) { + return true; + } + } + found = cachedPath.getHash().equals(FileHash.DIRECTORY) || buildMap.getChangedFiles().contains(cachedPath.getSourcePath().toString()); } diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/DiskCache.java b/build-caching/src/main/java/com/vertispan/j2cl/build/DiskCache.java index 204a29d7..f7c29fe1 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/DiskCache.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/DiskCache.java @@ -419,7 +419,7 @@ public void waitForTask(CollectedTaskInputs taskDetails, Listener listener) { knownMarkers.put(failureMarker, taskDir); // register to watch if a marker is made so we can get a call back, then check for existing markers - WatchKey key = registerWatchCreate(taskDir.resolve("status")); + WatchKey key = registerWatchCreate(taskDir); // check once more if we can take over the task dir, if we raced with the registration //TODO one more check here that we even want to make this and start the work diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/PropertyTrackingConfig.java b/build-caching/src/main/java/com/vertispan/j2cl/build/PropertyTrackingConfig.java index 52528d86..7cf1f322 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/PropertyTrackingConfig.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/PropertyTrackingConfig.java @@ -229,4 +229,9 @@ public Path getWebappDirectory() { } return Paths.get(s); } + + @Override + public boolean getIncremental() { + return Boolean.parseBoolean(getString("incremental")); + } } diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/TaskScheduler.java b/build-caching/src/main/java/com/vertispan/j2cl/build/TaskScheduler.java index ddf51598..4ae7a596 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/TaskScheduler.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/TaskScheduler.java @@ -39,7 +39,6 @@ public class TaskScheduler { // to start and this isn't null, we assert it is already set to this value (and skip the work), and assert it is // null when submitting any work. private final AtomicReference finalTaskMarker = new AtomicReference<>(); - private BuildService buildService; /** * Creates a scheduler to perform work as needed. Before any task is attempted, the @@ -61,10 +60,6 @@ public TaskScheduler(Executor executor, DiskCache diskCache, BuildLog buildLog) this.buildLog = buildLog; } - public void setBuildService(BuildService buildService) { - this.buildService = buildService; - } - /** * Params need to specify dependencies so we can track them internally, and when submitted */ diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/WatchService.java b/build-caching/src/main/java/com/vertispan/j2cl/build/WatchService.java index 5070a4cd..1141030a 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/WatchService.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/WatchService.java @@ -98,7 +98,6 @@ public void populateChanges(Project project, Map deletedFiles) { Path projPath = buildService.getDiskCache().cacheDir.toPath().resolve(project.getKey().replaceAll("[^\\-_a-zA-Z0-9.]", "-")); Path filesDat = projPath.resolve("files.dat"); - boolean putAll = true; try { File file; diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/impl/CollectedTaskInputs.java b/build-caching/src/main/java/com/vertispan/j2cl/build/impl/CollectedTaskInputs.java index 4f2dc667..33e0515e 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/impl/CollectedTaskInputs.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/impl/CollectedTaskInputs.java @@ -37,14 +37,12 @@ public class CollectedTaskInputs { private TaskFactory taskFactory; private List inputs; private Map usedConfigs; - private BuildService buildService; // not used for anything except kept around to avoid recomputing it private TaskFactory.Task task; - public CollectedTaskInputs(Project project, BuildService buildService) { + public CollectedTaskInputs(Project project) { this.project = project; - this.buildService = buildService; } public static CollectedTaskInputs jar(Project project) { @@ -52,7 +50,7 @@ public static CollectedTaskInputs jar(Project project) { assert project.getSourceRoots().size() == 1; Path jarPath = Paths.get(project.getSourceRoots().get(0)); - CollectedTaskInputs t = new CollectedTaskInputs(project, null); + CollectedTaskInputs t = new CollectedTaskInputs(project); t.setTaskFactory(new UnpackJarTaskFactory()); t.setTask(t.getTaskFactory().resolve(project, null, null)); // create a fake input and give it a hash so that this unpack only runs if the jar changes diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/BuildMapBuilder.java b/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/BuildMapBuilder.java new file mode 100644 index 00000000..e6e0b709 --- /dev/null +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/BuildMapBuilder.java @@ -0,0 +1,139 @@ +/* + * Copyright 2018 Google Inc. + * + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.vertispan.j2cl.build.incremental; + +import javassist.CtClass; +import javassist.CtMethod; +import javassist.NotFoundException; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.SignatureAttribute; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * This is a clone of LibraryInfoBuilder, with all pruning removed. + * The idea is to first figure out the desired behaviour. It may be that + * that logic is merged into LibraryInfoBuilder or a separate class is needed. + * Once this reaches an agreed milestone, discussions can + * start on changes needed to progress this further. + */ +public final class BuildMapBuilder { + private final Path outputPath; + + private final String LINE_SEPARATOR = System.lineSeparator(); + + public BuildMapBuilder(Path outputPath) { + this.outputPath = outputPath; + } + + public void addClass(CtClass clazz) throws NotFoundException { + StringBuffer sb = new StringBuffer("- sources"); + sb.append(LINE_SEPARATOR); + sb.append(clazz.getName()); + sb.append(":"); + sb.append(LINE_SEPARATOR); + sb.append("- hierarchy"); + sb.append(LINE_SEPARATOR); + + CtClass superClass = clazz.getSuperclass(); + + if (!superClass.getName().equals("java.lang.Object")) { + sb.append(superClass.getName()); + sb.append(":"); + sb.append(LINE_SEPARATOR); + } + + sb.append("- innerTypes"); + sb.append(LINE_SEPARATOR); + + Arrays.stream(clazz.getDeclaredClasses()).forEach(innerClass -> { + sb.append(innerClass.getName()); + sb.append(LINE_SEPARATOR); + }); + + sb.append("- interfaces"); + sb.append(LINE_SEPARATOR); + + Arrays.stream(clazz.getInterfaces()).forEach(interfaceClass -> { + sb.append(interfaceClass.getName()); + sb.append(LINE_SEPARATOR); + }); + + sb.append("- dependencies"); + sb.append(LINE_SEPARATOR); + + Set types = new HashSet<>(); + + clazz.getRefClasses() + .forEach( + c -> maybeAddType(c, types)); + + Arrays.stream(clazz.getFields()) + .forEach( + field -> { + try { + if(field.getGenericSignature() != null) { + SignatureAttribute.Type typeSignature = SignatureAttribute.toTypeSignature(field.getGenericSignature()); + if(typeSignature instanceof javassist.bytecode.SignatureAttribute.ClassType) { + javassist.bytecode.SignatureAttribute.ClassType classType = + (javassist.bytecode.SignatureAttribute.ClassType) typeSignature; + for (SignatureAttribute.TypeArgument typeArgument : classType.getTypeArguments()) { + maybeAddType(typeArgument.toString(), types); + } + } + } + maybeAddType(field.getType().getName(), types); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } catch (BadBytecode e) { + throw new RuntimeException(e); + } + }); + + types.forEach(type -> { + if (!clazz.getName().equals(type)) { + sb.append(type); + sb.append(LINE_SEPARATOR); + } + }); + + + String pkg = clazz.getPackageName().replaceAll("\\.", System.getProperty("file.separator")); + File output = new File(outputPath.toFile(), pkg + "/" + clazz.getSimpleName() + ".build.map"); + + Path path = Paths.get(output.toURI()); + byte[] strToBytes = sb.toString().getBytes(); + try { + Files.write(path, strToBytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + void maybeAddType(String clazz, Set types) { + if(!(clazz.startsWith("java."))) { + types.add(clazz); + } + } +} diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/LibraryInfoBuilder.java b/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/LibraryInfoBuilder.java new file mode 100644 index 00000000..fb905f13 --- /dev/null +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/LibraryInfoBuilder.java @@ -0,0 +1,381 @@ +/* + * Copyright 2018 Google Inc. + * + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.vertispan.j2cl.build.incremental; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +/*import com.google.j2cl.common.Problems; +import com.google.j2cl.common.Problems.FatalError; +import com.google.j2cl.common.SourcePosition; +import com.google.j2cl.transpiler.ast.AbstractVisitor; +import com.google.j2cl.transpiler.ast.DeclaredTypeDescriptor; +import com.google.j2cl.transpiler.ast.FieldAccess; +import com.google.j2cl.transpiler.ast.FieldDescriptor; +import com.google.j2cl.transpiler.ast.Invocation; +import com.google.j2cl.transpiler.ast.JavaScriptConstructorReference; +import com.google.j2cl.transpiler.ast.Member; +import com.google.j2cl.transpiler.ast.MemberDescriptor; +import com.google.j2cl.transpiler.ast.MethodDescriptor; +import com.google.j2cl.transpiler.ast.Type; +import com.google.j2cl.transpiler.ast.TypeDescriptors; +import com.google.protobuf.util.JsonFormat;*/ + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** Traverse types and gather execution flow information for building call graph. */ +public final class LibraryInfoBuilder { + + /* public static final int NULL_TYPE = 0; + private final LibraryInfo.Builder libraryInfo = LibraryInfo.newBuilder(); + private final Map types = new HashMap<>(); + + public void addType( + Type type, + String headerFilePath, + String implFilePath, + Map outputSourceInfoByMember) { + + if (!isPrunableType(type.getTypeDescriptor())) { + return; + } + + TypeInfo.Builder typeInfoBuilder = + TypeInfo.newBuilder() + .setTypeId(getTypeId(type.getTypeDescriptor())) + .setHeaderSourceFilePath(headerFilePath) + .setImplSourceFilePath(implFilePath) + .setJstypeInterface(type.isInterface() && type.getTypeDescriptor().isJsType()); + + DeclaredTypeDescriptor superTypeDescriptor = type.getSuperTypeDescriptor(); + if (superTypeDescriptor != null + && !superTypeDescriptor.isNative() + && !TypeDescriptors.isJavaLangObject(superTypeDescriptor)) { + typeInfoBuilder.setExtendsType(getTypeId(superTypeDescriptor)); + } + + for (DeclaredTypeDescriptor superInterfaceType : type.getSuperInterfaceTypeDescriptors()) { + if (!superInterfaceType.isNative() && !superInterfaceType.isJsFunctionInterface()) { + typeInfoBuilder.addImplementsType(getTypeId(superInterfaceType)); + } + } + + // Collect references to getter and setter for the same field under the same key to + // create only one MemberInfo instance that combines all the references appearing in their + // bodies (see #getMemberId). + Map memberInfoBuilders = + Maps.newLinkedHashMapWithExpectedSize(type.getMembers().size()); + + boolean hasConstantEntryPoint = false; + for (Member member : type.getMembers()) { + MemberDescriptor memberDescriptor = member.getDescriptor(); + + if (memberDescriptor.hasJsNamespace()) { + // Members with an explicit namespace members don't really belong to the type. Skip them + // here, otherwise they would be an entry point for this type, and the type might be + // unnecessarily retained by rta. + continue; + } + + if (memberDescriptor.getOrigin().isInstanceOfSupportMember()) { + // InstanceOf support members should not be considered methods that are prunable if there + // are no references, since the references are hidden by the runtime. In the end + // InstanceOf support members are live whenever the type is live. + continue; + } + + if (memberDescriptor.isField() && !mayTriggerClinit(memberDescriptor)) { + if (memberDescriptor.isCompileTimeConstant() && isJsAccessible(memberDescriptor)) { + hasConstantEntryPoint = true; + } + // We don't need to record fields, there is not much value on pruning them. However there is + // slight complication for fields that may trigger clinit which may (or may not) generated + // as getter, so we need to record their usage (hence their data here as well). + continue; + } + + MemberInfo.Builder builder = + memberInfoBuilders.computeIfAbsent( + getMemberId(memberDescriptor), + m -> createMemberInfo(memberDescriptor, outputSourceInfoByMember)); + + collectReferencedTypesAndMethodInvocations(member, builder); + } + + if (hasConstantEntryPoint) { + // Ensure the type is not pruned by RTA. + memberInfoBuilders.put( + "$js_entry$", + MemberInfo.newBuilder().setName("$js_entry$").setStatic(true).setJsAccessible(true)); + } + + libraryInfo.addType( + typeInfoBuilder.addAllMember( + memberInfoBuilders.values().stream() + .map(MemberInfo.Builder::build) + .collect(Collectors.toList()))); + } + + private static MemberInfo.Builder createMemberInfo( + MemberDescriptor memberDescriptor, + Map outputSourceInfoByMember) { + MemberInfo.Builder memberInfoBuilder = + MemberInfo.newBuilder() + .setName(getMemberId(memberDescriptor)) + .setStatic(memberDescriptor.isStatic()) + .setJsAccessible(isJsAccessible(memberDescriptor)); + + // See the limitations of member removal in b/177365417. + SourcePosition position = outputSourceInfoByMember.get(memberDescriptor); + if (position != null) { + memberInfoBuilder.setPosition( + com.google.j2cl.transpiler.backend.libraryinfo.SourcePosition.newBuilder() + .setStart(position.getStartFilePosition().getLine()) + // For the minifier, end position is exclusive. + .setEnd(position.getEndFilePosition().getLine() + 1) + .build()); + } + + return memberInfoBuilder; + } + + private void collectReferencedTypesAndMethodInvocations( + Member member, MemberInfo.Builder memberInfoBuilder) { + Set invokedMethods = + new LinkedHashSet<>(memberInfoBuilder.getInvokedMethodsList()); + + // The set of types that are explicitly referenced in this member; these come from + // JavaScriptConstructorReferences that appear in the AST from type literals, casts, + // instanceofs and ALSO also the qualifier in every static member reference. + // References to static members already include the enclosing class, so in order to avoid + // redundancy in library info these types are tracked separately and removed. + Set explicitlyReferencedTypes = + new LinkedHashSet<>(memberInfoBuilder.getReferencedTypesList()); + + // The set of types that are implicitly referenced in this member; these come from static + // Invocations in the AST, e.g. the enclosing class of a static method call. These types will be + // preserved by RTA when seeing the reference to the static member so there is no need to + // separately record them as referenced types. + Set typesReferencedViaStaticMemberReferences = new HashSet<>(); + + member.accept( + new AbstractVisitor() { + @Override + public void exitJavaScriptConstructorReference(JavaScriptConstructorReference node) { + DeclaredTypeDescriptor referencedType = + node.getReferencedTypeDeclaration().toRawTypeDescriptor(); + + if (!isPrunableType(referencedType)) { + return; + } + + if (isJsAccessible(referencedType)) { + return; + } + + // No need to record references to parent or itself since they will be live regardless. + if (member.getDescriptor().getEnclosingTypeDescriptor().isSubtypeOf(referencedType)) { + return; + } + + // In Javascript a Class is statically referenced by using it's constructor function. + explicitlyReferencedTypes.add(getTypeId(referencedType)); + } + + @Override + public void exitFieldAccess(FieldAccess node) { + FieldDescriptor target = node.getTarget(); + + if (!isPrunableType(target.getEnclosingTypeDescriptor())) { + return; + } + + if (isJsAccessible(target)) { + // We don't record access to js accessible fields since they are never pruned. + return; + } + + if (!mayTriggerClinit(target)) { + return; + } + + // Register static FieldAccess as getter/setter invocations. We are conservative here + // because getter and setter functions have the same name: i.e. the name of the field. + // If a field is accessed, we visit both getter and setter. + addInvokedMethod(target, node.getQualifier() != null ? (DeclaredTypeDescriptor) node.getQualifier().getTypeDescriptor() : null); + } + + @Override + public void exitInvocation(Invocation node) { + MethodDescriptor target = node.getTarget(); + + if (!isPrunableType(target.getEnclosingTypeDescriptor())) { + return; + } + + if (isJsAccessible(target)) { + // We don't record call to js accessible methods since they are never pruned. + return; + } + + // Only record a $clinit call if it is from a clinit itself. All other clinit calls are + // from the entry points of the class and doesn't need recording since RapidTypeAnalyser + // will make the clinit alive when it arrives to an entry point. + if (target.getName().equals("$clinit") + && !member.getDescriptor().getName().equals("$clinit")) { + return; + } + + // TODO(b/34928687): Remove after $loadmodule moved the AST. + if (target.getName().equals("$loadModules")) { + return; + } + + addInvokedMethod(target, node.getQualifier() != null ? (DeclaredTypeDescriptor) node.getQualifier().getTypeDescriptor() : null); + } + + private void addInvokedMethod(MemberDescriptor target, DeclaredTypeDescriptor nodeEnclosingType) { + invokedMethods.add(createMethodInvocation(target, nodeEnclosingType)); + if (!target.isInstanceMember()) { + typesReferencedViaStaticMemberReferences.add( + getTypeId(target.getEnclosingTypeDescriptor())); + } + } + }); + + memberInfoBuilder + .clearReferencedTypes() + .clearInvokedMethods() + .addAllInvokedMethods(invokedMethods) + // Record only the explicit type references without the implicit ones which are redundant. + .addAllReferencedTypes( + Sets.difference(explicitlyReferencedTypes, typesReferencedViaStaticMemberReferences)); + } + + private MethodInvocation createMethodInvocation(MemberDescriptor memberDescriptor, DeclaredTypeDescriptor nodeEnclosingType) { + return MethodInvocation.newBuilder() + .setMethod(getMemberId(memberDescriptor)) + .setTargetEnclosingType(getTypeId(memberDescriptor.getEnclosingTypeDescriptor())) + .setNodeEnclosingType((nodeEnclosingType != null) ? getTypeId(nodeEnclosingType) : -1) + .build(); + } + + private int getTypeId(DeclaredTypeDescriptor typeDescriptor) { + // Note that the IDs start from '1' to reserve '0' for NULL_TYPE. + return types.computeIfAbsent(typeDescriptor.getQualifiedJsName(), x -> types.size() + 1); + } + + private LibraryInfo build() { + libraryInfo.clearTypeMap(); + String[] typeMap = new String[types.size() + 1]; + typeMap[NULL_TYPE] = ""; + types.forEach((name, i) -> typeMap[i] = name); + libraryInfo.addAllTypeMap(Arrays.asList(typeMap)); + return libraryInfo.build(); + } + + *//** Serialize a LibraryInfo object into a JSON string. *//* + public String toJson(Problems problems) { + try { + return JsonFormat.printer().print(build()); + } catch (IOException e) { + problems.fatal(FatalError.CANNOT_WRITE_FILE, e.toString()); + return null; + } + } + + public byte[] toByteArray() { + return build().toByteArray(); + } + + private static String getMemberId(MemberDescriptor memberDescriptor) { + // TODO(b/158014657): remove this once the bug is fixed. + String mangledName = + isPropertyAccessor(memberDescriptor) + ? memberDescriptor.computePropertyMangledName() + : memberDescriptor.getMangledName(); + + // Avoid unintented collissions by using the seperate namespace for static and non-static. + return memberDescriptor.isInstanceMember() ? mangledName + "_$i" : mangledName; + } + + private static boolean mayTriggerClinit(MemberDescriptor memberDescriptor) { + return memberDescriptor.isStatic() && !memberDescriptor.isCompileTimeConstant(); + } + + private static boolean isPropertyAccessor(MemberDescriptor memberDescriptor) { + if (!memberDescriptor.isMethod()) { + return false; + } + MethodDescriptor methodDescriptor = (MethodDescriptor) memberDescriptor; + return methodDescriptor.isPropertyGetter() || methodDescriptor.isPropertySetter(); + } + + private static boolean isPrunableType(DeclaredTypeDescriptor typeDescriptor) { + return !typeDescriptor.isNative() && !typeDescriptor.isJsEnum(); + } + + private static boolean isJsAccessible(MemberDescriptor memberDescriptor) { + // Note that we are not collecting bootstrap classes in references so we need to force them in. + // There are 2 reasons for that: + // - We are inconsistent in marking their references wrt. being native or not. + // - They are frequently used and never pruned; recording them is wasteful. + return isInBootstrap(memberDescriptor.getEnclosingTypeDescriptor()) + || memberDescriptor.canBeReferencedExternally(); + } + + private static boolean isJsAccessible(DeclaredTypeDescriptor typeDescriptor) { + return isInBootstrap(typeDescriptor) + || typeDescriptor.getDeclaredMemberDescriptors().stream() + .filter(Predicates.not(MemberDescriptor::isInstanceMember)) + .anyMatch(MemberDescriptor::isJsMember); + } + + private static boolean isInBootstrap(DeclaredTypeDescriptor typeDescriptor) { + return TypeDescriptors.isBootstrapNamespace(typeDescriptor) + || isAccesssedFromJ2clBootstrapJsFiles(typeDescriptor); + } + + // There are references to non JsMember members of these types from JavaScript in the J2CL + // runtime. For now we will consider and all their members accessible by JavaScript code. + // TODO(b/29509857): remove once the refactoring of the code in nativebootstrap and vmbootstrap + // is completed and the references removed. + private static final ImmutableSet TYPES_ACCESSED_FROM_J2CL_BOOTSTRAP_JS = + ImmutableSet.of( + "javaemul.internal.InternalPreconditions", + "java.lang.Object", + "java.lang.Double", + "java.lang.Boolean", + "java.lang.Number", + "java.lang.CharSequence", + "java.lang.String", + "java.lang.Class", + "java.lang.Comparable", + "java.lang.Integer"); + + private static boolean isAccesssedFromJ2clBootstrapJsFiles( + DeclaredTypeDescriptor typeDescriptor) { + return TYPES_ACCESSED_FROM_J2CL_BOOTSTRAP_JS.contains(typeDescriptor.getQualifiedSourceName()); + }*/ +} diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/library_info.proto b/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/library_info.proto new file mode 100644 index 00000000..1a6133b0 --- /dev/null +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/incremental/library_info.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package j2cl; + +option java_multiple_files = true; +option java_package = "com.google.j2cl.transpiler.backend.libraryinfo"; + +message LibraryInfo { + repeated string type_map = 1; + repeated TypeInfo type = 2; +} + +message TypeInfo { + int32 type_id = 1; + int32 extends_type = 2; + repeated int32 implements_type = 3; + repeated MemberInfo member = 4; + string header_source_file_path = 5; + string impl_source_file_path = 6; + bool jstype_interface = 7; +} + +message MemberInfo { + string name = 1; + bool static = 3; + bool js_accessible = 4; + repeated MethodInvocation invoked_methods = 5; + repeated int32 referenced_types = 6; + SourcePosition position = 7; +} + +message MethodInvocation { + string method = 1; + int32 target_enclosing_type = 2; + int32 node_enclosing_type = 3; +} + +message SourcePosition { + int32 start = 1; + int32 end = 2; +} diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/task/Config.java b/build-caching/src/main/java/com/vertispan/j2cl/build/task/Config.java index d4b90022..abf5e142 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/task/Config.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/task/Config.java @@ -53,4 +53,6 @@ public interface Config { */ Path getWebappDirectory(); + boolean getIncremental(); + } diff --git a/build-caching/src/main/java/com/vertispan/j2cl/build/task/TaskFactory.java b/build-caching/src/main/java/com/vertispan/j2cl/build/task/TaskFactory.java index d770350c..3949e93c 100644 --- a/build-caching/src/main/java/com/vertispan/j2cl/build/task/TaskFactory.java +++ b/build-caching/src/main/java/com/vertispan/j2cl/build/task/TaskFactory.java @@ -9,7 +9,6 @@ import java.util.stream.Collectors; import com.vertispan.j2cl.build.BuildService; -import com.vertispan.j2cl.build.DiskCache; /** * A task describes the type of output it provides, and for a given project will provide the @@ -39,16 +38,13 @@ public String toString() { public final List inputs = new ArrayList<>(); - protected Input input(Dependency dependency, String outputType, BuildService buildService) { - return input(dependency.getProject(), outputType, buildService); - } - protected Input input(Project dependencyProject, String outputType, BuildService buildService) { + protected Input input(Project dependencyProject, String outputType) { com.vertispan.j2cl.build.Input i = new com.vertispan.j2cl.build.Input((com.vertispan.j2cl.build.Project) dependencyProject, outputType); inputs.add(i); return i; } - protected Function inputs(String outputType, BuildService buildService) { - return p -> input(p, outputType, buildService); + protected Function inputs(String outputType) { + return p -> input(p, outputType); } protected List scope(Collection dependencies, Dependency.Scope scope) { diff --git a/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/BuildMojo.java b/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/BuildMojo.java index 37dffcc6..4f509164 100644 --- a/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/BuildMojo.java +++ b/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/BuildMojo.java @@ -232,7 +232,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { TaskRegistry taskRegistry = createTaskRegistry(); // Given these, build the graph of work we need to complete - BuildService buildService = new BuildService(taskRegistry, taskScheduler, diskCache); + BuildService buildService = new BuildService(taskRegistry, taskScheduler, diskCache, false); buildService.assignProject(p, outputTask, config); // Get the hash of all current files, since we aren't running a watch service @@ -252,4 +252,10 @@ public void execute() throws MojoExecutionException, MojoFailureException { } } + /** + * True to enable experimental incremental mode. + */ + @Parameter(defaultValue = "false") + protected boolean incremental; + } diff --git a/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/TestMojo.java b/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/TestMojo.java index 262f1cb2..d070a507 100644 --- a/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/TestMojo.java +++ b/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/TestMojo.java @@ -334,7 +334,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { TaskRegistry taskRegistry = createTaskRegistry(); // Given these, build the graph of work we need to complete to get the list of tests - BuildService buildService = new BuildService(taskRegistry, taskScheduler, diskCache); + BuildService buildService = new BuildService(taskRegistry, taskScheduler, diskCache, false); buildService.assignProject(test, TestCollectionTask.TEST_COLLECTION_OUTPUT_TYPE, config); // Get the hash of all current files, since we aren't running a watch service @@ -393,7 +393,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { // Fresh build service (to avoid re-running other final tasks) since we're building serially, // but we reuse the params - buildService = new BuildService(taskRegistry, taskScheduler, diskCache); + buildService = new BuildService(taskRegistry, taskScheduler, diskCache, false); buildService.assignProject(suite, outputTask, overridenConfig); buildService.initialHashes(); BlockingBuildListener l = new BlockingBuildListener(); diff --git a/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/WatchMojo.java b/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/WatchMojo.java index 280e3bdd..04027d71 100644 --- a/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/WatchMojo.java +++ b/j2cl-maven-plugin/src/main/java/com/vertispan/j2cl/mojo/WatchMojo.java @@ -197,7 +197,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { // TODO support individual task registries per execution TaskRegistry taskRegistry = createTaskRegistry(); - BuildService buildService = new BuildService(taskRegistry, taskScheduler, diskCache); + BuildService buildService = new BuildService(taskRegistry, taskScheduler, diskCache, incremental); // TODO end // assemble all of the projects we are hoping to run - if we fail in this process, we can't actually start building or watching @@ -287,4 +287,10 @@ protected boolean shouldCompileTest() { protected boolean shouldCompileBuild() { return true; } + + /** + * True to enable experimental incremental mode. + */ + @Parameter(defaultValue = "false") + protected boolean incremental; } diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/BundleJarTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/BundleJarTask.java index 6509365a..fc032941 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/BundleJarTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/BundleJarTask.java @@ -48,8 +48,8 @@ public Task resolve(Project project, Config config, BuildService buildService) { .concat( scope(project.getDependencies(), Dependency.Scope.RUNTIME) .stream() - .map(inputs(OutputTypes.BUNDLED_JS, buildService)), - Stream.of(input(project, OutputTypes.BUNDLED_JS, buildService)) + .map(inputs(OutputTypes.BUNDLED_JS)), + Stream.of(input(project, OutputTypes.BUNDLED_JS)) ) .map(i -> i.filter(BUNDLE_JS)) .collect(Collectors.toList()); @@ -72,7 +72,7 @@ public Task resolve(Project project, Config config, BuildService buildService) { } //cheaty, but lets us cache - Input jszip = input(project, JSZIP_BUNDLE_OUTPUT_TYPE, buildService); + Input jszip = input(project, JSZIP_BUNDLE_OUTPUT_TYPE); File initialScriptFile = config.getWebappDirectory().resolve(config.getInitialScriptFilename()).toFile(); Map defines = new LinkedHashMap<>(config.getDefines()); @@ -83,7 +83,7 @@ public Task resolve(Project project, Config config, BuildService buildService) { ) // Only need to consider the original inputs and generated sources, // J2CL won't contribute this kind of sources - .map(p -> input(p, OutputTypes.BYTECODE, buildService).filter(COPIED_OUTPUT)) + .map(p -> input(p, OutputTypes.BYTECODE).filter(COPIED_OUTPUT)) .collect(Collectors.toList()); return new FinalOutputTask() { @@ -107,8 +107,6 @@ public void finish(TaskContext taskContext) throws IOException { } for (Path dir : Stream.concat(Stream.of(jszip), jsSources.stream()).map(Input::getParentPaths).flatMap(Collection::stream).collect(Collectors.toSet())) { - System.out.println("dir1: " + dir.toAbsolutePath()); - System.out.println("dir2: " + initialScriptFile.getParentFile().getAbsolutePath()); FileUtils.copyDirectory(dir.toFile(), initialScriptFile.getParentFile()); } diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/BytecodeTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/BytecodeTask.java index 24a6a2e4..e3e2317c 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/BytecodeTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/BytecodeTask.java @@ -2,6 +2,7 @@ import com.google.auto.service.AutoService; import com.google.j2cl.common.SourceUtils; +import com.vertispan.j2cl.build.BuildService; import com.vertispan.j2cl.build.task.*; import com.vertispan.j2cl.tools.Javac; @@ -45,7 +46,9 @@ public String getVersion() { } @Override - public Task resolve(Project project, Config config) { + public Task resolve(Project project, Config config, BuildService service) { + boolean incremental = config.getIncremental(); + if (!project.hasSourcesMapped()) { // instead, copy the bytecode+resources out of the jar so it can be used by downstream bytecode/apt tasks Input existingUnpackedBytecode = input(project, OutputTypes.INPUT_SOURCES); @@ -83,6 +86,15 @@ public Task resolve(Project project, Config config) { extraClasspath.stream() ).collect(Collectors.toList()); + if (incremental) { //TODO DO WE NEED IT + Path bytecodePath = service.getDiskCache().getLastSuccessfulDirectory(new com.vertispan.j2cl.build.Input((com.vertispan.j2cl.build.Project) project, + OutputTypes.BYTECODE)); + + if (bytecodePath != null) { + classpathDirs.add(bytecodePath.resolve("results").toFile()); + } + } + List sourcePaths = inputDirs.getParentPaths().stream().map(Path::toFile).collect(Collectors.toList()); File generatedClassesDir = getGeneratedClassesDir(context); File classOutputDir = context.outputPath().toFile(); diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/ClosureBundleTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/ClosureBundleTask.java index 1d6b6785..09396746 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/ClosureBundleTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/ClosureBundleTask.java @@ -48,8 +48,8 @@ public Task resolve(Project project, Config config, BuildService buildService) { // TODO filter to just JS and sourcemaps? probably not required unless we also get sources // from the actual input source instead of copying it along each step List js = Stream.of( - input(project, OutputTypes.TRANSPILED_JS, buildService), - input(project, OutputTypes.BYTECODE, buildService) + input(project, OutputTypes.TRANSPILED_JS), + input(project, OutputTypes.BYTECODE) ) .map(i -> i.filter(ClosureTask.PLAIN_JS_SOURCES)) .collect(Collectors.toList()); diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/ClosureTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/ClosureTask.java index 59d632ef..b1296eef 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/ClosureTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/ClosureTask.java @@ -4,6 +4,7 @@ import com.google.javascript.jscomp.CompilationLevel; import com.google.javascript.jscomp.CompilerOptions; import com.google.javascript.jscomp.DependencyOptions; +import com.vertispan.j2cl.build.BuildService; import com.vertispan.j2cl.build.task.*; import com.vertispan.j2cl.tools.Closure; import org.apache.commons.io.FileUtils; @@ -131,7 +132,7 @@ public String getVersion() { } @Override - public Task resolve(Project project, Config config) { + public Task resolve(Project project, Config config, BuildService service) { // collect current project JS sources and runtime deps JS sources // TODO filter to just JS and sourcemaps? probably not required unless we also get sources // from the actual input source instead of copying it along each step @@ -223,6 +224,18 @@ public void execute(TaskContext context) throws Exception { defines.putIfAbsent("goog.ENABLE_DEBUG_LOADER", "false");//TODO maybe overwrite instead? } +/* js.entrySet().forEach(entry -> { + + System.out.println("JS " + entry.getKey()); + entry.getValue().forEach(e -> { + System.out.println(" " + e); + + }); + + });*/ + + + boolean success = closureCompiler.compile( compilationLevel, dependencyMode, diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/IJarTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/IJarTask.java index 07c178ed..244565d7 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/IJarTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/IJarTask.java @@ -31,7 +31,7 @@ public String getVersion() { @Override public Task resolve(Project project, Config config, BuildService buildService) { - Input myStrippedBytecode = input(project, OutputTypes.STRIPPED_BYTECODE, buildService); + Input myStrippedBytecode = input(project, OutputTypes.STRIPPED_BYTECODE); return context -> { // for now we're going to just copy the bytecode diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/IJsTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/IJsTask.java index 8d0e3b37..14154ae2 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/IJsTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/IJsTask.java @@ -28,7 +28,7 @@ public String getVersion() { @Override public Task resolve(Project project, Config config, BuildService buildService) { - Input js = input(project, OutputTypes.TRANSPILED_JS, buildService); + Input js = input(project, OutputTypes.TRANSPILED_JS); return context -> { if (js.getFilesAndHashes().isEmpty()) { // nothing to do diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/J2clTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/J2clTask.java index ba0d81b0..c2e563ad 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/J2clTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/J2clTask.java @@ -4,8 +4,12 @@ import com.google.j2cl.common.SourceUtils; import com.vertispan.j2cl.build.BuildService; import com.vertispan.j2cl.build.ChangedAcceptor; -import com.vertispan.j2cl.build.DiskCache; -import com.vertispan.j2cl.build.task.*; +import com.vertispan.j2cl.build.task.CachedPath; +import com.vertispan.j2cl.build.task.Config; +import com.vertispan.j2cl.build.task.Input; +import com.vertispan.j2cl.build.task.OutputTypes; +import com.vertispan.j2cl.build.task.Project; +import com.vertispan.j2cl.build.task.TaskFactory; import com.vertispan.j2cl.tools.J2cl; import java.io.File; @@ -40,16 +44,19 @@ public String getVersion() { @Override public Task resolve(Project project, Config config, BuildService buildService) { - boolean incremental = true; + boolean incremental = config.getIncremental(); // J2CL is only interested in .java and .native.js files in our own sources - Input ownJavaSources = input(project, OutputTypes.STRIPPED_SOURCES, buildService).filter(JAVA_SOURCES, NATIVE_JS_SOURCES); - List ownNativeJsSources = Collections.singletonList(input(project, OutputTypes.BYTECODE, buildService).filter(NATIVE_JS_SOURCES)); + Input ownJavaSources = input(project, OutputTypes.STRIPPED_SOURCES).filter(JAVA_SOURCES, NATIVE_JS_SOURCES); + List ownNativeJsSources = Collections.singletonList(input(project, OutputTypes.BYTECODE).filter(NATIVE_JS_SOURCES)); + + Input myOwnByteCode = input(project, OutputTypes.STRIPPED_BYTECODE_HEADERS).filter(JAVA_BYTECODE); + // From our classpath, j2cl is only interested in our compile classpath's bytecode List projects = scope(project.getDependencies(), com.vertispan.j2cl.build.task.Dependency.Scope.COMPILE); List classpathHeaders = projects.stream() - .map(inputs(OutputTypes.STRIPPED_BYTECODE_HEADERS, buildService)) + .map(inputs(OutputTypes.STRIPPED_BYTECODE_HEADERS)) // we only want bytecode _changes_, but we'll use the whole dir .map(input -> input.filter(JAVA_BYTECODE)) .collect(Collectors.toList()); @@ -59,7 +66,8 @@ public Task resolve(Project project, Config config, BuildService buildService) { return (context) -> { List javaFiles = ownJavaSources.getFilesAndHashes() .stream() - .filter( new ChangedAcceptor((com.vertispan.j2cl.build.Project) project, buildService)).collect(Collectors.toList()); + .filter( new ChangedAcceptor((com.vertispan.j2cl.build.Project) project, buildService)) + .collect(Collectors.toList()); if (javaFiles.isEmpty()) { return;// no work to do @@ -70,15 +78,23 @@ public Task resolve(Project project, Config config, BuildService buildService) { ) .collect(Collectors.toList()); + //ownJavaSources only one file if (incremental) { Path bytecodePath = buildService.getDiskCache().getLastSuccessfulDirectory(new com.vertispan.j2cl.build.Input((com.vertispan.j2cl.build.Project) project, OutputTypes.BYTECODE)); + Path transpiledPath = buildService.getDiskCache().getLastSuccessfulDirectory(new com.vertispan.j2cl.build.Input((com.vertispan.j2cl.build.Project) project, + OutputTypes.TRANSPILED_JS)); + + if(transpiledPath != null) { + buildService.copyAndDeleteFiles((com.vertispan.j2cl.build.Project) project, OutputTypes.TRANSPILED_JS, context.outputPath()); + } + if (bytecodePath != null) { classpathDirs.add(bytecodePath.resolve("results").toFile()); } } - //classpathDirs.add() + J2cl j2cl = new J2cl(classpathDirs, bootstrapClasspath, context.outputPath().toFile(), context); // TODO convention for mapping to original file paths, provide FileInfo out of Inputs instead of Paths, @@ -86,6 +102,7 @@ public Task resolve(Project project, Config config, BuildService buildService) { List javaSources = javaFiles.stream().filter(e -> JAVA_SOURCES.matches(e.getSourcePath())) .map(p -> SourceUtils.FileInfo.create(p.getAbsolutePath().toString(), p.getSourcePath().toString())) .collect(Collectors.toList()); + List nativeSources = ownNativeJsSources.stream().flatMap(i -> i.getFilesAndHashes().stream()) .filter( new ChangedAcceptor((com.vertispan.j2cl.build.Project) project, buildService)) .filter(e -> NATIVE_JS_SOURCES.matches(e.getSourcePath())) @@ -94,9 +111,6 @@ public Task resolve(Project project, Config config, BuildService buildService) { // TODO when we make j2cl incremental we'll consume the provided sources and hashes (the "values" in the // maps above), and diff them against the previous compile - System.out.print("j2cl: "); - javaSources.stream().forEach(f -> System.out.print(f.sourcePath() + " ")); - System.out.println(""); if (!j2cl.transpile(javaSources, nativeSources)) { throw new IllegalStateException("Error while running J2CL"); } diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/JavacTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/JavacTask.java index 0f058522..8f252ea8 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/JavacTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/JavacTask.java @@ -38,13 +38,13 @@ public String getVersion() { @Override public Task resolve(Project project, Config config, BuildService buildService) { - boolean incremental = true; + boolean incremental = config.getIncremental(); // emits only stripped bytecode, so we're not worried about anything other than .java files to compile and .class on the classpath - Input ownSources = input(project, OutputTypes.STRIPPED_SOURCES, buildService).filter(JAVA_SOURCES); + Input ownSources = input(project, OutputTypes.STRIPPED_SOURCES).filter(JAVA_SOURCES); List projects = scope(project.getDependencies(), com.vertispan.j2cl.build.task.Dependency.Scope.COMPILE); List classpathHeaders = projects.stream() - .map(inputs(OutputTypes.STRIPPED_BYTECODE_HEADERS, buildService)) + .map(inputs(OutputTypes.STRIPPED_BYTECODE_HEADERS)) // we only want bytecode _changes_, but we'll use the whole dir .map(input -> input.filter(JAVA_BYTECODE)) .collect(Collectors.toList()); diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/StripSourcesTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/StripSourcesTask.java index fe180c51..95fa569e 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/StripSourcesTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/StripSourcesTask.java @@ -35,7 +35,7 @@ public String getVersion() { @Override public Task resolve(Project project, Config config, BuildService buildService) { - Input inputSources = input(project, OutputTypes.BYTECODE, buildService).filter(JAVA_SOURCES); + Input inputSources = input(project, OutputTypes.BYTECODE).filter(JAVA_SOURCES); return context -> { if (inputSources.getFilesAndHashes().isEmpty()) { diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/TestCollectionTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/TestCollectionTask.java index 3b126a9c..b573fcaa 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/TestCollectionTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/TestCollectionTask.java @@ -38,7 +38,7 @@ public String getVersion() { public Task resolve(Project project, Config config, BuildService buildService) { // gather possible inputs so we can get the test summary file // we assume here that the user will correctly depend on the junit apt, might revise this later - Input apt = input(project, OutputTypes.BYTECODE, buildService).filter(TEST_SUMMARY_JSON, TEST_SUITE); + Input apt = input(project, OutputTypes.BYTECODE).filter(TEST_SUMMARY_JSON, TEST_SUITE); return new FinalOutputTask() { @Override public void execute(TaskContext context) throws Exception { diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/TurbineTask.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/TurbineTask.java index 176b8999..aa6b2a8f 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/TurbineTask.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/build/provided/TurbineTask.java @@ -8,12 +8,17 @@ import com.google.turbine.main.Main; import com.google.turbine.options.LanguageVersion; import com.google.turbine.options.TurbineOptions; +import com.vertispan.j2cl.build.BuildService; +import com.vertispan.j2cl.build.incremental.BuildMapBuilder; import com.vertispan.j2cl.build.task.Config; import com.vertispan.j2cl.build.task.Dependency; import com.vertispan.j2cl.build.task.Input; import com.vertispan.j2cl.build.task.OutputTypes; import com.vertispan.j2cl.build.task.Project; import com.vertispan.j2cl.build.task.TaskFactory; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.compress.archivers.ArchiveInputStream; @@ -32,6 +37,8 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -40,6 +47,7 @@ @AutoService(TaskFactory.class) public class TurbineTask extends JavacTask { + public static final PathMatcher JAVA_SOURCES = withSuffix(".java"); @Override @@ -58,10 +66,10 @@ public String getVersion() { } @Override - public Task resolve(Project project, Config config) { + public Task resolve(Project project, Config config, BuildService service) { int version = getJavaVersion(); if(version == 8) { - return super.resolve(project, config); + return super.resolve(project, config, service); } // emits only stripped bytecode, so we're not worried about anything other than .java files to compile and .class on the classpath @@ -69,6 +77,7 @@ public Task resolve(Project project, Config config) { List extraClasspath = config.getExtraClasspath(); + boolean incremental = config.getIncremental(); List compileClasspath = scope(project.getDependencies(), Dependency.Scope.COMPILE).stream() .map(p -> input(p, OutputTypes.STRIPPED_BYTECODE_HEADERS)) .map(input -> input.filter(JAVA_BYTECODE)) @@ -85,6 +94,8 @@ public Task resolve(Project project, Config config) { File resultFolder = context.outputPath().toFile(); File output = new File(resultFolder, "output.jar"); + Input myOwnByteCode = input(project, OutputTypes.STRIPPED_BYTECODE_HEADERS).filter(JAVA_BYTECODE); + List sources = ownSources.getFilesAndHashes() .stream() .map(p -> SourceUtils.FileInfo.create(p.getAbsolutePath().toString(), p.getSourcePath().toString())) @@ -107,10 +118,41 @@ public Task resolve(Project project, Config config) { //ohhhh apt is in maven reactor System.out.println(e.getMessage()); } + + if(incremental) { + + Input temp = input(project, OutputTypes.TRANSPILED_JS); + + service.addStrippedSourcesPath((com.vertispan.j2cl.build.Project) project, context.outputPath()); + + BuildMapBuilder builder = new BuildMapBuilder(context.outputPath()); + String jarFileName = output.toString(); + + JarFile jar = new JarFile(output); + ClassPool pool = ClassPool.getDefault(); + try{ + pool.insertClassPath(jarFileName); + + jar.stream().map(JarEntry::getName).forEach(name -> { + if(name.endsWith(".class") && !name.startsWith("META-INF")) { + String className = name.replace(".class", "").replace("/", "."); + try { + CtClass clazz = pool.get(className); + builder.addClass(clazz); + } catch (NotFoundException e) { + throw new RuntimeException(e); + } + } + }); + + } catch (NotFoundException e) { + System.out.println("error loading jar!!"); + } + } }; } - public void extractJar(String zipFilePath, String extractDirectory) { + private void extractJar(String zipFilePath, String extractDirectory) { InputStream inputStream; try { Path filePath = Paths.get(zipFilePath); diff --git a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/Javac.java b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/Javac.java index 45b06502..3ba893d9 100644 --- a/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/Javac.java +++ b/j2cl-tasks/src/main/java/com/vertispan/j2cl/tools/Javac.java @@ -35,9 +35,6 @@ public class Javac { public Javac(BuildLog log, File generatedClassesPath, List sourcePaths, List classpath, File classesDirFile, File bootstrap) throws IOException { this.log = log; -// for (File file : classpath) { -// System.out.println(file.getAbsolutePath() + " " + file.exists() + " " + file.isDirectory()); -// } javacOptions = new ArrayList<>(Arrays.asList("-encoding", "utf8", "-implicit:none", "-bootclasspath", bootstrap.toString())); if (generatedClassesPath == null) { javacOptions.add("-proc:none"); diff --git a/j2cl-tools/pom.xml b/j2cl-tools/pom.xml deleted file mode 100644 index 6e355c4d..00000000 --- a/j2cl-tools/pom.xml +++ /dev/null @@ -1,200 +0,0 @@ - - - 4.0.0 - - - com.vertispan.j2cl - j2cl-tools - 0.19-SNAPSHOT - - j2cl-build - - J2CL Cachable builds - - - - - com.vertispan.j2cl - transpiler - ${j2cl.version} - - - com.google.javascript - closure-compiler-unshaded - - - - - org.jspecify - jspecify - - - com.vertispan.j2cl - gwt-incompatible-stripper - ${j2cl.version} - - - com.vertispan.j2cl - frontend - ${j2cl.version} - - - com.google.errorprone - javac - - - - - com.vertispan.j2cl - common - ${j2cl.version} - - - - - com.vertispan.javascript - closure-compiler-unshaded - - - commons-codec - commons-codec - - - commons-io - commons-io - - - - com.google.guava - guava - - - - - com.google.auto.service - auto-service - 1.0 - provided - - - - com.vertispan.j2cl - build-caching - ${project.version} - - - - - junit - junit - test - - - - - - vertispan-releases - Vertispan hosted artifacts-releases - https://repo.vertispan.com/j2cl - - - vertispan-snapshots - Vertispan hosted artifacts-snapshots - https://repo.vertispan.com/j2cl - - - - - - release - - - - org.apache.maven.plugins - maven-source-plugin - ${maven.source.plugin.version} - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven.javadoc.plugin.version} - - - attach-javadocs - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven.gpg.plugin.version} - - - sign-artifacts - verify - - sign - - - - - - - - - - - - vertispan-releases - Vertispan hosted artifacts-releases - https://repo.vertispan.com/j2cl - - true - - - true - - - - sonatype-snapshots-repo - https://oss.sonatype.org/content/repositories/snapshots - - true - daily - fail - - - - - - - - org.apache.maven.plugins - maven-plugin-plugin - 3.5 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.0.0 - - true - - - - - \ No newline at end of file diff --git a/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/AptTask.java b/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/AptTask.java deleted file mode 100644 index bca40da9..00000000 --- a/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/AptTask.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.vertispan.j2cl.build.provided; - -import com.google.auto.service.AutoService; -import com.vertispan.j2cl.build.BuildService; -import com.vertispan.j2cl.build.DiskCache; -import com.vertispan.j2cl.build.task.*; - -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.PathMatcher; - -@AutoService(TaskFactory.class) -public class AptTask extends TaskFactory { - public static final PathMatcher BYTECODE = FileSystems.getDefault().getPathMatcher("glob:**/*.class"); - public static final PathMatcher NOT_BYTECODE = p -> !BYTECODE.matches(p); - - @Override - public String getOutputType() { - return OutputTypes.GENERATED_SOURCES; - } - - @Override - public String getTaskName() { - return "default"; - } - - @Override - public String getVersion() { - return "0"; - } - - @Override - public Task resolve(Project project, Config config, BuildService buildService) { - if (!project.hasSourcesMapped()) { - // we explicitly don't copy the generated sources, they already exist in the proj sources - return (ignored) -> {}; - } - - // we assume that bytecode was generated by javac, and will read the generated sources out of there - Input myBytecode = input(project, OutputTypes.BYTECODE, buildService).filter(NOT_BYTECODE); - - return (output) -> { - // the BytecodeTask already did the work for us, just copy sources to output - for (CachedPath entry : myBytecode.getFilesAndHashes()) { - Files.createDirectories(output.path().resolve(entry.getSourcePath()).getParent()); - Files.copy(entry.getAbsolutePath(), output.path().resolve(entry.getSourcePath())); - } - }; - } -} diff --git a/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/BytecodeTask.java b/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/BytecodeTask.java deleted file mode 100644 index fc1eaa71..00000000 --- a/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/BytecodeTask.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.vertispan.j2cl.build.provided; - -import com.google.auto.service.AutoService; -import com.google.j2cl.common.SourceUtils; -import com.vertispan.j2cl.build.BuildService; -import com.vertispan.j2cl.build.ChangedAcceptor; -import com.vertispan.j2cl.build.DiskCache; -import com.vertispan.j2cl.build.task.*; -import com.vertispan.j2cl.tools.Javac; - -import java.io.File; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.util.Collection; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * This implementation and {@link AptTask} are wired together, if you replace - * one you may need to replace the other at the same time (the SkipAptTask is an - * exception to this). - * - * The assumption is that since these are generated at the same time by a single - * invocation of javac, we want to generate the bytecode first for downstream - * projects so they can also generate their own sources. With this though, - * the AptTask should be a no-op, so it shouldn't really matter. - */ -@AutoService(TaskFactory.class) -public class BytecodeTask extends TaskFactory { - - public static final PathMatcher JAVA_SOURCES = FileSystems.getDefault().getPathMatcher("glob:**/*.java"); - public static final PathMatcher JAVA_BYTECODE = FileSystems.getDefault().getPathMatcher("glob:**/*.class"); - - @Override - public String getOutputType() { - return OutputTypes.BYTECODE; - } - - @Override - public String getTaskName() { - return "default"; - } - - @Override - public String getVersion() { - return "0"; - } - - @Override - public Task resolve(Project project, Config config, BuildService buildService) { - boolean incremental = true; - if (!project.hasSourcesMapped()) { - // instead copy the bytecode out of the jar so it can be used by downtream bytecode/apt tasks - Input existingUnpackedBytecode = input(project, OutputTypes.INPUT_SOURCES, buildService);//.filter(JAVA_BYTECODE); - return (output) -> { - for (CachedPath entry : existingUnpackedBytecode.getFilesAndHashes()) { - Files.createDirectories(output.path().resolve(entry.getSourcePath()).getParent()); - Files.copy(entry.getAbsolutePath(), output.path().resolve(entry.getSourcePath())); - } - }; - } - - // TODO just use one input for both of these - // track the dirs (with all file changes) so that APT can see things it wants - Input inputDirs = input(project, OutputTypes.INPUT_SOURCES, buildService); - // track just java files (so we can just compile them) - Input inputSources = input(project, OutputTypes.INPUT_SOURCES, buildService).filter(JAVA_SOURCES); - - List bytecodeClasspath = scope(project.getDependencies(), com.vertispan.j2cl.build.task.Dependency.Scope.COMPILE) - .stream() - .map(inputs(OutputTypes.BYTECODE, buildService)) - .collect(Collectors.toList()); - - File bootstrapClasspath = config.getBootstrapClasspath(); - List extraClasspath = config.getExtraClasspath(); - return (output) -> { - List files = inputSources.getFilesAndHashes() - .stream() - .filter( new ChangedAcceptor((com.vertispan.j2cl.build.Project) project, buildService)).collect(Collectors.toList()); - - if (files.isEmpty()) { - return;// no work to do - } - - bytecodeClasspath.stream().map(Input::getParentPaths).flatMap(Collection::stream).map(Path::toFile).forEach( - f -> System.out.println("bytecodepath: " + f.getAbsolutePath().toString())); - - List classpathDirs = Stream.of( - bytecodeClasspath.stream().map(Input::getParentPaths).flatMap(Collection::stream).map(Path::toFile), - extraClasspath.stream() //, -// inputDirs.getParentPaths().stream().map(Path::toFile) - ) - .flatMap(Function.identity()) - .collect(Collectors.toList()); - - if (incremental) { - Path bytecodePath = buildService.getDiskCache().getLastSuccessfulDirectory(new com.vertispan.j2cl.build.Input((com.vertispan.j2cl.build.Project) project, - OutputTypes.BYTECODE)); - - if (bytecodePath != null) { - classpathDirs.add(bytecodePath.resolve("results").toFile()); - } - } - - // TODO don't dump APT to the same dir? - Javac javac = new Javac(output.path().toFile(), classpathDirs, output.path().toFile(), bootstrapClasspath); - - // TODO convention for mapping to original file paths, provide FileInfo out of Inputs instead of Paths, - // automatically relativized? - List sources = files.stream() - .map(p -> SourceUtils.FileInfo.create(p.getAbsolutePath().toString(), p.getSourcePath().toString())) - .collect(Collectors.toList()); - - try { - if (!javac.compile(sources)) { - throw new RuntimeException("Failed to complete bytecode task, check log"); - } - } catch (Exception exception) { - exception.printStackTrace(); - throw exception; - } - - }; - } -} diff --git a/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/ClosureTask.java b/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/ClosureTask.java deleted file mode 100644 index 5a43c054..00000000 --- a/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/ClosureTask.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.vertispan.j2cl.build.provided; - -import com.google.auto.service.AutoService; -import com.google.javascript.jscomp.CompilationLevel; -import com.google.javascript.jscomp.CompilerOptions; -import com.google.javascript.jscomp.DependencyOptions; -import com.vertispan.j2cl.build.BuildService; -import com.vertispan.j2cl.build.DiskCache; -import com.vertispan.j2cl.build.task.*; -import com.vertispan.j2cl.tools.Closure; -import org.apache.commons.io.FileUtils; - -import java.io.File; -import java.io.IOException; -import java.nio.file.*; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@AutoService(TaskFactory.class) -public class ClosureTask extends TaskFactory { - public static final PathMatcher JS_SOURCES = FileSystems.getDefault().getPathMatcher("glob:**/*.js"); - public static final PathMatcher NATIVE_JS_SOURCES = FileSystems.getDefault().getPathMatcher("glob:**/*.native.js"); - public static final PathMatcher PLAIN_JS_SOURCES = new PathMatcher() { - @Override - public boolean matches(Path path) { - return JS_SOURCES.matches(path) && !NATIVE_JS_SOURCES.matches(path); - } - - @Override - public String toString() { - return "Only non-native JS sources"; - } - }; - @Override - public String getOutputType() { - return OutputTypes.OPTIMIZED_JS; - } - - @Override - public String getTaskName() { - return "default"; - } - - @Override - public String getVersion() { - return "0"; - } - - @Override - public Task resolve(Project project, Config config, BuildService buildService) { - // collect current project JS sources and runtime deps JS sources - // TODO filter to just JS and sourcemaps? probably not required unless we also get sources - // from the actual input source instead of copying it along each step - List jsSources = Stream.concat( - Stream.of( - input(project, OutputTypes.TRANSPILED_JS, buildService).filter(JS_SOURCES), - input(project, OutputTypes.GENERATED_SOURCES, buildService).filter(PLAIN_JS_SOURCES), - input(project, OutputTypes.INPUT_SOURCES, buildService).filter(PLAIN_JS_SOURCES) - ), - scope(project.getDependencies(), Dependency.Scope.RUNTIME) - .stream() - .flatMap(p -> Stream.of( - input(p, OutputTypes.TRANSPILED_JS, buildService).filter(JS_SOURCES), - input(p, OutputTypes.GENERATED_SOURCES, buildService).filter(PLAIN_JS_SOURCES), - input(p, OutputTypes.INPUT_SOURCES, buildService).filter(PLAIN_JS_SOURCES) - )) - ).collect(Collectors.toList()); - - - // grab configs we plan to use - String compilationLevelConfig = config.getCompilationLevel(); - String initialScriptFilename = config.getInitialScriptFilename(); - Map configDefines = config.getDefines(); - DependencyOptions.DependencyMode dependencyMode = DependencyOptions.DependencyMode.valueOf(config.getDependencyMode()); - List entrypoint = config.getEntrypoint(); - CompilerOptions.LanguageMode languageOut = CompilerOptions.LanguageMode.fromString(config.getLanguageOut()); - //TODO probably kill this, or at least make it work like an import via another task so we detect changes - Collection externs = config.getExterns(); - boolean checkAssertions = config.getCheckAssertions(); - boolean rewritePolyfills = config.getRewritePolyfills(); - boolean sourcemapsEnabled = config.getSourcemapsEnabled(); - List extraJsZips = config.getExtraJsZips(); - String env = config.getEnv(); - - String sourcemapDirectory = "sources"; - return new FinalOutputTask() { - @Override - public void execute(TaskOutput output) throws Exception { - Closure closureCompiler = new Closure(); - - File closureOutputDir = output.path().toFile(); - - CompilationLevel compilationLevel = CompilationLevel.fromString(compilationLevelConfig); - - // set up a source directory to build from, and to make sourcemaps work - // TODO move logic to the "post" phase to decide whether or not to copy the sourcemap dir - String jsOutputDir = new File(closureOutputDir + "/" + initialScriptFilename).getParent(); - File sources; - if (compilationLevel == CompilationLevel.BUNDLE) { - if (!sourcemapsEnabled) { - //TODO warn that sourcemaps are there anyway, we can't disable in bundle modes? - } - sources = new File(jsOutputDir, sourcemapDirectory); - } else { - if (sourcemapsEnabled) { - sources = new File(jsOutputDir, sourcemapDirectory);//write to the same place as in bundle mode - } else { - sources = null; - } - } - if (sources != null) { - Files.createDirectories(Paths.get(closureOutputDir.getAbsolutePath(), initialScriptFilename).getParent()); - - //TODO this is quite dirty, we should make this a configurable input to match - // which files are important - for (Input jsSource : jsSources) { - for (Path path : jsSource.getParentPaths()) { - FileUtils.copyDirectory(path.toFile(), sources); - } - } - } - - Map defines = new LinkedHashMap<>(configDefines); - - if (compilationLevel == CompilationLevel.BUNDLE) { - defines.putIfAbsent("goog.ENABLE_DEBUG_LOADER", "false");//TODO maybe overwrite instead? - } - - boolean success = closureCompiler.compile( - compilationLevel, - dependencyMode, - languageOut, - jsSources, - sources, - extraJsZips, - entrypoint, - defines, - externs, - null, - true,//TODO have this be passed in, - checkAssertions, - rewritePolyfills, - sourcemapsEnabled, - env, - closureOutputDir + "/" + initialScriptFilename - ); - - if (!success) { - throw new IllegalStateException("Closure Compiler failed, check log for details"); - } - - } - - @Override - public void finish(TaskOutput taskOutput) throws IOException { - Path webappDirectory = config.getWebappDirectory(); - if (!Files.exists(webappDirectory)) { - Files.createDirectories(webappDirectory); - } - FileUtils.copyDirectory(taskOutput.path().toFile(), webappDirectory.toFile()); - } - }; - } -} diff --git a/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/SkipAptTask.java b/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/SkipAptTask.java deleted file mode 100644 index 7324de67..00000000 --- a/j2cl-tools/src/main/java/com/vertispan/j2cl/build/provided/SkipAptTask.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.vertispan.j2cl.build.provided; - -import com.google.auto.service.AutoService; -import com.vertispan.j2cl.build.BuildService; -import com.vertispan.j2cl.build.DiskCache; -import com.vertispan.j2cl.build.task.Config; -import com.vertispan.j2cl.build.task.OutputTypes; -import com.vertispan.j2cl.build.task.Project; -import com.vertispan.j2cl.build.task.TaskFactory; - -/** - * Disables annotation processors from within this build, and relies instead on some other tooling - * already generating sources (hopefully incrementally). By producing no sources and depending on - * nothing it will run quickly and be cached after the first time even if inputs change - and without - * it, the "extra" non-stripped bytecode task is unnecessary. - */ -@AutoService(TaskFactory.class) -public class SkipAptTask extends TaskFactory { - @Override - public String getOutputType() { - return OutputTypes.GENERATED_SOURCES; - } - - @Override - public String getTaskName() { - return "skip"; - } - - @Override - public String getVersion() { - return "0"; - } - - @Override - public Task resolve(Project project, Config config, BuildService buildService) { - return (ignore) -> {}; - } -} diff --git a/pom.xml b/pom.xml index e87a44d1..c486ec4b 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,9 @@ build-caching j2cl-tasks j2cl-maven-plugin + @@ -87,6 +89,11 @@ 1.6 + + + 0.10.0-3c97afeac v20220502-1 1.11