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;
    }
  public void makeModules(final Collection<Module> initial, final Flags flags) {
    if (myHistory == null && !flags.tests()) {
      clean();
    }

    new Logger(flags) {
      @Override
      public void log(final PrintStream stream) {
        stream.println("Request to make modules:");
        logMany(stream, initial);
        stream.println("End of request");
      }
    }.log();

    final ClasspathKind kind = ClasspathKind.compile(flags.tests());

    final Set<Module> modules = new HashSet<Module>();
    final Set<String> marked = new HashSet<String>();
    final Map<String, Boolean> visited = new HashMap<String, Boolean>();
    final Set<String> frontier = new HashSet<String>();

    final Map<String, Set<String>> reversedDependencies = new HashMap<String, Set<String>>();
    final DotPrinter printer = new DotPrinter(flags.logStream());

    printer.header();

    for (Module m : myProject.getModules().values()) {
      final String mName = m.getName();

      printer.node(mName);

      for (ClasspathItem cpi : m.getClasspath(kind)) {
        if (cpi instanceof Module) {
          final String name = ((Module) cpi).getName();

          printer.edge(name, mName);

          Set<String> sm = reversedDependencies.get(name);

          if (sm == null) {
            sm = new HashSet<String>();
            reversedDependencies.put(name, sm);
          }

          sm.add(mName);
        }
      }
    }

    printer.footer();

    // Building "upper" subgraph

    printer.header();

    new Object() {
      public void run(final Collection<Module> initial) {
        if (initial == null) return;

        for (Module module : initial) {

          final String mName = module.getName();

          if (marked.contains(mName)) continue;

          printer.node(mName);

          final List<Module> dep = new ArrayList<Module>();

          for (ClasspathItem cpi : module.getClasspath(kind)) {
            if (cpi instanceof Module && !marked.contains(((Module) cpi).getName())) {
              printer.edge(((Module) cpi).getName(), mName);
              dep.add((Module) cpi);
            }
          }

          if (dep.size() == 0) {
            frontier.add(mName);
          }

          marked.add(mName);

          run(dep);
        }
      }
    }.run(initial);

    printer.footer();

    // Traversing "upper" subgraph and collecting outdated modules and their descendants
    new Object() {
      public void run(final Collection<String> initial, final boolean force) {
        if (initial == null) return;

        for (String moduleName : initial) {
          if (!marked.contains(moduleName)) continue;

          final Boolean property = visited.get(moduleName);

          if (property == null || !property && force) {
            final boolean outdated = getModule(moduleName).isOutdated(flags.tests(), myHistory);

            if (force || outdated) {
              visited.put(moduleName, true);
              modules.add(myProject.getModules().get(moduleName));

              run(reversedDependencies.get(moduleName), true);
            } else {
              if (property == null) {
                visited.put(moduleName, false);
              }
              run(reversedDependencies.get(moduleName), false);
            }
          }
        }
      }
    }.run(frontier, flags.force());

    new Logger(flags) {
      @Override
      public void log(PrintStream stream) {
        stream.println("Propagated modules:");
        logMany(stream, modules);
        stream.println("End of propagated");
      }
    }.log();

    if (modules.size() == 0 && !flags.force()) {
      System.out.println("All requested modules are up-to-date.");
      return;
    }

    final BusyBeaver beaver = new BusyBeaver(myProjectBuilder);

    myProjectBuilder.buildStart();

    if (flags.tests()) {
      beaver.build(
          modules,
          new Flags() {
            public boolean tests() {
              return false;
            }

            public boolean incremental() {
              return flags.incremental();
            }

            public boolean force() {
              return flags.force();
            }

            public PrintStream logStream() {
              return flags.logStream();
            }
          });
    }

    beaver.build(modules, flags);

    myProjectBuilder.buildStop();

    for (Module mod : modules) {
      getModule(mod.getName()).updateOutputStatus();
    }
  }
    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;
    }