private Collection<File> getClasspathFiles(
      ModuleChunk chunk,
      JpsJavaClasspathKind kind,
      final boolean excludeMainModuleOutput,
      ClasspathPart classpathPart,
      final boolean exportedOnly) {
    final Set<File> files = new LinkedHashSet<File>();
    for (JpsModule module : chunk.getModules()) {
      JpsJavaDependenciesEnumerator enumerator =
          JpsJavaExtensionService.dependencies(module).includedIn(kind).recursively();
      if (exportedOnly) {
        enumerator = enumerator.exportedOnly();
      }
      if (classpathPart == ClasspathPart.BEFORE_JDK) {
        enumerator = enumerator.satisfying(new BeforeJavaSdkItemFilter(module));
      } else if (classpathPart == ClasspathPart.AFTER_JDK) {
        enumerator = enumerator.satisfying(new AfterJavaSdkItemFilter(module));
      }
      JpsJavaDependenciesRootsEnumerator rootsEnumerator = enumerator.classes();
      if (excludeMainModuleOutput) {
        rootsEnumerator = rootsEnumerator.withoutSelfModuleOutput();
      }
      files.addAll(rootsEnumerator.getRoots());
    }

    if (classpathPart == ClasspathPart.BEFORE_JDK) {
      for (JpsModule module : chunk.getModules()) {
        JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE);
        if (sdk != null) {
          files.addAll(sdk.getParent().getFiles(JpsOrderRootType.COMPILED));
        }
      }
    }
    return files;
  }
 private static Map<String, String> buildClassToSourceMap(
     ModuleChunk chunk,
     CompileContext context,
     Set<String> toCompilePaths,
     String moduleOutputPath)
     throws IOException {
   final Map<String, String> class2Src = new HashMap<String, String>();
   for (Module module : chunk.getModules()) {
     final String moduleName = module.getName().toLowerCase(Locale.US);
     final SourceToOutputMapping srcToOut =
         context.getDataManager().getSourceToOutputMap(moduleName, context.isCompilingTests());
     for (String src : srcToOut.getKeys()) {
       if (!toCompilePaths.contains(src) && isGroovyFile(src)) {
         final Collection<String> outs = srcToOut.getState(src);
         if (outs != null) {
           for (String out : outs) {
             if (out.endsWith(".class") && out.startsWith(moduleOutputPath)) {
               final String className =
                   out.substring(moduleOutputPath.length(), out.length() - ".class".length())
                       .replace('/', '.');
               class2Src.put(className, src);
             }
           }
         }
       }
     }
   }
   return class2Src;
 }
 private static String getJavaExecutable(ModuleChunk chunk) {
   Sdk sdk = chunk.getModules().iterator().next().getSdk();
   if (sdk instanceof JavaSdk) {
     return ((JavaSdk) sdk).getJavaExecutable();
   }
   return SystemProperties.getJavaHome() + "/bin/java";
 }
 /**
  * @param chunk
  * @return mapping "sourceRoot" -> "package prefix" Package prefix uses slashes instead of dots
  *     and ends with trailing slash
  */
 public static Map<File, String> getSourceRootsWithDependents(ModuleChunk chunk) {
   final boolean includeTests = chunk.containsTests();
   final Map<File, String> result = new LinkedHashMap<File, String>();
   processModulesRecursively(
       chunk,
       JpsJavaClasspathKind.compile(includeTests),
       new Consumer<JpsModule>() {
         @Override
         public void consume(JpsModule module) {
           for (JpsModuleSourceRoot root : module.getSourceRoots()) {
             if (root.getRootType().equals(JavaSourceRootType.SOURCE)
                 || includeTests && root.getRootType().equals(JavaSourceRootType.TEST_SOURCE)) {
               JavaSourceRootProperties properties =
                   (JavaSourceRootProperties)
                       ((JpsSimpleElement<?>) root.getProperties()).getData();
               String prefix = properties.getPackagePrefix();
               if (!prefix.isEmpty()) {
                 prefix = prefix.replace('.', '/');
                 if (!prefix.endsWith("/")) {
                   prefix += "/";
                 }
               } else {
                 prefix = null;
               }
               result.put(JpsPathUtil.urlToFile(root.getUrl()), prefix);
             }
           }
         }
       });
   return result;
 }
 private static void processModulesRecursively(
     ModuleChunk chunk, JpsJavaClasspathKind kind, Consumer<JpsModule> processor) {
   JpsJavaExtensionService.getInstance()
       .enumerateDependencies(chunk.getModules())
       .includedIn(kind)
       .recursively()
       .processModules(processor);
 }
 public Collection<File> getCompilationClasspath(
     ModuleChunk chunk, boolean excludeMainModuleOutput) {
   return getClasspathFiles(
       chunk,
       JpsJavaClasspathKind.compile(chunk.containsTests()),
       excludeMainModuleOutput,
       ClasspathPart.AFTER_JDK,
       true);
 }
 private static String getModuleOutput(CompileContext context, ModuleChunk chunk) {
   final Module representativeModule = chunk.getModules().iterator().next();
   File moduleOutputDir =
       context
           .getProjectPaths()
           .getModuleOutputDir(representativeModule, context.isCompilingTests());
   assert moduleOutputDir != null;
   String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath());
   return moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/";
 }
 public static Collection<File> getOutputPathsWithDependents(final ModuleChunk chunk) {
   final boolean forTests = chunk.containsTests();
   final Set<File> sourcePaths = new LinkedHashSet<File>();
   processModulesRecursively(
       chunk,
       JpsJavaClasspathKind.compile(forTests),
       new Consumer<JpsModule>() {
         @Override
         public void consume(JpsModule module) {
           addFile(
               sourcePaths, JpsJavaExtensionService.getInstance().getOutputUrl(module, forTests));
         }
       });
   return sourcePaths;
 }
  public ModuleLevelBuilder.ExitCode build(final CompileContext context, ModuleChunk chunk)
      throws ProjectBuildException {
    try {
      final List<File> toCompile = collectChangedFiles(context, chunk);
      if (toCompile.isEmpty()) {
        return ExitCode.NOTHING_DONE;
      }

      String moduleOutput = getModuleOutput(context, chunk);
      String compilerOutput = getCompilerOutput(moduleOutput);

      final Set<String> toCompilePaths = new LinkedHashSet<String>();
      for (File file : toCompile) {
        toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath()));
      }

      Map<String, String> class2Src =
          buildClassToSourceMap(chunk, context, toCompilePaths, moduleOutput);

      String ideCharset = chunk.getProject().getProjectCharset();
      String encoding =
          !Comparing.equal(CharsetToolkit.getDefaultSystemCharset().name(), ideCharset)
              ? ideCharset
              : null;
      List<String> patchers = Collections.emptyList(); // todo patchers
      final File tempFile =
          GroovycOSProcessHandler.fillFileWithGroovycParameters(
              compilerOutput,
              toCompilePaths,
              FileUtil.toSystemDependentName(moduleOutput),
              class2Src,
              encoding,
              patchers);

      // todo different outputs in a chunk
      // todo xmx
      final List<String> cmd =
          ExternalProcessUtil.buildJavaCommandLine(
              getJavaExecutable(chunk),
              "org.jetbrains.groovy.compiler.rt.GroovycRunner",
              Collections.<String>emptyList(),
              new ArrayList<String>(generateClasspath(context, chunk)),
              Arrays.asList(
                  "-Xmx384m",
                  "-Dfile.encoding=" + CharsetToolkit.getDefaultSystemCharset().name() /*,
                      "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239"*/),
              Arrays.<String>asList(myForStubs ? "stubs" : "groovyc", tempFile.getPath()));

      final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd));
      GroovycOSProcessHandler handler =
          GroovycOSProcessHandler.runGroovyc(
              process,
              new Consumer<String>() {
                public void consume(String s) {
                  context.processMessage(new ProgressMessage(s));
                }
              });

      if (handler.shouldRetry()) {
        if (CHUNK_REBUILD_ORDERED.get(context) != null) {
          CHUNK_REBUILD_ORDERED.set(context, null);
        } else {
          CHUNK_REBUILD_ORDERED.set(context, Boolean.TRUE);
          return ExitCode.CHUNK_REBUILD_REQUIRED;
        }
      }

      if (myForStubs) {
        JavaBuilder.addTempSourcePathRoot(context, new File(compilerOutput));
      }

      for (CompilerMessage message : handler.getCompilerMessages()) {
        context.processMessage(message);
      }
      if (!myForStubs
          && updateDependencies(
              context, chunk, toCompile, moduleOutput, handler.getSuccessfullyCompiled())) {
        return ExitCode.ADDITIONAL_PASS_REQUIRED;
      }
      return ExitCode.OK;
    } catch (Exception e) {
      throw new ProjectBuildException(e);
    }
  }
    public BuildStatus build(final Collection<Module> modules, final Flags flags) {
      boolean incremental = flags.incremental();
      final List<ModuleChunk> chunks = myProjectBuilder.getChunks(flags.tests()).getChunkList();

      for (final ModuleChunk c : chunks) {
        final Set<Module> chunkModules = c.getElements();

        if (!DefaultGroovyMethods.intersect(modules, chunkModules).isEmpty()) {
          final Set<String> removedSources = new HashSet<String>();

          if (incremental) {
            final Set<String> chunkSources = new HashSet<String>();
            final Set<String> outdatedSources = new HashSet<String>();

            for (Module m : chunkModules) {
              final ModuleWrapper mw = getModule(m.getName());

              outdatedSources.addAll(mw.getOutdatedFiles(flags.tests()));
              chunkSources.addAll(mw.getSources(flags.tests()));
              removedSources.addAll(mw.getRemovedFiles(flags.tests()));
            }

            final BuildStatus result =
                iterativeCompile(c, chunkSources, outdatedSources, removedSources, flags);

            incremental = result == BuildStatus.INCREMENTAL;

            if (result == BuildStatus.FAILURE) {
              return result;
            }
          } else {
            new Logger(flags) {
              @Override
              public void log(PrintStream stream) {
                stream.println("Compiling chunk " + c.getName() + " non-incrementally.");
              }
            }.log();

            for (Module m : chunkModules) {
              final ModuleWrapper mw = getModule(m.getName());
              removedSources.addAll(flags.tests() ? mw.getRemovedTests() : mw.getRemovedSources());
            }

            final Set<Module> toClean = new HashSet<Module>();

            for (Module m : chunkModules) {
              if (!cleared.contains(m)) {
                toClean.add(m);
              }
            }

            if (!toClean.isEmpty() && !flags.tests()) {
              builder.clearChunk(new ModuleChunk(toClean), null, ProjectWrapper.this);
              cleared.addAll(toClean);
            }

            final Mappings delta = dependencyMapping.createDelta();
            final Callbacks.Backend deltaCallback = delta.getCallback();

            try {
              builder.buildChunk(c, flags.tests(), null, deltaCallback, ProjectWrapper.this);
            } catch (Exception e) {
              e.printStackTrace();
              return BuildStatus.FAILURE;
            }

            final Set<String> allFiles = new HashSet<String>();

            for (Module m : c.getElements()) {
              final ModuleWrapper module = getModule(m.getName());
              affectedFiles.removeAll(module.getSources(flags.tests()));
              allFiles.addAll(module.getSources(flags.tests()));
            }

            final Collection<File> files = new HashSet<File>();

            for (String f : allFiles) {
              files.add(new File(f));
            }

            dependencyMapping.integrate(delta, files, removedSources);

            for (Module m : chunkModules) {
              Reporter.reportBuildSuccess(m, flags.tests());
            }
          }
        }
      }

      return BuildStatus.INCREMENTAL;
    }
    BuildStatus iterativeCompile(
        final ModuleChunk chunk,
        final Set<String> sources,
        final Set<String> outdated,
        final Set<String> removed,
        final Flags flags) {
      final Collection<String> filesToCompile =
          DefaultGroovyMethods.intersect(affectedFiles, sources);

      if (outdated != null) {
        for (String s : outdated) {
          assert (s != null);
        }

        filesToCompile.addAll(outdated);
      }

      filesToCompile.removeAll(compiledFiles);

      if (!filesToCompile.isEmpty() || removed != null) {
        final Set<String> outputFiles = new HashSet<String>();

        for (String f : filesToCompile) {
          final Set<ClassRepr> classes = dependencyMapping.getClasses(f);

          if (classes != null) {
            for (ClassRepr cr : classes) {
              outputFiles.add(cr.getFileName());
            }
          }
        }

        if (removed != null) {
          for (String f : removed) {
            final Set<ClassRepr> classes = dependencyMapping.getClasses(f);
            if (classes != null) {
              for (ClassRepr cr : classes) {
                outputFiles.add(cr.getFileName());
              }
            }
          }
        }

        if (!outputFiles.isEmpty()) {
          new Logger(flags) {
            @Override
            public void log(PrintStream stream) {
              stream.println("Cleaning output files:");
              logFilePaths(stream, outputFiles);
              stream.println("End of files");
            }
          }.log();

          builder.clearChunk(chunk, outputFiles, ProjectWrapper.this);
        }

        final Mappings delta = dependencyMapping.createDelta();
        final Callbacks.Backend deltaBackend = delta.getCallback();

        new Logger(flags) {
          @Override
          public void log(PrintStream stream) {
            stream.println("Compiling files:");
            logFilePaths(stream, filesToCompile);
            stream.println("End of files");
          }
        }.log();

        boolean buildException = false;

        try {
          builder.buildChunk(
              chunk, flags.tests(), filesToCompile, deltaBackend, ProjectWrapper.this);
        } catch (Exception e) {
          e.printStackTrace();
          buildException = true;
        }

        if (!buildException) {
          compiledFiles.addAll(filesToCompile);
          affectedFiles.removeAll(filesToCompile);

          final Collection<File> files = new HashSet<File>();
          final Collection<File> compiled = new HashSet<File>();

          for (String f : filesToCompile) {
            files.add(new File(f));
          }

          for (String f : compiledFiles) {
            compiled.add(new File(f));
          }

          final Collection<File> affected = new HashSet<File>();

          final boolean incremental =
              dependencyMapping.differentiate(delta, removed, files, compiled, affected);

          for (File a : affected) {
            affectedFiles.add(FileUtil.toSystemIndependentName(a.getAbsolutePath()));
          }

          dependencyMapping.integrate(delta, files, removed);

          if (!incremental) {
            affectedFiles.addAll(sources);
            affectedFiles.removeAll(compiledFiles);

            final BuildStatus result = iterativeCompile(chunk, sources, null, null, flags);

            if (result == BuildStatus.FAILURE) {
              return result;
            }

            return BuildStatus.CONSERVATIVE;
          }

          return iterativeCompile(chunk, sources, null, null, flags);
        } else {
          return BuildStatus.FAILURE;
        }
      } else {
        for (Module m : chunk.getElements()) {
          Reporter.reportBuildSuccess(m, flags.tests());
        }
      }

      return BuildStatus.INCREMENTAL;
    }