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

Reply via email to