This is an automated email from the ASF dual-hosted git repository. joshtynjala pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/royale-compiler.git
commit 42642e2089f7a21773c914828696ae6821605125 Author: Josh Tynjala <joshtynj...@apache.org> AuthorDate: Wed Feb 16 14:33:06 2022 -0800 Added --watch compiler option Watches for file system changes and re-outputs any changed compilation units. Ctrl+C to exit. --- .../royale/compiler/config/Configuration.java | 18 + .../apache/royale/compiler/clients/COMPJSC.java | 31 +- .../royale/compiler/clients/COMPJSCNative.java | 609 +++++++++-------- .../royale/compiler/clients/COMPJSCRoyale.java | 751 +++++++++++---------- .../apache/royale/compiler/clients/MXMLJSC.java | 41 +- .../royale/compiler/clients/MXMLJSCNative.java | 76 ++- .../royale/compiler/clients/MXMLJSCNode.java | 75 +- .../royale/compiler/clients/MXMLJSCRoyale.java | 78 ++- .../compiler/clients/MXMLJSCRoyaleCordova.java | 76 ++- .../codegen/mxml/royale/MXMLRoyalePublisher.java | 10 +- .../org/apache/royale/compiler/clients/COMPC.java | 39 ++ .../org/apache/royale/compiler/clients/MXMLC.java | 81 ++- .../compiler/internal/watcher/WatchThread.java | 273 ++++++++ 13 files changed, 1486 insertions(+), 672 deletions(-) diff --git a/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java b/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java index 505fd37..64b7de2 100644 --- a/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java +++ b/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java @@ -6395,4 +6395,22 @@ public class Configuration this.strictXML = strictXML; } + // + // 'watch' + // + + private boolean watch = false; + + public boolean getWatch() + { + return watch; + } + + @Config + @Mapping("watch") + public void setWatch(ConfigurationValue cv, boolean b) + { + watch = b; + } + } diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSC.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSC.java index f44094e..14fa835 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSC.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSC.java @@ -24,9 +24,6 @@ import java.util.*; import org.apache.royale.compiler.clients.problems.ProblemPrinter; import org.apache.royale.compiler.clients.problems.WorkspaceProblemFormatter; import org.apache.royale.compiler.config.CompilerDiagnosticsConstants; -import org.apache.royale.compiler.exceptions.ConfigurationException; -import org.apache.royale.compiler.exceptions.ConfigurationException.IOError; -import org.apache.royale.compiler.exceptions.ConfigurationException.MustSpecifyTarget; import org.apache.royale.compiler.internal.driver.js.goog.JSGoogCompcConfiguration; import org.apache.royale.compiler.internal.parsing.as.RoyaleASDocDelegate; import org.apache.royale.compiler.problems.ICompilerProblem; @@ -49,7 +46,8 @@ public class COMPJSC extends MXMLJSC PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_PROBLEMS(4); + FAILED_WITH_CONFIG_PROBLEMS(4), + WATCHING(1000); ExitCode(int code) { @@ -84,7 +82,10 @@ public class COMPJSC extends MXMLJSC public static void main(final String[] args) { int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -162,9 +163,6 @@ public class COMPJSC extends MXMLJSC if (continueCompilation) { - List<String> targets = config.getCompilerTargets(); - for (String target : targets) - System.out.println("target:" + target); targetloop: for (String target : config.getCompilerTargets()) { @@ -182,7 +180,7 @@ public class COMPJSC extends MXMLJSC compc.workspace.setASDocDelegate(new RoyaleASDocDelegate(true)); compc.configurationClass = JSGoogCompcConfiguration.class; result = compc.mainNoExit(removeJSArgs(args)); - if (result != COMPC.ExitCode.SUCCESS.getCode()) + if (result != COMPC.ExitCode.SUCCESS.getCode() && result != COMPC.ExitCode.WATCHING.getCode()) { problems.addAll(compc.problems.getProblems()); break targetloop; @@ -193,7 +191,7 @@ public class COMPJSC extends MXMLJSC COMPJSCRoyale royale = new COMPJSCRoyale(); lastCompiler = royale; result = royale.mainNoExit(removeASArgs(args), problems.getProblems(), false); - if (result != COMPJSCRoyale.ExitCode.SUCCESS.getCode()) + if (result != COMPJSCRoyale.ExitCode.SUCCESS.getCode() && result != COMPJSCRoyale.ExitCode.WATCHING.getCode()) { break targetloop; } @@ -203,7 +201,7 @@ public class COMPJSC extends MXMLJSC COMPJSCNative jsc = new COMPJSCNative(); lastCompiler = jsc; result = jsc.mainNoExit(removeASArgs(args), problems.getProblems(), false); - if (result != COMPJSCNative.ExitCode.SUCCESS.getCode()) + if (result != COMPJSCNative.ExitCode.SUCCESS.getCode() && result != COMPJSCNative.ExitCode.WATCHING.getCode()) { break targetloop; } @@ -243,7 +241,10 @@ public class COMPJSC extends MXMLJSC } finally { - waitAndClose(); + if (!config.getWatch() || !ExitCode.SUCCESS.equals(exitCode)) + { + waitAndClose(); + } if (outProblems != null && problems.hasFilteredProblems()) { @@ -253,7 +254,11 @@ public class COMPJSC extends MXMLJSC } } } - return exitCode.code; + if (config.getWatch() && ExitCode.SUCCESS.equals(exitCode)) + { + exitCode = ExitCode.WATCHING; + } + return exitCode.getCode(); } public COMPJSC() diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCNative.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCNative.java index e17a592..c3c432c 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCNative.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCNative.java @@ -53,6 +53,7 @@ import com.google.debugging.sourcemap.SourceMapParseException; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; +import org.apache.royale.compiler.clients.MXMLJSC.JSTargetType; import org.apache.royale.compiler.clients.problems.ProblemQuery; import org.apache.royale.compiler.codegen.js.IJSWriter; import org.apache.royale.compiler.driver.IBackend; @@ -66,6 +67,8 @@ import org.apache.royale.compiler.internal.projects.CompilerProject; import org.apache.royale.compiler.internal.projects.RoyaleJSProject; import org.apache.royale.compiler.internal.targets.RoyaleSWCTarget; import org.apache.royale.compiler.internal.units.SWCCompilationUnit; +import org.apache.royale.compiler.internal.watcher.WatchThread; +import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter; import org.apache.royale.compiler.internal.targets.JSTarget; import org.apache.royale.compiler.internal.workspaces.Workspace; import org.apache.royale.compiler.problems.ICompilerProblem; @@ -94,7 +97,8 @@ public class COMPJSCNative extends MXMLJSCNative PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_PROBLEMS(4); + FAILED_WITH_CONFIG_PROBLEMS(4), + WATCHING(1000); ExitCode(int code) { @@ -129,7 +133,10 @@ public class COMPJSCNative extends MXMLJSCNative public static void main(final String[] args) { int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -185,305 +192,314 @@ public class COMPJSCNative extends MXMLJSCNative if (jsTarget != null) { - Collection<ICompilerProblem> errors = new ArrayList<ICompilerProblem>(); - Collection<ICompilerProblem> warnings = new ArrayList<ICompilerProblem>(); - - if (!config.getCreateTargetWithErrors()) + if (!writeSWC()) { - problems.getErrorsAndWarnings(errors, warnings); - if (errors.size() > 0) - return false; + return false; } + compilationSuccess = true; + } + } + catch (Exception e) + { + System.out.println(e); + final ICompilerProblem problem = new InternalCompilerProblem(e); + problems.add(problem); + } - boolean packingSWC = false; - String outputFolderName = getOutputFilePath(); - File swcFile = new File(outputFolderName); - File jsOut = new File("js/out"); - File externsOut = new File("externs"); - ZipFile zipFile = null; - ZipOutputStream zipOutputStream = null; - String catalog = null; - StringBuilder fileList = new StringBuilder(); - if (outputFolderName.endsWith(".swc")) + return compilationSuccess; + } + + private boolean writeSWC() throws IOException, InterruptedException + { + Collection<ICompilerProblem> errors = new ArrayList<ICompilerProblem>(); + Collection<ICompilerProblem> warnings = new ArrayList<ICompilerProblem>(); + + if (!config.getCreateTargetWithErrors()) + { + problems.getErrorsAndWarnings(errors, warnings); + if (errors.size() > 0) + return false; + } + + boolean packingSWC = false; + String outputFolderName = getOutputFilePath(); + File swcFile = new File(outputFolderName); + File jsOut = new File("js/out"); + File externsOut = new File("externs"); + ZipFile zipFile = null; + ZipOutputStream zipOutputStream = null; + String catalog = null; + StringBuilder fileList = new StringBuilder(); + if (outputFolderName.endsWith(".swc")) + { + packingSWC = true; + if (!swcFile.exists()) + { + problems.add(new LibraryNotFoundProblem(outputFolderName)); + return false; + } + zipFile = new ZipFile(swcFile, ZipFile.OPEN_READ); + final InputStream catalogInputStream = SWCReader.getInputStream(zipFile, SWCReader.CATALOG_XML); + + catalog = IOUtils.toString(catalogInputStream); + catalogInputStream.close(); + zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(outputFolderName + ".new"))); + zipOutputStream.setLevel(Deflater.NO_COMPRESSION); + for (final Enumeration<? extends ZipEntry> entryEnum = zipFile.entries(); entryEnum.hasMoreElements();) + { + final ZipEntry entry = entryEnum.nextElement(); + if (!entry.getName().contains("js/out") && + !entry.getName().contains(SWCReader.CATALOG_XML)) { - packingSWC = true; - if (!swcFile.exists()) - { - problems.add(new LibraryNotFoundProblem(outputFolderName)); - return false; - } - zipFile = new ZipFile(swcFile, ZipFile.OPEN_READ); - final InputStream catalogInputStream = SWCReader.getInputStream(zipFile, SWCReader.CATALOG_XML); - - catalog = IOUtils.toString(catalogInputStream); - catalogInputStream.close(); - zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(outputFolderName + ".new"))); - zipOutputStream.setLevel(Deflater.NO_COMPRESSION); - for (final Enumeration<? extends ZipEntry> entryEnum = zipFile.entries(); entryEnum.hasMoreElements();) + if (config.isVerbose()) { - final ZipEntry entry = entryEnum.nextElement(); - if (!entry.getName().contains("js/out") && - !entry.getName().contains(SWCReader.CATALOG_XML)) - { - if (config.isVerbose()) - { - System.out.println("Copy " + entry.getName()); - } - InputStream input = zipFile.getInputStream(entry); - zipOutputStream.putNextEntry(new ZipEntry(entry.getName())); - IOUtils.copy(input, zipOutputStream); - zipOutputStream.flush(); - zipOutputStream.closeEntry(); - } + System.out.println("Copy " + entry.getName()); } - int filesIndex = catalog.indexOf("<files>"); - if (filesIndex != -1) + InputStream input = zipFile.getInputStream(entry); + zipOutputStream.putNextEntry(new ZipEntry(entry.getName())); + IOUtils.copy(input, zipOutputStream); + zipOutputStream.flush(); + zipOutputStream.closeEntry(); + } + } + int filesIndex = catalog.indexOf("<files>"); + if (filesIndex != -1) + { + int filesIndex2 = catalog.indexOf("</files>"); + String files = catalog.substring(filesIndex, filesIndex2); + int fileIndex = files.indexOf("<file", 6); + int pathIndex = files.indexOf("path="); + while (pathIndex != -1) + { + int pathIndex2 = files.indexOf("\"", pathIndex + 6); + int fileIndex2 = files.indexOf("/>", fileIndex); + String path = files.substring(pathIndex + 6, pathIndex2); + if (!path.startsWith("js/out")) { - int filesIndex2 = catalog.indexOf("</files>"); - String files = catalog.substring(filesIndex, filesIndex2); - int fileIndex = files.indexOf("<file", 6); - int pathIndex = files.indexOf("path="); - while (pathIndex != -1) - { - int pathIndex2 = files.indexOf("\"", pathIndex + 6); - int fileIndex2 = files.indexOf("/>", fileIndex); - String path = files.substring(pathIndex + 6, pathIndex2); - if (!path.startsWith("js/out")) - { - fileList.append(files.substring(fileIndex - 8, fileIndex2 + 3)); - } - pathIndex = files.indexOf("path=", pathIndex2); - fileIndex = files.indexOf("<file", fileIndex2); - } - catalog = catalog.substring(0, filesIndex) + catalog.substring(filesIndex2 + 8); + fileList.append(files.substring(fileIndex - 8, fileIndex2 + 3)); } + pathIndex = files.indexOf("path=", pathIndex2); + fileIndex = files.indexOf("<file", fileIndex2); } + catalog = catalog.substring(0, filesIndex) + catalog.substring(filesIndex2 + 8); + } + } - File outputFolder = null; - if (!packingSWC) - outputFolder = new File(outputFolderName); + File outputFolder = null; + if (!packingSWC) + outputFolder = new File(outputFolderName); - Set<String> externs = config.getExterns(); - Collection<ICompilationUnit> roots = ((RoyaleSWCTarget)target).getReachableCompilationUnits(errors); - Collection<ICompilationUnit> reachableCompilationUnits = project.getReachableCompilationUnitsInSWFOrder(roots); - for (final ICompilationUnit cu : reachableCompilationUnits) + Set<String> externs = config.getExterns(); + Collection<ICompilationUnit> roots = ((RoyaleSWCTarget)target).getReachableCompilationUnits(errors); + Collection<ICompilationUnit> reachableCompilationUnits = project.getReachableCompilationUnitsInSWFOrder(roots); + for (final ICompilationUnit cu : reachableCompilationUnits) + { + ICompilationUnit.UnitType cuType = cu.getCompilationUnitType(); + + if (cuType == ICompilationUnit.UnitType.AS_UNIT + || cuType == ICompilationUnit.UnitType.MXML_UNIT) + { + String symbol = cu.getQualifiedNames().get(0); + if (externs.contains(symbol)) continue; + + if (project.isExternalLinkage(cu)) continue; + + if (!packingSWC) { - ICompilationUnit.UnitType cuType = cu.getCompilationUnitType(); + final File outputClassFile = getOutputClassFile( + cu.getQualifiedNames().get(0), outputFolder, true); - if (cuType == ICompilationUnit.UnitType.AS_UNIT - || cuType == ICompilationUnit.UnitType.MXML_UNIT) + if (config.isVerbose()) { - String symbol = cu.getQualifiedNames().get(0); - if (externs.contains(symbol)) continue; - - if (project.isExternalLinkage(cu)) continue; - - if (!packingSWC) - { - final File outputClassFile = getOutputClassFile( - cu.getQualifiedNames().get(0), outputFolder, true); - - if (config.isVerbose()) - { - System.out.println("Compiling file: " + outputClassFile); - } - - ICompilationUnit unit = cu; - - IJSWriter writer; - if (cuType == ICompilationUnit.UnitType.AS_UNIT) - { - writer = (IJSWriter) project.getBackend().createWriter(project, - (List<ICompilerProblem>) errors, unit, - false); - } - else - { - writer = (IJSWriter) project.getBackend().createMXMLWriter( - project, (List<ICompilerProblem>) errors, - unit, false); - } - problems.addAll(errors); - - BufferedOutputStream out = new BufferedOutputStream( - new FileOutputStream(outputClassFile)); - BufferedOutputStream sourceMapOut = null; - File outputSourceMapFile = null; - if (project.config.getSourceMap()) - { - outputSourceMapFile = getOutputSourceMapFile( - cu.getQualifiedNames().get(0), outputFolder, true); - sourceMapOut = new BufferedOutputStream( - new FileOutputStream(outputSourceMapFile)); - } - writer.writeTo(out, sourceMapOut, outputSourceMapFile); - out.flush(); - out.close(); - if (sourceMapOut != null) - { - sourceMapOut.flush(); - sourceMapOut.close(); - } - writer.close(); - } - else - { - if (config.isVerbose()) - { - System.out.println("Compiling file: " + cu.getQualifiedNames().get(0)); - } - - ICompilationUnit unit = cu; - - IJSWriter writer; - if (cuType == ICompilationUnit.UnitType.AS_UNIT) - { - writer = (IJSWriter) project.getBackend().createWriter(project, - (List<ICompilerProblem>) errors, unit, - false); - } - else - { - writer = (IJSWriter) project.getBackend().createMXMLWriter( - project, (List<ICompilerProblem>) errors, - unit, false); - } - problems.addAll(errors); - - ByteArrayOutputStream temp = new ByteArrayOutputStream(); - ByteArrayOutputStream sourceMapTemp = null; - - boolean isExterns = false; - if(cu.getDefinitionPromises().size() > 0) - { - isExterns = project.isExterns(cu.getDefinitionPromises().get(0).getQualifiedName()); - } - - // if the file is @externs DON'T create source map file - if (project.config.getSourceMap() && !isExterns) - { - sourceMapTemp = new ByteArrayOutputStream(); - } - writer.writeTo(temp, sourceMapTemp, null); - - File outputClassFile = getOutputClassFile( - cu.getQualifiedNames().get(0), - isExterns ? externsOut : jsOut, - false); - String outputClassFilePath = outputClassFile.getPath(); - outputClassFilePath = outputClassFilePath.replace('\\', '/'); - if (config.isVerbose()) - { - System.out.println("Writing file: " + outputClassFilePath); - } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - temp.writeTo(baos); - writeFileToZip(zipOutputStream, outputClassFilePath, baos, fileList); - if(sourceMapTemp != null) - { - String sourceMapFilePath = getOutputSourceMapFile( - cu.getQualifiedNames().get(0), - isExterns ? externsOut : jsOut, - false).getPath(); - sourceMapFilePath = sourceMapFilePath.replace('\\', '/'); - if (config.isVerbose()) - { - System.out.println("Writing file: " + sourceMapFilePath); - } - baos = new ByteArrayOutputStream(); - processSourceMap(sourceMapTemp, baos, outputClassFile, symbol); - writeFileToZip(zipOutputStream, sourceMapFilePath, baos, fileList); - } - writer.close(); - } + System.out.println("Compiling file: " + outputClassFile); } - else if (cuType == ICompilationUnit.UnitType.SWC_UNIT) + + ICompilationUnit unit = cu; + + IJSWriter writer; + if (cuType == ICompilationUnit.UnitType.AS_UNIT) { - String symbol = cu.getQualifiedNames().get(0); - if (externs.contains(symbol)) continue; - if (project.isExternalLinkage(cu)) continue; - if (!packingSWC) - { - // we probably shouldn't skip this -JT - continue; - } + writer = (IJSWriter) project.getBackend().createWriter(project, + (List<ICompilerProblem>) errors, unit, + false); + } + else + { + writer = (IJSWriter) project.getBackend().createMXMLWriter( + project, (List<ICompilerProblem>) errors, + unit, false); + } + problems.addAll(errors); - // if another .swc file is on our library-path, we must - // include the .js (and .js.map) files because the - // bytecode will also be included. if we have the - // bytecode, but not the .js files, the compiler won't - // know where to find the .js files. that's really bad. - - // if the bytecode and .js files should not be included, - // then the developer is expected to use - // external-library-path instead of library-path. - - SWCCompilationUnit swcCU = (SWCCompilationUnit) cu; - String outputClassFile = getOutputClassFile( - cu.getQualifiedNames().get(0), - jsOut, - false).getPath(); - outputClassFile = outputClassFile.replace('\\', '/'); - ISWCFileEntry fileEntry = swcCU.getSWC().getFile(outputClassFile); - if (fileEntry == null) - { - continue; - } - if (config.isVerbose()) - { - System.out.println("Writing file: " + outputClassFile + " from SWC: " + swcCU.getAbsoluteFilename()); - } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - InputStream fileStream = fileEntry.createInputStream(); - IOUtils.copy(fileStream, baos); - fileStream.close(); - writeFileToZip(zipOutputStream, outputClassFile, baos, fileList); - - String outputMapFile = outputClassFile + ".map"; - fileEntry = swcCU.getSWC().getFile(outputMapFile); - if (fileEntry == null) - { - continue; - } + BufferedOutputStream out = new BufferedOutputStream( + new FileOutputStream(outputClassFile)); + BufferedOutputStream sourceMapOut = null; + File outputSourceMapFile = null; + if (project.config.getSourceMap()) + { + outputSourceMapFile = getOutputSourceMapFile( + cu.getQualifiedNames().get(0), outputFolder, true); + sourceMapOut = new BufferedOutputStream( + new FileOutputStream(outputSourceMapFile)); + } + writer.writeTo(out, sourceMapOut, outputSourceMapFile); + out.flush(); + out.close(); + if (sourceMapOut != null) + { + sourceMapOut.flush(); + sourceMapOut.close(); + } + writer.close(); + } + else + { + if (config.isVerbose()) + { + System.out.println("Compiling file: " + cu.getQualifiedNames().get(0)); + } + + ICompilationUnit unit = cu; + + IJSWriter writer; + if (cuType == ICompilationUnit.UnitType.AS_UNIT) + { + writer = (IJSWriter) project.getBackend().createWriter(project, + (List<ICompilerProblem>) errors, unit, + false); + } + else + { + writer = (IJSWriter) project.getBackend().createMXMLWriter( + project, (List<ICompilerProblem>) errors, + unit, false); + } + problems.addAll(errors); + + ByteArrayOutputStream temp = new ByteArrayOutputStream(); + ByteArrayOutputStream sourceMapTemp = null; + + boolean isExterns = false; + if(cu.getDefinitionPromises().size() > 0) + { + isExterns = project.isExterns(cu.getDefinitionPromises().get(0).getQualifiedName()); + } + + // if the file is @externs DON'T create source map file + if (project.config.getSourceMap() && !isExterns) + { + sourceMapTemp = new ByteArrayOutputStream(); + } + writer.writeTo(temp, sourceMapTemp, null); + + File outputClassFile = getOutputClassFile( + cu.getQualifiedNames().get(0), + isExterns ? externsOut : jsOut, + false); + String outputClassFilePath = outputClassFile.getPath(); + outputClassFilePath = outputClassFilePath.replace('\\', '/'); + if (config.isVerbose()) + { + System.out.println("Writing file: " + outputClassFilePath); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + temp.writeTo(baos); + writeFileToZip(zipOutputStream, outputClassFilePath, baos, fileList); + if(sourceMapTemp != null) + { + String sourceMapFilePath = getOutputSourceMapFile( + cu.getQualifiedNames().get(0), + isExterns ? externsOut : jsOut, + false).getPath(); + sourceMapFilePath = sourceMapFilePath.replace('\\', '/'); if (config.isVerbose()) { - System.out.println("Writing file: " + outputMapFile + " from SWC: " + swcCU.getAbsoluteFilename()); + System.out.println("Writing file: " + sourceMapFilePath); } baos = new ByteArrayOutputStream(); - fileStream = fileEntry.createInputStream(); - IOUtils.copy(fileStream, baos); - fileStream.close(); - writeFileToZip(zipOutputStream, outputMapFile, baos, fileList); + processSourceMap(sourceMapTemp, baos, outputClassFile, symbol); + writeFileToZip(zipOutputStream, sourceMapFilePath, baos, fileList); } + writer.close(); } - if (packingSWC) + } + else if (cuType == ICompilationUnit.UnitType.SWC_UNIT) + { + String symbol = cu.getQualifiedNames().get(0); + if (externs.contains(symbol)) continue; + if (project.isExternalLinkage(cu)) continue; + if (!packingSWC) { - zipFile.close(); - int libraryIndex = catalog.indexOf("</libraries>"); - catalog = catalog.substring(0, libraryIndex + 13) + - " <files>\n" + fileList.toString() + " </files>" + - catalog.substring(libraryIndex + 13); - zipOutputStream.putNextEntry(new ZipEntry(SWCReader.CATALOG_XML)); - zipOutputStream.write(catalog.getBytes()); - zipOutputStream.flush(); - zipOutputStream.closeEntry(); - zipOutputStream.flush(); - zipOutputStream.close(); - swcFile.delete(); - File newSWCFile = new File(outputFolderName + ".new"); - newSWCFile.renameTo(swcFile); + // we probably shouldn't skip this -JT + continue; } - compilationSuccess = true; + + // if another .swc file is on our library-path, we must + // include the .js (and .js.map) files because the + // bytecode will also be included. if we have the + // bytecode, but not the .js files, the compiler won't + // know where to find the .js files. that's really bad. + + // if the bytecode and .js files should not be included, + // then the developer is expected to use + // external-library-path instead of library-path. + + SWCCompilationUnit swcCU = (SWCCompilationUnit) cu; + String outputClassFile = getOutputClassFile( + cu.getQualifiedNames().get(0), + jsOut, + false).getPath(); + outputClassFile = outputClassFile.replace('\\', '/'); + ISWCFileEntry fileEntry = swcCU.getSWC().getFile(outputClassFile); + if (fileEntry == null) + { + continue; + } + if (config.isVerbose()) + { + System.out.println("Writing file: " + outputClassFile + " from SWC: " + swcCU.getAbsoluteFilename()); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InputStream fileStream = fileEntry.createInputStream(); + IOUtils.copy(fileStream, baos); + fileStream.close(); + writeFileToZip(zipOutputStream, outputClassFile, baos, fileList); + + String outputMapFile = outputClassFile + ".map"; + fileEntry = swcCU.getSWC().getFile(outputMapFile); + if (fileEntry == null) + { + continue; + } + if (config.isVerbose()) + { + System.out.println("Writing file: " + outputMapFile + " from SWC: " + swcCU.getAbsoluteFilename()); + } + baos = new ByteArrayOutputStream(); + fileStream = fileEntry.createInputStream(); + IOUtils.copy(fileStream, baos); + fileStream.close(); + writeFileToZip(zipOutputStream, outputMapFile, baos, fileList); } } - catch (Exception e) + if (packingSWC) { - System.out.println(e); - final ICompilerProblem problem = new InternalCompilerProblem(e); - problems.add(problem); + zipFile.close(); + int libraryIndex = catalog.indexOf("</libraries>"); + catalog = catalog.substring(0, libraryIndex + 13) + + " <files>\n" + fileList.toString() + " </files>" + + catalog.substring(libraryIndex + 13); + zipOutputStream.putNextEntry(new ZipEntry(SWCReader.CATALOG_XML)); + zipOutputStream.write(catalog.getBytes()); + zipOutputStream.flush(); + zipOutputStream.closeEntry(); + zipOutputStream.flush(); + zipOutputStream.close(); + swcFile.delete(); + File newSWCFile = new File(outputFolderName + ".new"); + newSWCFile.renameTo(swcFile); } - - return compilationSuccess; + return true; } private void processSourceMap(ByteArrayOutputStream sourceMapTemp, ByteArrayOutputStream baos, File outputClassFile, String symbol) @@ -580,6 +596,55 @@ public class COMPJSCNative extends MXMLJSCNative fileList.append(" <file path=\"" + entryFilePath + "\" mod=\"" + fileDate + "\"/>\n"); } + @Override + protected void setupWatcher() + { + if (!config.getWatch()) + { + return; + } + IWatchWriter writer = new IWatchWriter() + { + private long startTime; + + public void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException + { + startTime = System.nanoTime(); + workspace.startBuilding(); + try + { + target = project.getBackend().createTarget(project, + getTargetSettings(), null); + ((JSTarget) target).build(null, problems); + } + finally + { + workspace.doneBuilding(); + } + } + + public void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException + { + try + { + if (!writeSWC()) + { + throw new IOException("Failed to write SWC file."); + } + + long endTime = System.nanoTime(); + System.out.println((endTime - startTime) / 1e9 + " seconds"); + } + finally + { + workspace.doneBuilding(); + } + } + }; + WatchThread watcherThread = new WatchThread(JSTargetType.JS_NATIVE.getText(), writer, config, project, workspace, problems); + watcherThread.start(); + } + /** * Build target artifact. * diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCRoyale.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCRoyale.java index 5a084e6..763ac51 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCRoyale.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCRoyale.java @@ -45,6 +45,7 @@ import com.google.debugging.sourcemap.SourceMapParseException; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; +import org.apache.royale.compiler.clients.MXMLJSC.JSTargetType; import org.apache.royale.compiler.clients.problems.ProblemQuery; import org.apache.royale.compiler.codegen.js.IJSWriter; import org.apache.royale.compiler.driver.IBackend; @@ -58,6 +59,8 @@ import org.apache.royale.compiler.internal.projects.CompilerProject; import org.apache.royale.compiler.internal.projects.RoyaleJSProject; import org.apache.royale.compiler.internal.targets.RoyaleSWCTarget; import org.apache.royale.compiler.internal.units.SWCCompilationUnit; +import org.apache.royale.compiler.internal.watcher.WatchThread; +import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter; import org.apache.royale.compiler.internal.targets.JSTarget; import org.apache.royale.compiler.internal.workspaces.Workspace; import org.apache.royale.compiler.problems.ICompilerProblem; @@ -87,7 +90,8 @@ public class COMPJSCRoyale extends MXMLJSCRoyale PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_PROBLEMS(4); + FAILED_WITH_CONFIG_PROBLEMS(4), + WATCHING(1000); ExitCode(int code) { @@ -122,7 +126,10 @@ public class COMPJSCRoyale extends MXMLJSCRoyale public static void main(final String[] args) { int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -178,379 +185,388 @@ public class COMPJSCRoyale extends MXMLJSCRoyale if (jsTarget != null) { - Collection<ICompilerProblem> errors = new ArrayList<ICompilerProblem>(); - Collection<ICompilerProblem> warnings = new ArrayList<ICompilerProblem>(); - - if (!config.getCreateTargetWithErrors()) + if (!writeSWC()) { - problems.getErrorsAndWarnings(errors, warnings); - if (errors.size() > 0) - return false; + return false; } + compilationSuccess = true; + } + } + catch (Exception e) + { + System.out.println(e); + final ICompilerProblem problem = new InternalCompilerProblem(e); + problems.add(problem); + } - boolean packingSWC = false; - String outputFolderName = getOutputFilePath(); - File swcFile = new File(outputFolderName); - File jsOut = new File("js/out"); - File externsOut = new File("externs"); - ZipFile zipFile = null; - ZipOutputStream zipOutputStream = null; - String catalog = null; - StringBuilder fileList = new StringBuilder(); - if (outputFolderName.endsWith(".swc")) + return compilationSuccess; + } + + private boolean writeSWC() throws IOException, InterruptedException + { + Collection<ICompilerProblem> errors = new ArrayList<ICompilerProblem>(); + Collection<ICompilerProblem> warnings = new ArrayList<ICompilerProblem>(); + + if (!config.getCreateTargetWithErrors()) + { + problems.getErrorsAndWarnings(errors, warnings); + if (errors.size() > 0) + return false; + } + + boolean packingSWC = false; + String outputFolderName = getOutputFilePath(); + File swcFile = new File(outputFolderName); + File jsOut = new File("js/out"); + File externsOut = new File("externs"); + ZipFile zipFile = null; + ZipOutputStream zipOutputStream = null; + String catalog = null; + StringBuilder fileList = new StringBuilder(); + if (outputFolderName.endsWith(".swc")) + { + packingSWC = true; + if (!swcFile.exists()) + { + problems.add(new LibraryNotFoundProblem(outputFolderName)); + return false; + } + zipFile = new ZipFile(swcFile, ZipFile.OPEN_READ); + final InputStream catalogInputStream = SWCReader.getInputStream(zipFile, SWCReader.CATALOG_XML); + + catalog = IOUtils.toString(catalogInputStream); + catalogInputStream.close(); + zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(outputFolderName + ".new"))); + zipOutputStream.setLevel(Deflater.NO_COMPRESSION); + for (final Enumeration<? extends ZipEntry> entryEnum = zipFile.entries(); entryEnum.hasMoreElements();) + { + final ZipEntry entry = entryEnum.nextElement(); + if (!entry.getName().contains("js/out") && + !entry.getName().contains(SWCReader.CATALOG_XML)) { - packingSWC = true; - if (!swcFile.exists()) - { - problems.add(new LibraryNotFoundProblem(outputFolderName)); - return false; - } - zipFile = new ZipFile(swcFile, ZipFile.OPEN_READ); - final InputStream catalogInputStream = SWCReader.getInputStream(zipFile, SWCReader.CATALOG_XML); - - catalog = IOUtils.toString(catalogInputStream); - catalogInputStream.close(); - zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(outputFolderName + ".new"))); - zipOutputStream.setLevel(Deflater.NO_COMPRESSION); - for (final Enumeration<? extends ZipEntry> entryEnum = zipFile.entries(); entryEnum.hasMoreElements();) + if (config.isVerbose()) { - final ZipEntry entry = entryEnum.nextElement(); - if (!entry.getName().contains("js/out") && - !entry.getName().contains(SWCReader.CATALOG_XML)) - { - if (config.isVerbose()) - { - System.out.println("Copy " + entry.getName()); - } - InputStream input = zipFile.getInputStream(entry); - ZipEntry ze = new ZipEntry(entry.getName()); - ze.setMethod(ZipEntry.STORED); - ze.setSize(entry.getSize()); - ze.setCompressedSize(entry.getCompressedSize()); - ze.setCrc(entry.getCrc()); - long fileDate = System.currentTimeMillis(); - long zipFileDate = fileDate; - String metadataDate = targetSettings.getSWFMetadataDate(); - if (metadataDate != null) - { - String metadataFormat = targetSettings.getSWFMetadataDateFormat(); - try { - SimpleDateFormat sdf = new SimpleDateFormat(metadataFormat); - Date d = sdf.parse(metadataDate); - Calendar cal = new GregorianCalendar(); - cal.setTime(d); - sdf.setTimeZone(TimeZone.getTimeZone("UTC")); - d = sdf.parse(metadataDate); - fileDate = d.getTime(); - ZonedDateTime zdt = ZonedDateTime.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), - cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), 0, ZoneId.systemDefault()); - zipFileDate = zdt.toInstant().toEpochMilli(); - } catch (ParseException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e1) { - e1.printStackTrace(); - } - } - ze.setTime(zipFileDate); - zipOutputStream.putNextEntry(ze); - IOUtils.copy(input, zipOutputStream); - zipOutputStream.flush(); - zipOutputStream.closeEntry(); + System.out.println("Copy " + entry.getName()); + } + InputStream input = zipFile.getInputStream(entry); + ZipEntry ze = new ZipEntry(entry.getName()); + ze.setMethod(ZipEntry.STORED); + ze.setSize(entry.getSize()); + ze.setCompressedSize(entry.getCompressedSize()); + ze.setCrc(entry.getCrc()); + long fileDate = System.currentTimeMillis(); + long zipFileDate = fileDate; + String metadataDate = targetSettings.getSWFMetadataDate(); + if (metadataDate != null) + { + String metadataFormat = targetSettings.getSWFMetadataDateFormat(); + try { + SimpleDateFormat sdf = new SimpleDateFormat(metadataFormat); + Date d = sdf.parse(metadataDate); + Calendar cal = new GregorianCalendar(); + cal.setTime(d); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + d = sdf.parse(metadataDate); + fileDate = d.getTime(); + ZonedDateTime zdt = ZonedDateTime.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), + cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), 0, ZoneId.systemDefault()); + zipFileDate = zdt.toInstant().toEpochMilli(); + } catch (ParseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalArgumentException e1) { + e1.printStackTrace(); } } - int filesIndex = catalog.indexOf("<files>"); - if (filesIndex != -1) + ze.setTime(zipFileDate); + zipOutputStream.putNextEntry(ze); + IOUtils.copy(input, zipOutputStream); + zipOutputStream.flush(); + zipOutputStream.closeEntry(); + } + } + int filesIndex = catalog.indexOf("<files>"); + if (filesIndex != -1) + { + int filesIndex2 = catalog.indexOf("</files>"); + String files = catalog.substring(filesIndex, filesIndex2); + int fileIndex = files.indexOf("<file", 6); + int pathIndex = files.indexOf("path="); + while (pathIndex != -1) + { + int pathIndex2 = files.indexOf("\"", pathIndex + 6); + int fileIndex2 = files.indexOf("/>", fileIndex); + String path = files.substring(pathIndex + 6, pathIndex2); + if (!path.startsWith("js/out")) { - int filesIndex2 = catalog.indexOf("</files>"); - String files = catalog.substring(filesIndex, filesIndex2); - int fileIndex = files.indexOf("<file", 6); - int pathIndex = files.indexOf("path="); - while (pathIndex != -1) - { - int pathIndex2 = files.indexOf("\"", pathIndex + 6); - int fileIndex2 = files.indexOf("/>", fileIndex); - String path = files.substring(pathIndex + 6, pathIndex2); - if (!path.startsWith("js/out")) - { - fileList.append(files.substring(fileIndex - 8, fileIndex2 + 3)); - } - pathIndex = files.indexOf("path=", pathIndex2); - fileIndex = files.indexOf("<file", fileIndex2); - } - catalog = catalog.substring(0, filesIndex) + catalog.substring(filesIndex2 + 8); + fileList.append(files.substring(fileIndex - 8, fileIndex2 + 3)); } + pathIndex = files.indexOf("path=", pathIndex2); + fileIndex = files.indexOf("<file", fileIndex2); } + catalog = catalog.substring(0, filesIndex) + catalog.substring(filesIndex2 + 8); + } + } - File outputFolder = null; - if (!packingSWC) - outputFolder = new File(outputFolderName); + File outputFolder = null; + if (!packingSWC) + outputFolder = new File(outputFolderName); - Set<String> externs = config.getExterns(); - Collection<ICompilationUnit> roots = ((RoyaleSWCTarget)target).getReachableCompilationUnits(errors); - Collection<ICompilationUnit> reachableCompilationUnits = project.getReachableCompilationUnitsInSWFOrder(roots); - for (final ICompilationUnit cu : reachableCompilationUnits) + Set<String> externs = config.getExterns(); + Collection<ICompilationUnit> roots = ((RoyaleSWCTarget)target).getReachableCompilationUnits(errors); + Collection<ICompilationUnit> reachableCompilationUnits = project.getReachableCompilationUnitsInSWFOrder(roots); + for (final ICompilationUnit cu : reachableCompilationUnits) + { + ICompilationUnit.UnitType cuType = cu.getCompilationUnitType(); + + if (cuType == ICompilationUnit.UnitType.AS_UNIT + || cuType == ICompilationUnit.UnitType.MXML_UNIT) + { + String symbol = cu.getQualifiedNames().get(0); + if (externs.contains(symbol)) continue; + + if (project.isExternalLinkage(cu)) continue; + + if (!packingSWC) { - ICompilationUnit.UnitType cuType = cu.getCompilationUnitType(); + final File outputClassFile = getOutputClassFile( + cu.getQualifiedNames().get(0), outputFolder, true); - if (cuType == ICompilationUnit.UnitType.AS_UNIT - || cuType == ICompilationUnit.UnitType.MXML_UNIT) + if (config.isVerbose()) { - String symbol = cu.getQualifiedNames().get(0); - if (externs.contains(symbol)) continue; - - if (project.isExternalLinkage(cu)) continue; - - if (!packingSWC) - { - final File outputClassFile = getOutputClassFile( - cu.getQualifiedNames().get(0), outputFolder, true); - - if (config.isVerbose()) - { - System.out.println("Compiling file: " + outputClassFile); - } - - ICompilationUnit unit = cu; - - IJSWriter writer; - if (cuType == ICompilationUnit.UnitType.AS_UNIT) - { - writer = (IJSWriter) project.getBackend().createWriter(project, - (List<ICompilerProblem>) problems.getProblems(), unit, - false); - } - else - { - writer = (IJSWriter) project.getBackend().createMXMLWriter( - project, (List<ICompilerProblem>) problems.getProblems(), - unit, false); - } - - BufferedOutputStream out = new BufferedOutputStream( - new FileOutputStream(outputClassFile)); - BufferedOutputStream sourceMapOut = null; - File outputSourceMapFile = null; - if (project.config.getSourceMap()) - { - outputSourceMapFile = getOutputSourceMapFile( - cu.getQualifiedNames().get(0), outputFolder, true); - sourceMapOut = new BufferedOutputStream( - new FileOutputStream(outputSourceMapFile)); - } - writer.writeTo(out, sourceMapOut, outputSourceMapFile); - out.flush(); - out.close(); - if (sourceMapOut != null) - { - sourceMapOut.flush(); - sourceMapOut.close(); - } - writer.close(); - } - else - { - if (config.isVerbose()) - { - System.out.println("Compiling file: " + cu.getQualifiedNames().get(0)); - } - - ICompilationUnit unit = cu; - - IJSWriter writer; - if (cuType == ICompilationUnit.UnitType.AS_UNIT) - { - writer = (IJSWriter) project.getBackend().createWriter(project, - (List<ICompilerProblem>) problems.getProblems(), unit, - false); - } - else - { - writer = (IJSWriter) project.getBackend().createMXMLWriter( - project, (List<ICompilerProblem>) problems.getProblems(), - unit, false); - } - - ByteArrayOutputStream temp = new ByteArrayOutputStream(); - ByteArrayOutputStream sourceMapTemp = null; - - boolean isExterns = false; - if(cu.getDefinitionPromises().size() > 0) - { - isExterns = project.isExterns(cu.getDefinitionPromises().get(0).getQualifiedName()); - } - - // if the file is @externs DON'T create source map file - if (project.config.getSourceMap() && !isExterns) - { - sourceMapTemp = new ByteArrayOutputStream(); - } - writer.writeTo(temp, sourceMapTemp, null); - - File outputClassFile = getOutputClassFile( - cu.getQualifiedNames().get(0), - isExterns ? externsOut : jsOut, - false); - String outputClassFilePath = outputClassFile.getPath(); - outputClassFilePath = outputClassFilePath.replace('\\', '/'); - if (config.isVerbose()) - { - System.out.println("Writing file: " + outputClassFilePath); - } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - temp.writeTo(baos); - writeFileToZip(zipOutputStream, outputClassFilePath, baos, fileList); - - if(sourceMapTemp != null) - { - String sourceMapFilePath = getOutputSourceMapFile( - cu.getQualifiedNames().get(0), - isExterns ? externsOut : jsOut, - false).getPath(); - sourceMapFilePath = sourceMapFilePath.replace('\\', '/'); - if (config.isVerbose()) - { - System.out.println("Writing file: " + sourceMapFilePath); - } - baos = new ByteArrayOutputStream(); - processSourceMap(sourceMapTemp, baos, outputClassFile, symbol); - writeFileToZip(zipOutputStream, sourceMapFilePath, baos, fileList); - } - writer.close(); - } + System.out.println("Compiling file: " + outputClassFile); } - else if (cuType == ICompilationUnit.UnitType.SWC_UNIT) + + ICompilationUnit unit = cu; + + IJSWriter writer; + if (cuType == ICompilationUnit.UnitType.AS_UNIT) { - String symbol = cu.getQualifiedNames().get(0); - if (externs.contains(symbol)) continue; - if (project.isExternalLinkage(cu)) continue; - if (!packingSWC) - { - // we probably shouldn't skip this -JT - continue; - } + writer = (IJSWriter) project.getBackend().createWriter(project, + (List<ICompilerProblem>) problems.getProblems(), unit, + false); + } + else + { + writer = (IJSWriter) project.getBackend().createMXMLWriter( + project, (List<ICompilerProblem>) problems.getProblems(), + unit, false); + } - // if another .swc file is on our library-path, we must - // include the .js (and .js.map) files because the - // bytecode will also be included. if we have the - // bytecode, but not the .js files, the compiler won't - // know where to find the .js files. that's really bad. - - // if the bytecode and .js files should not be included, - // then the developer is expected to use - // external-library-path instead of library-path. - - SWCCompilationUnit swcCU = (SWCCompilationUnit) cu; - String outputClassFile = getOutputClassFile( - cu.getQualifiedNames().get(0), - jsOut, - false).getPath(); - outputClassFile = outputClassFile.replace('\\', '/'); - ISWCFileEntry fileEntry = swcCU.getSWC().getFile(outputClassFile); - if (fileEntry == null) - { - continue; - } - if (config.isVerbose()) - { - System.out.println("Writing file: " + outputClassFile + " from SWC: " + swcCU.getAbsoluteFilename()); - } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - InputStream fileStream = fileEntry.createInputStream(); - IOUtils.copy(fileStream, baos); - fileStream.close(); - writeFileToZip(zipOutputStream, outputClassFile, baos, fileList); - - String outputMapFile = outputClassFile + ".map"; - fileEntry = swcCU.getSWC().getFile(outputMapFile); - if (fileEntry == null) - { - continue; - } + BufferedOutputStream out = new BufferedOutputStream( + new FileOutputStream(outputClassFile)); + BufferedOutputStream sourceMapOut = null; + File outputSourceMapFile = null; + if (project.config.getSourceMap()) + { + outputSourceMapFile = getOutputSourceMapFile( + cu.getQualifiedNames().get(0), outputFolder, true); + sourceMapOut = new BufferedOutputStream( + new FileOutputStream(outputSourceMapFile)); + } + writer.writeTo(out, sourceMapOut, outputSourceMapFile); + out.flush(); + out.close(); + if (sourceMapOut != null) + { + sourceMapOut.flush(); + sourceMapOut.close(); + } + writer.close(); + } + else + { + if (config.isVerbose()) + { + System.out.println("Compiling file: " + cu.getQualifiedNames().get(0)); + } + + ICompilationUnit unit = cu; + + IJSWriter writer; + if (cuType == ICompilationUnit.UnitType.AS_UNIT) + { + writer = (IJSWriter) project.getBackend().createWriter(project, + (List<ICompilerProblem>) problems.getProblems(), unit, + false); + } + else + { + writer = (IJSWriter) project.getBackend().createMXMLWriter( + project, (List<ICompilerProblem>) problems.getProblems(), + unit, false); + } + + ByteArrayOutputStream temp = new ByteArrayOutputStream(); + ByteArrayOutputStream sourceMapTemp = null; + + boolean isExterns = false; + if(cu.getDefinitionPromises().size() > 0) + { + isExterns = project.isExterns(cu.getDefinitionPromises().get(0).getQualifiedName()); + } + + // if the file is @externs DON'T create source map file + if (project.config.getSourceMap() && !isExterns) + { + sourceMapTemp = new ByteArrayOutputStream(); + } + writer.writeTo(temp, sourceMapTemp, null); + + File outputClassFile = getOutputClassFile( + cu.getQualifiedNames().get(0), + isExterns ? externsOut : jsOut, + false); + String outputClassFilePath = outputClassFile.getPath(); + outputClassFilePath = outputClassFilePath.replace('\\', '/'); + if (config.isVerbose()) + { + System.out.println("Writing file: " + outputClassFilePath); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + temp.writeTo(baos); + writeFileToZip(zipOutputStream, outputClassFilePath, baos, fileList); + + if(sourceMapTemp != null) + { + String sourceMapFilePath = getOutputSourceMapFile( + cu.getQualifiedNames().get(0), + isExterns ? externsOut : jsOut, + false).getPath(); + sourceMapFilePath = sourceMapFilePath.replace('\\', '/'); if (config.isVerbose()) { - System.out.println("Writing file: " + outputMapFile + " from SWC: " + swcCU.getAbsoluteFilename()); + System.out.println("Writing file: " + sourceMapFilePath); } baos = new ByteArrayOutputStream(); - fileStream = fileEntry.createInputStream(); - IOUtils.copy(fileStream, baos); - fileStream.close(); - writeFileToZip(zipOutputStream, outputMapFile, baos, fileList); + processSourceMap(sourceMapTemp, baos, outputClassFile, symbol); + writeFileToZip(zipOutputStream, sourceMapFilePath, baos, fileList); } + writer.close(); } - if (!config.getCreateTargetWithErrors()) + } + else if (cuType == ICompilationUnit.UnitType.SWC_UNIT) + { + String symbol = cu.getQualifiedNames().get(0); + if (externs.contains(symbol)) continue; + if (project.isExternalLinkage(cu)) continue; + if (!packingSWC) { - errors.clear(); - warnings.clear(); - problems.getErrorsAndWarnings(errors, warnings); - if (errors.size() > 0) - return false; + // we probably shouldn't skip this -JT + continue; } - if (packingSWC) + + // if another .swc file is on our library-path, we must + // include the .js (and .js.map) files because the + // bytecode will also be included. if we have the + // bytecode, but not the .js files, the compiler won't + // know where to find the .js files. that's really bad. + + // if the bytecode and .js files should not be included, + // then the developer is expected to use + // external-library-path instead of library-path. + + SWCCompilationUnit swcCU = (SWCCompilationUnit) cu; + String outputClassFile = getOutputClassFile( + cu.getQualifiedNames().get(0), + jsOut, + false).getPath(); + outputClassFile = outputClassFile.replace('\\', '/'); + ISWCFileEntry fileEntry = swcCU.getSWC().getFile(outputClassFile); + if (fileEntry == null) { - zipFile.close(); - long fileDate = System.currentTimeMillis(); - long zipFileDate = fileDate; - String metadataDate = targetSettings.getSWFMetadataDate(); - if (metadataDate != null) - { - String metadataFormat = targetSettings.getSWFMetadataDateFormat(); - try { - SimpleDateFormat sdf = new SimpleDateFormat(metadataFormat); - Date d = sdf.parse(metadataDate); - Calendar cal = new GregorianCalendar(); - cal.setTime(d); - sdf.setTimeZone(TimeZone.getTimeZone("UTC")); - d = sdf.parse(metadataDate); - fileDate = d.getTime(); - ZonedDateTime zdt = ZonedDateTime.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), - cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), 0, ZoneId.systemDefault()); - zipFileDate = zdt.toInstant().toEpochMilli(); - } catch (ParseException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e1) { - e1.printStackTrace(); - } - } - int libraryIndex = catalog.indexOf("</libraries>"); - catalog = catalog.substring(0, libraryIndex + 13) + - " <files>\n" + fileList.toString() + " </files>" + - catalog.substring(libraryIndex + 13); - ZipEntry ze = new ZipEntry(SWCReader.CATALOG_XML); - ze.setTime(zipFileDate); - ze.setMethod(ZipEntry.STORED); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - baos.write(catalog.getBytes()); - ze.setSize(baos.size()); - ze.setCompressedSize(baos.size()); - CRC32 crc = new CRC32(); - crc.reset(); - crc.update(baos.toByteArray()); - ze.setCrc(crc.getValue()); - - zipOutputStream.putNextEntry(ze); - baos.writeTo(zipOutputStream); - zipOutputStream.flush(); - zipOutputStream.closeEntry(); - zipOutputStream.flush(); - zipOutputStream.close(); - swcFile.delete(); - File newSWCFile = new File(outputFolderName + ".new"); - newSWCFile.renameTo(swcFile); + continue; } - compilationSuccess = true; + if (config.isVerbose()) + { + System.out.println("Writing file: " + outputClassFile + " from SWC: " + swcCU.getAbsoluteFilename()); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InputStream fileStream = fileEntry.createInputStream(); + IOUtils.copy(fileStream, baos); + fileStream.close(); + writeFileToZip(zipOutputStream, outputClassFile, baos, fileList); + + String outputMapFile = outputClassFile + ".map"; + fileEntry = swcCU.getSWC().getFile(outputMapFile); + if (fileEntry == null) + { + continue; + } + if (config.isVerbose()) + { + System.out.println("Writing file: " + outputMapFile + " from SWC: " + swcCU.getAbsoluteFilename()); + } + baos = new ByteArrayOutputStream(); + fileStream = fileEntry.createInputStream(); + IOUtils.copy(fileStream, baos); + fileStream.close(); + writeFileToZip(zipOutputStream, outputMapFile, baos, fileList); } } - catch (Exception e) + if (!config.getCreateTargetWithErrors()) { - System.out.println(e); - final ICompilerProblem problem = new InternalCompilerProblem(e); - problems.add(problem); + errors.clear(); + warnings.clear(); + problems.getErrorsAndWarnings(errors, warnings); + if (errors.size() > 0) + return false; } - - return compilationSuccess; + if (packingSWC) + { + zipFile.close(); + long fileDate = System.currentTimeMillis(); + long zipFileDate = fileDate; + String metadataDate = targetSettings.getSWFMetadataDate(); + if (metadataDate != null) + { + String metadataFormat = targetSettings.getSWFMetadataDateFormat(); + try { + SimpleDateFormat sdf = new SimpleDateFormat(metadataFormat); + Date d = sdf.parse(metadataDate); + Calendar cal = new GregorianCalendar(); + cal.setTime(d); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + d = sdf.parse(metadataDate); + fileDate = d.getTime(); + ZonedDateTime zdt = ZonedDateTime.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), + cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), 0, ZoneId.systemDefault()); + zipFileDate = zdt.toInstant().toEpochMilli(); + } catch (ParseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalArgumentException e1) { + e1.printStackTrace(); + } + } + int libraryIndex = catalog.indexOf("</libraries>"); + catalog = catalog.substring(0, libraryIndex + 13) + + " <files>\n" + fileList.toString() + " </files>" + + catalog.substring(libraryIndex + 13); + ZipEntry ze = new ZipEntry(SWCReader.CATALOG_XML); + ze.setTime(zipFileDate); + ze.setMethod(ZipEntry.STORED); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(catalog.getBytes()); + ze.setSize(baos.size()); + ze.setCompressedSize(baos.size()); + CRC32 crc = new CRC32(); + crc.reset(); + crc.update(baos.toByteArray()); + ze.setCrc(crc.getValue()); + + zipOutputStream.putNextEntry(ze); + baos.writeTo(zipOutputStream); + zipOutputStream.flush(); + zipOutputStream.closeEntry(); + zipOutputStream.flush(); + zipOutputStream.close(); + swcFile.delete(); + File newSWCFile = new File(outputFolderName + ".new"); + newSWCFile.renameTo(swcFile); + } + return true; } private void processSourceMap(ByteArrayOutputStream sourceMapTemp, ByteArrayOutputStream baos, File outputClassFile, String symbol) @@ -647,6 +663,55 @@ public class COMPJSCRoyale extends MXMLJSCRoyale fileList.append(" <file path=\"" + entryFilePath + "\" mod=\"" + fileDate + "\"/>\n"); } + @Override + protected void setupWatcher() + { + if (!config.getWatch()) + { + return; + } + IWatchWriter writer = new IWatchWriter() + { + private long startTime; + + public void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException + { + startTime = System.nanoTime(); + workspace.startBuilding(); + try + { + target = project.getBackend().createTarget(project, + getTargetSettings(), null); + ((JSTarget) target).build(null, problems); + } + finally + { + workspace.doneBuilding(); + } + } + + public void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException + { + try + { + if (!writeSWC()) + { + throw new IOException("Failed to write SWC file."); + } + + long endTime = System.nanoTime(); + System.out.println((endTime - startTime) / 1e9 + " seconds"); + } + finally + { + workspace.doneBuilding(); + } + } + }; + WatchThread watcherThread = new WatchThread(JSTargetType.JS_ROYALE.getText(), writer, config, project, workspace, problems); + watcherThread.start(); + } + /** * Build target artifact. * diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSC.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSC.java index 02e4726..f56a6f0 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSC.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSC.java @@ -169,7 +169,8 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_PROBLEMS(4); + FAILED_WITH_CONFIG_PROBLEMS(4), + WATCHING(1000); ExitCode(int code) { @@ -217,7 +218,10 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, public static void main(final String[] args) { int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -262,7 +266,6 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, public MXMLJSC() { - DefinitionBase.setPerformanceCachingEnabled(true); workspace = new Workspace(); workspace.setASDocDelegate(new RoyaleASDocDelegate()); project = new RoyaleJSProject(workspace, null); @@ -331,7 +334,7 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, result = mxmlc.mainCompileOnly(removeJSArgs(args), err); else result = mxmlc.mainNoExit(removeJSArgs(args)); - if (result != MXMLC.ExitCode.SUCCESS.getCode()) + if (result != MXMLC.ExitCode.SUCCESS.getCode() && result != MXMLC.ExitCode.WATCHING.getCode()) { problems.addAll(mxmlc.problems.getProblems()); break targetloop; @@ -341,7 +344,7 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, MXMLJSCRoyale royale = new MXMLJSCRoyale(); lastCompiler = royale; result = royale.mainNoExit(removeASArgs(args), problems.getProblems(), false); - if (result != MXMLJSCRoyale.ExitCode.SUCCESS.getCode()) + if (result != MXMLJSCRoyale.ExitCode.SUCCESS.getCode() && result != MXMLJSCRoyale.ExitCode.WATCHING.getCode()) { break targetloop; } @@ -350,7 +353,7 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, MXMLJSCRoyaleCordova royaleCordova = new MXMLJSCRoyaleCordova(); lastCompiler = royaleCordova; result = royaleCordova.mainNoExit(removeASArgs(args), problems.getProblems(), false); - if (result != MXMLJSCRoyaleCordova.ExitCode.SUCCESS.getCode()) + if (result != MXMLJSCRoyaleCordova.ExitCode.SUCCESS.getCode() && result != MXMLJSCRoyaleCordova.ExitCode.WATCHING.getCode()) { break targetloop; } @@ -359,7 +362,7 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, MXMLJSCNode node = new MXMLJSCNode(); lastCompiler = node; result = node.mainNoExit(removeASArgs(args), problems.getProblems(), false); - if (result != MXMLJSCNode.ExitCode.SUCCESS.getCode()) + if (result != MXMLJSCNode.ExitCode.SUCCESS.getCode() && result != MXMLJSCNode.ExitCode.WATCHING.getCode()) { break targetloop; } @@ -368,7 +371,7 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, MXMLJSCNodeModule nodeModule = new MXMLJSCNodeModule(); lastCompiler = nodeModule; result = nodeModule.mainNoExit(removeASArgs(args), problems.getProblems(), false); - if (result != MXMLJSCNodeModule.ExitCode.SUCCESS.getCode()) + if (result != MXMLJSCNodeModule.ExitCode.SUCCESS.getCode() && result != MXMLJSCNodeModule.ExitCode.WATCHING.getCode()) { break targetloop; } @@ -377,7 +380,7 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, MXMLJSCNative jsc = new MXMLJSCNative(); lastCompiler = jsc; result = jsc.mainNoExit(removeASArgs(args), problems.getProblems(), false); - if (result != MXMLJSCNative.ExitCode.SUCCESS.getCode()) + if (result != MXMLJSCNative.ExitCode.SUCCESS.getCode() && result != MXMLJSCNative.ExitCode.WATCHING.getCode()) { break targetloop; } @@ -417,7 +420,10 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, } finally { - waitAndClose(); + if (!config.getWatch() || !ExitCode.SUCCESS.equals(exitCode)) + { + waitAndClose(); + } if (outProblems != null && problems.hasFilteredProblems()) { @@ -427,7 +433,11 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, } } } - return exitCode.code; + if (config.getWatch() && ExitCode.SUCCESS.equals(exitCode)) + { + exitCode = ExitCode.WATCHING; + } + return exitCode.getCode(); } protected String[] removeJSArgs(String[] args) @@ -618,6 +628,13 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, return false; } + if (config.getWatch() && !config.debug()) + { + final ICompilerProblem problem = new ConfigurationProblem(null, -1, + -1, -1, -1, "configuration variable 'debug' must be true if configuration variable 'watch' is true"); + problems.add(problem); + } + for(String target : config.getCompilerTargets()) { JSTargetType jsTargetType = JSTargetType.fromString(target); @@ -637,6 +654,8 @@ public class MXMLJSC implements JSCompilerEntryPoint, ProblemQueryProvider, if (problems.hasErrors()) return false; + + DefinitionBase.setPerformanceCachingEnabled(!config.getWatch()); return true; } diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCNative.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCNative.java index 3b333e4..44e1264 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCNative.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCNative.java @@ -34,6 +34,7 @@ import java.util.Set; import java.util.TreeSet; import org.apache.commons.io.FilenameUtils; +import org.apache.royale.compiler.clients.MXMLJSC.JSTargetType; import org.apache.royale.compiler.clients.problems.ProblemPrinter; import org.apache.royale.compiler.clients.problems.ProblemQuery; import org.apache.royale.compiler.clients.problems.ProblemQueryProvider; @@ -62,6 +63,8 @@ import org.apache.royale.compiler.internal.targets.RoyaleJSTarget; import org.apache.royale.compiler.internal.targets.JSTarget; import org.apache.royale.compiler.internal.units.ResourceModuleCompilationUnit; import org.apache.royale.compiler.internal.units.SourceCompilationUnitFactory; +import org.apache.royale.compiler.internal.watcher.WatchThread; +import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter; import org.apache.royale.compiler.internal.workspaces.Workspace; import org.apache.royale.compiler.problems.ConfigurationProblem; import org.apache.royale.compiler.problems.ICompilerProblem; @@ -95,7 +98,6 @@ public class MXMLJSCNative implements JSCompilerEntryPoint, ProblemQueryProvider return problems; } - /* * Exit code enumerations. */ @@ -105,7 +107,8 @@ public class MXMLJSCNative implements JSCompilerEntryPoint, ProblemQueryProvider PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_PROBLEMS(4); + FAILED_WITH_CONFIG_PROBLEMS(4), + WATCHING(1000); ExitCode(int code) { @@ -141,7 +144,10 @@ public class MXMLJSCNative implements JSCompilerEntryPoint, ProblemQueryProvider public static void main(final String[] args) { int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -182,7 +188,6 @@ public class MXMLJSCNative implements JSCompilerEntryPoint, ProblemQueryProvider { IBackend backend = new JSCBackend(); - DefinitionBase.setPerformanceCachingEnabled(true); workspace = new Workspace(); workspace.setASDocDelegate(new RoyaleASDocDelegate()); project = new RoyaleJSProject(workspace, backend); @@ -272,7 +277,10 @@ public class MXMLJSCNative implements JSCompilerEntryPoint, ProblemQueryProvider } finally { - waitAndClose(); + if (!config.getWatch() || !ExitCode.SUCCESS.equals(exitCode)) + { + waitAndClose(); + } if (outProblems != null && problems.hasFilteredProblems()) { @@ -282,7 +290,62 @@ public class MXMLJSCNative implements JSCompilerEntryPoint, ProblemQueryProvider } } } - return exitCode.code; + if (config.getWatch() && ExitCode.SUCCESS.equals(exitCode)) + { + setupWatcher(); + exitCode = ExitCode.WATCHING; + } + return exitCode.getCode(); + } + + protected void setupWatcher() + { + if (!config.getWatch()) + { + return; + } + IWatchWriter writer = new IWatchWriter() + { + private long startTime; + + public void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException + { + startTime = System.nanoTime(); + workspace.startBuilding(); + try + { + target = project.getBackend().createTarget(project, + getTargetSettings(), null); + ((JSTarget) target).build(mainCU, problems); + } + finally + { + workspace.doneBuilding(); + } + } + + public void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException + { + workspace.startBuilding(); + try + { + File outputFolder = jsPublisher.getOutputFolder(); + for (ICompilationUnit unit : units) + { + writeCompilationUnit(unit, outputFolder); + } + + long endTime = System.nanoTime(); + System.out.println((endTime - startTime) / 1e9 + " seconds"); + } + finally + { + workspace.doneBuilding(); + } + } + }; + WatchThread watcherThread = new WatchThread(JSTargetType.JS_NATIVE.getText(), writer, config, project, workspace, problems); + watcherThread.start(); } /** @@ -682,6 +745,7 @@ public class MXMLJSCNative implements JSCompilerEntryPoint, ProblemQueryProvider return false; validateTargetFile(); + DefinitionBase.setPerformanceCachingEnabled(!config.getWatch()); return true; } catch (ConfigurationException e) diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCNode.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCNode.java index 852f997..fdad636 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCNode.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCNode.java @@ -34,6 +34,7 @@ import java.util.Set; import java.util.TreeSet; import org.apache.commons.io.FilenameUtils; +import org.apache.royale.compiler.clients.MXMLJSC.JSTargetType; import org.apache.royale.compiler.clients.problems.ProblemPrinter; import org.apache.royale.compiler.clients.problems.ProblemQuery; import org.apache.royale.compiler.clients.problems.ProblemQueryProvider; @@ -62,6 +63,8 @@ import org.apache.royale.compiler.internal.targets.RoyaleJSTarget; import org.apache.royale.compiler.internal.targets.JSTarget; import org.apache.royale.compiler.internal.units.ResourceModuleCompilationUnit; import org.apache.royale.compiler.internal.units.SourceCompilationUnitFactory; +import org.apache.royale.compiler.internal.watcher.WatchThread; +import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter; import org.apache.royale.compiler.internal.workspaces.Workspace; import org.apache.royale.compiler.problems.ConfigurationProblem; import org.apache.royale.compiler.problems.ICompilerProblem; @@ -104,7 +107,8 @@ public class MXMLJSCNode implements JSCompilerEntryPoint, ProblemQueryProvider, PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_PROBLEMS(4); + FAILED_WITH_CONFIG_PROBLEMS(4), + WATCHING(1000); ExitCode(int code) { @@ -140,7 +144,10 @@ public class MXMLJSCNode implements JSCompilerEntryPoint, ProblemQueryProvider, public static void main(final String[] args) { int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -184,7 +191,6 @@ public class MXMLJSCNode implements JSCompilerEntryPoint, ProblemQueryProvider, protected MXMLJSCNode(IBackend backend) { - DefinitionBase.setPerformanceCachingEnabled(true); workspace = new Workspace(); workspace.setASDocDelegate(new RoyaleASDocDelegate()); project = new RoyaleJSProject(workspace, backend); @@ -274,7 +280,10 @@ public class MXMLJSCNode implements JSCompilerEntryPoint, ProblemQueryProvider, } finally { - waitAndClose(); + if (!config.getWatch() || !ExitCode.SUCCESS.equals(exitCode)) + { + waitAndClose(); + } if (outProblems != null && problems.hasFilteredProblems()) { @@ -284,7 +293,62 @@ public class MXMLJSCNode implements JSCompilerEntryPoint, ProblemQueryProvider, } } } - return exitCode.code; + if (config.getWatch() && ExitCode.SUCCESS.equals(exitCode)) + { + setupWatcher(); + exitCode = ExitCode.WATCHING; + } + return exitCode.getCode(); + } + + protected void setupWatcher() + { + if (!config.getWatch()) + { + return; + } + IWatchWriter writer = new IWatchWriter() + { + private long startTime; + + public void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException + { + startTime = System.nanoTime(); + workspace.startBuilding(); + try + { + target = project.getBackend().createTarget(project, + getTargetSettings(), null); + ((JSTarget) target).build(mainCU, problems); + } + finally + { + workspace.doneBuilding(); + } + } + + public void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException + { + workspace.startBuilding(); + try + { + File outputFolder = jsPublisher.getOutputFolder(); + for (ICompilationUnit unit : units) + { + writeCompilationUnit(unit, outputFolder); + } + + long endTime = System.nanoTime(); + System.out.println((endTime - startTime) / 1e9 + " seconds"); + } + finally + { + workspace.doneBuilding(); + } + } + }; + WatchThread watcherThread = new WatchThread(JSTargetType.JS_NODE.getText(), writer, config, project, workspace, problems); + watcherThread.start(); } /** @@ -684,6 +748,7 @@ public class MXMLJSCNode implements JSCompilerEntryPoint, ProblemQueryProvider, return false; validateTargetFile(); + DefinitionBase.setPerformanceCachingEnabled(!config.getWatch()); return true; } catch (ConfigurationException e) diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCRoyale.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCRoyale.java index ae080c4..e7d3522 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCRoyale.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCRoyale.java @@ -33,6 +33,7 @@ import java.text.SimpleDateFormat; import java.util.*; import org.apache.commons.io.FilenameUtils; +import org.apache.royale.compiler.clients.MXMLJSC.JSTargetType; import org.apache.royale.compiler.clients.problems.ProblemPrinter; import org.apache.royale.compiler.clients.problems.ProblemQuery; import org.apache.royale.compiler.clients.problems.ProblemQueryProvider; @@ -76,6 +77,8 @@ import org.apache.royale.compiler.internal.tree.properties.ResourceBundleFileNod import org.apache.royale.compiler.internal.units.ResourceBundleCompilationUnit; import org.apache.royale.compiler.internal.units.ResourceModuleCompilationUnit; import org.apache.royale.compiler.internal.units.SourceCompilationUnitFactory; +import org.apache.royale.compiler.internal.watcher.WatchThread; +import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter; import org.apache.royale.compiler.internal.workspaces.Workspace; import org.apache.royale.compiler.problems.ConfigurationProblem; import org.apache.royale.compiler.problems.ICompilerProblem; @@ -115,7 +118,6 @@ public class MXMLJSCRoyale implements JSCompilerEntryPoint, ProblemQueryProvider return problems; } - /* * Exit code enumerations. */ @@ -125,7 +127,8 @@ public class MXMLJSCRoyale implements JSCompilerEntryPoint, ProblemQueryProvider PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_PROBLEMS(4); + FAILED_WITH_CONFIG_PROBLEMS(4), + WATCHING(1000); ExitCode(int code) { @@ -163,7 +166,10 @@ public class MXMLJSCRoyale implements JSCompilerEntryPoint, ProblemQueryProvider public static void main(final String[] args) { int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -207,7 +213,6 @@ public class MXMLJSCRoyale implements JSCompilerEntryPoint, ProblemQueryProvider public MXMLJSCRoyale(IBackend backend) { - DefinitionBase.setPerformanceCachingEnabled(true); workspace = new Workspace(); workspace.setASDocDelegate(new RoyaleASDocDelegate()); project = new RoyaleJSProject(workspace, backend); @@ -297,7 +302,10 @@ public class MXMLJSCRoyale implements JSCompilerEntryPoint, ProblemQueryProvider } finally { - waitAndClose(); + if (!config.getWatch() || !ExitCode.SUCCESS.equals(exitCode)) + { + waitAndClose(); + } if (outProblems != null && problems.hasFilteredProblems()) { @@ -307,7 +315,62 @@ public class MXMLJSCRoyale implements JSCompilerEntryPoint, ProblemQueryProvider } } } - return exitCode.code; + if (config.getWatch() && ExitCode.SUCCESS.equals(exitCode)) + { + setupWatcher(); + return ExitCode.WATCHING.getCode(); + } + return exitCode.getCode(); + } + + protected void setupWatcher() + { + if (!config.getWatch()) + { + return; + } + IWatchWriter writer = new IWatchWriter() + { + private long startTime; + + public void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException + { + startTime = System.nanoTime(); + workspace.startBuilding(); + try + { + target = project.getBackend().createTarget(project, + getTargetSettings(), null); + ((JSTarget) target).build(mainCU, problems); + } + finally + { + workspace.doneBuilding(); + } + } + + public void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException + { + workspace.startBuilding(); + try + { + File outputFolder = jsPublisher.getOutputFolder(); + for (ICompilationUnit unit : units) + { + writeCompilationUnit(unit, outputFolder); + } + + long endTime = System.nanoTime(); + System.out.println((endTime - startTime) / 1e9 + " seconds"); + } + finally + { + workspace.doneBuilding(); + } + } + }; + WatchThread watcherThread = new WatchThread(JSTargetType.JS_ROYALE.getText(), writer, config, project, workspace, problems); + watcherThread.start(); } /** @@ -1121,6 +1184,9 @@ public class MXMLJSCRoyale implements JSCompilerEntryPoint, ProblemQueryProvider return false; validateTargetFile(); + + DefinitionBase.setPerformanceCachingEnabled(!config.getWatch()); + return true; } catch (ConfigurationException e) diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCRoyaleCordova.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCRoyaleCordova.java index 83db08d..69ec7e3 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCRoyaleCordova.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/MXMLJSCRoyaleCordova.java @@ -34,6 +34,7 @@ import java.util.Set; import java.util.TreeSet; import org.apache.commons.io.FilenameUtils; +import org.apache.royale.compiler.clients.MXMLJSC.JSTargetType; import org.apache.royale.compiler.clients.problems.ProblemPrinter; import org.apache.royale.compiler.clients.problems.ProblemQuery; import org.apache.royale.compiler.clients.problems.ProblemQueryProvider; @@ -62,6 +63,8 @@ import org.apache.royale.compiler.internal.targets.RoyaleJSTarget; import org.apache.royale.compiler.internal.targets.JSTarget; import org.apache.royale.compiler.internal.units.ResourceModuleCompilationUnit; import org.apache.royale.compiler.internal.units.SourceCompilationUnitFactory; +import org.apache.royale.compiler.internal.watcher.WatchThread; +import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter; import org.apache.royale.compiler.internal.workspaces.Workspace; import org.apache.royale.compiler.problems.ConfigurationProblem; import org.apache.royale.compiler.problems.ICompilerProblem; @@ -95,7 +98,6 @@ public class MXMLJSCRoyaleCordova implements JSCompilerEntryPoint, ProblemQueryP return problems; } - /* * Exit code enumerations. */ @@ -105,7 +107,8 @@ public class MXMLJSCRoyaleCordova implements JSCompilerEntryPoint, ProblemQueryP PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_PROBLEMS(4); + FAILED_WITH_CONFIG_PROBLEMS(4), + WATCHING(1000); ExitCode(int code) { @@ -143,7 +146,10 @@ public class MXMLJSCRoyaleCordova implements JSCompilerEntryPoint, ProblemQueryP public static void main(final String[] args) { int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -187,7 +193,6 @@ public class MXMLJSCRoyaleCordova implements JSCompilerEntryPoint, ProblemQueryP public MXMLJSCRoyaleCordova(IBackend backend) { - DefinitionBase.setPerformanceCachingEnabled(true); workspace = new Workspace(); workspace.setASDocDelegate(new RoyaleASDocDelegate()); project = new RoyaleJSProject(workspace, backend); @@ -277,7 +282,10 @@ public class MXMLJSCRoyaleCordova implements JSCompilerEntryPoint, ProblemQueryP } finally { - waitAndClose(); + if (!config.getWatch() || !ExitCode.SUCCESS.equals(exitCode)) + { + waitAndClose(); + } if (outProblems != null && problems.hasFilteredProblems()) { @@ -287,7 +295,62 @@ public class MXMLJSCRoyaleCordova implements JSCompilerEntryPoint, ProblemQueryP } } } - return exitCode.code; + if (config.getWatch() && ExitCode.SUCCESS.equals(exitCode)) + { + setupWatcher(); + exitCode = ExitCode.WATCHING; + } + return exitCode.getCode(); + } + + protected void setupWatcher() + { + if (!config.getWatch()) + { + return; + } + IWatchWriter writer = new IWatchWriter() + { + private long startTime; + + public void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException + { + startTime = System.nanoTime(); + workspace.startBuilding(); + try + { + target = project.getBackend().createTarget(project, + getTargetSettings(), null); + ((JSTarget) target).build(mainCU, problems); + } + finally + { + workspace.doneBuilding(); + } + } + + public void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException + { + workspace.startBuilding(); + try + { + File outputFolder = jsPublisher.getOutputFolder(); + for (ICompilationUnit unit : units) + { + writeCompilationUnit(unit, outputFolder); + } + + long endTime = System.nanoTime(); + System.out.println((endTime - startTime) / 1e9 + " seconds"); + } + finally + { + workspace.doneBuilding(); + } + } + }; + WatchThread watcherThread = new WatchThread(JSTargetType.JS_ROYALE_CORDOVA.getText(), writer, config, project, workspace, problems); + watcherThread.start(); } /** @@ -687,6 +750,7 @@ public class MXMLJSCRoyaleCordova implements JSCompilerEntryPoint, ProblemQueryP return false; validateTargetFile(); + DefinitionBase.setPerformanceCachingEnabled(!config.getWatch()); return true; } catch (ConfigurationException e) diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyalePublisher.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyalePublisher.java index ec52ced..80dab0e 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyalePublisher.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyalePublisher.java @@ -131,10 +131,10 @@ public class MXMLRoyalePublisher extends JSGoogPublisher implements IJSGoogPubli // If the output path is specified using the config-xml or the commandline. else if (outputPathParameter != null) { + outputParentFolder = new File(outputPathParameter); // FB usually specified -output <project-path>/bin-release/app.swf - if (outputPathParameter.contains(".swf")) { - if (outputParentFolder == null) - outputParentFolder = new File(outputPathParameter); + if (outputPathParameter.contains(".swf")) + { if (moduleOutput != null && outputPathParameter.contains(moduleOutput)) { String rootFolder = outputPathParameter.substring(0, outputPathParameter.indexOf(moduleOutput)); @@ -146,9 +146,9 @@ public class MXMLRoyalePublisher extends JSGoogPublisher implements IJSGoogPubli outputParentFolder = new File(rootFolder).getParentFile(); } else + { outputParentFolder = outputParentFolder.getParentFile().getParentFile(); - } else { - outputParentFolder = new File(outputPathParameter); + } } } else diff --git a/compiler/src/main/java/org/apache/royale/compiler/clients/COMPC.java b/compiler/src/main/java/org/apache/royale/compiler/clients/COMPC.java index c8c320b..150a572 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/clients/COMPC.java +++ b/compiler/src/main/java/org/apache/royale/compiler/clients/COMPC.java @@ -34,11 +34,14 @@ import org.apache.royale.compiler.config.ICompilerSettingsConstants; import org.apache.royale.compiler.exceptions.ConfigurationException; import org.apache.royale.compiler.internal.projects.RoyaleProjectConfigurator; import org.apache.royale.compiler.internal.targets.SWFTarget; +import org.apache.royale.compiler.internal.watcher.WatchThread; +import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter; import org.apache.royale.compiler.problems.ICompilerProblem; import org.apache.royale.compiler.problems.MissingRequirementConfigurationProblem; import org.apache.royale.compiler.targets.ISWCTarget; import org.apache.royale.compiler.targets.ITargetSettings; import org.apache.royale.compiler.targets.ITarget.TargetType; +import org.apache.royale.compiler.units.ICompilationUnit; import org.apache.royale.swc.ISWC; import org.apache.royale.swc.io.ISWCWriter; import org.apache.royale.swc.io.SWCDirectoryWriter; @@ -127,6 +130,42 @@ public class COMPC extends MXMLC implements FlexTool return message; } + @Override + protected void setupWatcher() + { + if (!config.getWatch()) + { + return; + } + IWatchWriter writer = new IWatchWriter() + { + public void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException + { + startTime = System.nanoTime(); + workspace.startBuilding(); + try + { + if (!setupTargetFile()) + { + throw new IOException("Failed to setup target file."); + } + buildArtifact(); + } + finally + { + workspace.doneBuilding(); + } + } + + public void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException + { + reportTargetCompletion(); + } + }; + WatchThread watcherThread = new WatchThread("SWF", writer, config, project, workspace, problems); + watcherThread.start(); + } + /** * Build SWC library artifact. The compilation units are already built at * this point. diff --git a/compiler/src/main/java/org/apache/royale/compiler/clients/MXMLC.java b/compiler/src/main/java/org/apache/royale/compiler/clients/MXMLC.java index 718136f..eec1238 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/clients/MXMLC.java +++ b/compiler/src/main/java/org/apache/royale/compiler/clients/MXMLC.java @@ -63,6 +63,7 @@ import org.apache.royale.compiler.filespecs.IFileSpecification; import org.apache.royale.compiler.internal.common.Counter; import org.apache.royale.compiler.internal.config.FlashBuilderConfigurator; import org.apache.royale.compiler.internal.config.localization.LocalizationManager; +import org.apache.royale.compiler.internal.definitions.DefinitionBase; import org.apache.royale.compiler.internal.graph.GraphMLWriter; import org.apache.royale.compiler.internal.projects.RoyaleProject; import org.apache.royale.compiler.internal.projects.DefinitionPriority.BasePriority; @@ -73,6 +74,8 @@ import org.apache.royale.compiler.internal.targets.Target; import org.apache.royale.compiler.internal.units.ResourceModuleCompilationUnit; import org.apache.royale.compiler.internal.units.SourceCompilationUnitFactory; import org.apache.royale.compiler.internal.units.StyleModuleCompilationUnit; +import org.apache.royale.compiler.internal.watcher.WatchThread; +import org.apache.royale.compiler.internal.watcher.WatchThread.IWatchWriter; import org.apache.royale.compiler.internal.workspaces.Workspace; import org.apache.royale.compiler.problems.ConfigurationProblem; import org.apache.royale.compiler.problems.FileIOProblem; @@ -123,7 +126,8 @@ public class MXMLC implements FlexTool PRINT_HELP(1), FAILED_WITH_ERRORS(2), FAILED_WITH_EXCEPTIONS(3), - FAILED_WITH_CONFIG_ERRORS(4); + FAILED_WITH_CONFIG_ERRORS(4), + WATCHING(1000); ExitCode(int code) { @@ -147,7 +151,10 @@ public class MXMLC implements FlexTool public static void main(final String[] args) { final int exitCode = staticMainNoExit(args); - System.exit(exitCode); + if (exitCode != ExitCode.WATCHING.getCode()) + { + System.exit(exitCode); + } } /** @@ -258,7 +265,10 @@ public class MXMLC implements FlexTool } finally { - waitAndClose(); + if (!config.getWatch() || !ExitCode.SUCCESS.equals(exitCode)) + { + waitAndClose(); + } if (Counter.COUNT_TOKENS || Counter.COUNT_NODES || Counter.COUNT_DEFINITIONS || Counter.COUNT_SCOPES) @@ -266,7 +276,65 @@ public class MXMLC implements FlexTool Counter.getInstance().dumpCounts(); } } - return exitCode.code; + if (config.getWatch() && ExitCode.SUCCESS.equals(exitCode)) + { + setupWatcher(); + exitCode = ExitCode.WATCHING; + } + return exitCode.getCode(); + } + + protected void setupWatcher() + { + if (!config.getWatch()) + { + return; + } + IWatchWriter writer = new IWatchWriter() + { + public void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException + { + startTime = System.nanoTime(); + workspace.startBuilding(); + try + { + if (!setupTargetFile()) + { + throw new IOException("Failed to setup target file."); + } + buildArtifact(); + } + finally + { + workspace.doneBuilding(); + } + } + + public void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException + { + workspace.startBuilding(); + try + { + final File outputFile = new File(getOutputFilePath()); + final int swfSize = writeSWF(swfTarget, outputFile); + long endTime = System.nanoTime(); + String seconds = String.format("%5.3f", (endTime - startTime) / 1e9); + Map<String, Object> params = new HashMap<String, Object>(); + params.put("byteCount", swfSize); + params.put("path", outputFile.getCanonicalPath()); + params.put("seconds", seconds); + swfOutputMessage = Messages.getString("MXMLC.bytes_written_to_file_in_seconds_format", + params); + reportTargetCompletion(); + } + finally + { + workspace.doneBuilding(); + } + } + }; + WatchThread watcherThread = new WatchThread("SWF", writer, config, project, workspace, problems); + watcherThread.start(); } /** @@ -331,7 +399,7 @@ public class MXMLC implements FlexTool Counter.getInstance().dumpCounts(); } } - return exitCode.code; + return exitCode.getCode(); } /** @@ -559,6 +627,9 @@ public class MXMLC implements FlexTool return false; validateTargetFile(); + + DefinitionBase.setPerformanceCachingEnabled(!config.getWatch()); + return true; } catch (ConfigurationException e) diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/watcher/WatchThread.java b/compiler/src/main/java/org/apache/royale/compiler/internal/watcher/WatchThread.java new file mode 100644 index 0000000..3ba7bed --- /dev/null +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/watcher/WatchThread.java @@ -0,0 +1,273 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.royale.compiler.internal.watcher; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.royale.compiler.clients.problems.ProblemPrinter; +import org.apache.royale.compiler.clients.problems.ProblemQuery; +import org.apache.royale.compiler.clients.problems.WorkspaceProblemFormatter; +import org.apache.royale.compiler.common.DependencyTypeSet; +import org.apache.royale.compiler.config.Configuration; +import org.apache.royale.compiler.filespecs.IFileSpecification; +import org.apache.royale.compiler.internal.projects.RoyaleProject; +import org.apache.royale.compiler.internal.workspaces.Workspace; +import org.apache.royale.compiler.problems.ICompilerProblem; +import org.apache.royale.compiler.problems.InternalCompilerProblem; +import org.apache.royale.compiler.units.ICompilationUnit; +import org.apache.royale.utils.FilenameNormalization; + +public class WatchThread extends Thread +{ + /* + * Exit code enumerations. + */ + static enum ExitCode + { + SUCCESS(0), + PRINT_HELP(1), + FAILED_WITH_ERRORS(2), + FAILED_WITH_EXCEPTIONS(3), + FAILED_WITH_CONFIG_PROBLEMS(4); + + ExitCode(int code) + { + this.code = code; + } + + final int code; + + int getCode() + { + return code; + } + } + + /** + * Specifies how a target rebuilds and writes compilation units that have + * been detected as changed by the watch thread. + */ + public static interface IWatchWriter + { + /** + * Rebuilds the specified compilation units and populates any problems + * that were found while rebuilding. + */ + void rebuild(Collection<ICompilationUnit> units, Collection<ICompilerProblem> problems) throws InterruptedException, IOException; + + /** + * Writes the specified compilation units to the output directory. + */ + void write(Collection<ICompilationUnit> units) throws InterruptedException, IOException; + } + + public WatchThread(String name, IWatchWriter writer, Configuration config, RoyaleProject project, Workspace workspace, ProblemQuery problems) + { + super(); + + this.name = name; + this.writer = writer; + this.config = config; + this.project = project; + this.workspace = workspace; + this.problems = problems; + } + + private String name; + private IWatchWriter writer; + private Configuration config; + private RoyaleProject project; + private Workspace workspace; + private ProblemQuery problems; + + private Map<WatchKey, Path> watchKeys; + private WatchService watchService; + + public void run() + { + try + { + watchKeys = new HashMap<>(); + watchService = FileSystems.getDefault().newWatchService(); + Set<Path> watchedPaths = new HashSet<>(); + for (String sourcePath : config.getCompilerSourcePath()) + { + sourcePath = FilenameNormalization.normalize(sourcePath); + watchPath(Paths.get(sourcePath), watchedPaths); + } + for (String fileSpec : config.getFileSpecs()) + { + fileSpec = FilenameNormalization.normalize(fileSpec); + Path fileSpecPath = Paths.get(fileSpec); + watchPath(fileSpecPath.getParent(), watchedPaths); + } + + System.out.println("Watching for file changes in target " + name + "..."); + while (true) + { + if (isInterrupted()) + { + return; + } + checkForChanges(); + } + } + catch(Exception e) + { + final ICompilerProblem problem = new InternalCompilerProblem(e); + System.err.println(problem); + System.exit(ExitCode.FAILED_WITH_EXCEPTIONS.code); + } + } + + private void checkForChanges() throws Exception + { + // pause the thread while there are no changes pending, + // for better performance + WatchKey watchKey = watchService.take(); + + Set<ICompilationUnit> changedCUs = new HashSet<>(); + while (watchKey != null) + { + processWatchKey(watchKey, changedCUs); + + // keep handling new changes until we run out + watchKey = watchService.poll(); + } + + recompile(changedCUs); + } + + private void processWatchKey(WatchKey watchKey, Set<ICompilationUnit> changedCUs) throws InterruptedException + { + Path path = watchKeys.get(watchKey); + for (WatchEvent<?> event : watchKey.pollEvents()) + { + WatchEvent.Kind<?> kind = event.kind(); + Path childPath = (Path) event.context(); + childPath = path.resolve(childPath); + String fileName = childPath.getFileName().toString(); + if (fileName.endsWith(".mxml") || fileName.endsWith(".as")) + { + String normalizedChildPath = FilenameNormalization.normalize(childPath.toString()); + IFileSpecification fileSpec = workspace.getFileSpecification(normalizedChildPath); + + if (kind.equals(StandardWatchEventKinds.ENTRY_CREATE)) + { + workspace.fileAdded(fileSpec); + } + else if (kind.equals(StandardWatchEventKinds.ENTRY_DELETE)) + { + workspace.fileRemoved(fileSpec); + } + else if (kind.equals(StandardWatchEventKinds.ENTRY_MODIFY)) + { + workspace.fileChanged(fileSpec); + } + for (ICompilationUnit cu : workspace.getCompilationUnits(normalizedChildPath, project)) + { + changedCUs.add(cu); + } + } + } + if (!watchKey.reset()) + { + watchKeys.remove(watchKey); + } + } + + private void recompile(Set<ICompilationUnit> changedCUs) throws InterruptedException, IOException + { + if (changedCUs.size() == 0) + { + return; + } + + System.out.println("File change detected. Recompiling " + name + "..."); + + problems.clear(); + + writer.rebuild(changedCUs, problems.getProblems()); + + for (ICompilationUnit cu : changedCUs) + { + DependencyTypeSet dependencyTypes = DependencyTypeSet.allOf(); + Set<ICompilationUnit> reverseDeps = project.getDirectReverseDependencies(cu, dependencyTypes); + changedCUs.addAll(reverseDeps); + } + + List<ICompilerProblem> errs = new ArrayList<ICompilerProblem>(); + List<ICompilerProblem> warns = new ArrayList<ICompilerProblem>(); + problems.getErrorsAndWarnings(errs, warns); + + if (!problems.hasFilteredProblems() || !problems.hasErrors()) + { + writer.write(changedCUs); + } + + if (problems.hasFilteredProblems()) + { + Iterable<ICompilerProblem> filteredProblems = problems.getFilteredProblems(); + final WorkspaceProblemFormatter formatter = new WorkspaceProblemFormatter( + workspace); + final ProblemPrinter printer = new ProblemPrinter(formatter); + printer.printProblems(filteredProblems); + } + + System.out.println("Watching for file changes in target " + name + "..."); + } + + private void watchPath(Path path, Set<Path> watchedPaths) throws IOException + { + path = path.toAbsolutePath(); + if (watchedPaths.contains(path)) + { + return; + } + watchedPaths.add(path); + java.nio.file.Files.walkFileTree(path, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult preVisitDirectory(Path subPath, BasicFileAttributes attrs) + throws IOException { + WatchKey watchKey = subPath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); + watchKeys.put(watchKey, subPath); + return FileVisitResult.CONTINUE; + } + }); + } +} \ No newline at end of file