Predicate<FileRef> allowedOutputPredicate) {
    Set<CompilationUnit> extractMessagesFrom = Sets.newHashSet();
    List<CompilationTask> sourceNotChanged = Lists.newArrayList();
    for (CompilationUnit cUnit : getCompilationUnits()) {
      FileRef sourceFileRef = cUnit.getSourceFileRef();
      SourcePosition sourcePosition = new SourcePosition(sourceFileRef);
      for (OutputLanguage language : outputLanguages) {
        String suffix = language.getSuffix(compilationVersion);
        FileRef outputFileRef = sourceFileRef.removeExtension().addSuffix(suffix);
        if (allowedOutputPredicate.apply(outputFileRef)) {
          extractMessagesFrom.add(cUnit);
          CompilationTask task =
              new CompilationTask(cUnit, codeGeneratorFactory, language,
                                  outputFileRef);
          // if the output file does not exist (last modified = 0) or if the source has been
          // modified since the last time that the output has been generated, or if the source has
          // changed, then we need to recompile the target
          if (outputFileRef.getLastModified() < sourceFileRef.getLastModified()
              || manager.sourceChanged(task)) {
            task.execute(alertSink, alertPolicy);
          } else {
            alertSink.add(new ProgressAlert(sourcePosition, "Skipped (source unchanged)"));
            sourceNotChanged.add(task);
          }
        } else {
          alertSink.add(new ProgressAlert(sourcePosition, "Skipped (output supressed)"));
        }
      }
    }
    // For each task we didn't execute, check to see if any of the interfaces
    // it depends on have changed (which could happen as a result of
    // recompiling one of the things it depends on).
    // TODO(laurence): see whether it's possible to combine these two loops by
    // having usedInterfacesChanged not change its value.
    for (CompilationTask task : sourceNotChanged) {
      if (manager.usedInterfacesChanged(task)) {
        FileRef sourceFileRef = task.getCompilationUnit().getSourceFileRef();
        SourcePosition sourcePosition = new SourcePosition(sourceFileRef);
        alertSink.add(new ProgressAlert(sourcePosition, "Reconsidered; callees have changed"));
        task.execute(alertSink, alertPolicy);
      }
    }
    // Optionally write out a java properties file that contains
    // strings extracted from <gxp:msg>s
    if (propertiesFile != null && !extractMessagesFrom.isEmpty()) {
      SourcePosition outputPosition = new SourcePosition(propertiesFile);
      alertSink.add(new ProgressAlert(outputPosition, "Generating"));
      List<ExtractedMessage> messages = Lists.newArrayList();
      for (CompilationUnit cUnit : extractMessagesFrom) {
        messages.addAll(cUnit.getMessageExtractedTree().getMessages());