@Nullable
  private static AnalyzeExhaust analyze(
      final K2JVMCompileEnvironmentConfiguration configuration, boolean stubs) {
    final JetCoreEnvironment environment = configuration.getEnvironment();
    AnalyzerWithCompilerReport analyzerWithCompilerReport =
        new AnalyzerWithCompilerReport(configuration.getMessageCollector());
    final Predicate<PsiFile> filesToAnalyzeCompletely =
        stubs ? Predicates.<PsiFile>alwaysFalse() : Predicates.<PsiFile>alwaysTrue();
    analyzerWithCompilerReport.analyzeAndReport(
        new Function0<AnalyzeExhaust>() {
          @NotNull
          @Override
          public AnalyzeExhaust invoke() {
            return AnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
                environment.getProject(),
                environment.getSourceFiles(),
                filesToAnalyzeCompletely,
                JetControlFlowDataTraceFactory.EMPTY,
                configuration.getEnvironment().getCompilerDependencies());
          }
        },
        environment.getSourceFiles());

    return analyzerWithCompilerReport.hasErrors()
        ? null
        : analyzerWithCompilerReport.getAnalyzeExhaust();
  }
  @Nullable
  public static ClassFileFactory compileModule(
      K2JVMCompileEnvironmentConfiguration configuration, Module moduleBuilder, File directory) {
    if (moduleBuilder.getSourceFiles().isEmpty()) {
      throw new CompileEnvironmentException("No source files where defined");
    }

    CompileEnvironmentUtil.addSourcesFromModuleToEnvironment(
        configuration.getEnvironment(), moduleBuilder, directory);
    for (String classpathRoot : moduleBuilder.getClasspathRoots()) {
      configuration.getEnvironment().addToClasspath(new File(classpathRoot));
    }

    GenerationState generationState = analyzeAndGenerate(configuration);
    if (generationState == null) {
      return null;
    }
    return generationState.getFactory();
  }
  public static boolean compileBunchOfSources(
      K2JVMCompileEnvironmentConfiguration configuration,
      String sourceFileOrDir,
      String jar,
      String outputDir,
      boolean includeRuntime) {
    configuration.getEnvironment().addSources(sourceFileOrDir);

    return compileBunchOfSources(configuration, jar, outputDir, includeRuntime);
  }
 @Nullable
 public static GenerationState analyzeAndGenerate(
     K2JVMCompileEnvironmentConfiguration configuration) {
   return analyzeAndGenerate(
       configuration,
       configuration
           .getEnvironment()
           .getCompilerDependencies()
           .getCompilerSpecialMode()
           .isStubs());
 }
  public static boolean compileBunchOfSourceDirectories(
      K2JVMCompileEnvironmentConfiguration configuration,
      List<String> sources,
      String jar,
      String outputDir,
      boolean includeRuntime) {
    for (String source : sources) {
      configuration.getEnvironment().addSources(source);
    }

    return compileBunchOfSources(configuration, jar, outputDir, includeRuntime);
  }
  @NotNull
  private static GenerationState generate(
      final K2JVMCompileEnvironmentConfiguration configuration,
      AnalyzeExhaust exhaust,
      boolean stubs) {
    JetCoreEnvironment environment = configuration.getEnvironment();
    Project project = environment.getProject();
    Progress backendProgress =
        new Progress() {
          @Override
          public void log(String message) {
            configuration
                .getMessageCollector()
                .report(
                    CompilerMessageSeverity.LOGGING, message, CompilerMessageLocation.NO_LOCATION);
          }
        };
    GenerationState generationState =
        new GenerationState(
            project,
            ClassBuilderFactories.binaries(stubs),
            backendProgress,
            exhaust,
            environment.getSourceFiles(),
            configuration.getEnvironment().getCompilerDependencies().getCompilerSpecialMode());
    generationState.compileCorrectFiles(CompilationErrorHandler.THROW_EXCEPTION);

    List<CompilerPlugin> plugins = configuration.getCompilerPlugins();
    if (plugins != null) {
      CompilerPluginContext context =
          new CompilerPluginContext(
              project, exhaust.getBindingContext(), environment.getSourceFiles());
      for (CompilerPlugin plugin : plugins) {
        plugin.processFiles(context);
      }
    }
    return generationState;
  }
  public static boolean compileModules(
      K2JVMCompileEnvironmentConfiguration configuration,
      @NotNull List<Module> modules,
      @NotNull File directory,
      @Nullable String jarPath,
      @Nullable String outputDir,
      boolean jarRuntime) {

    for (Module moduleBuilder : modules) {
      // TODO: this should be done only once for the environment
      if (configuration.getEnvironment().getCompilerDependencies().getRuntimeJar() != null) {
        CompileEnvironmentUtil.addToClasspath(
            configuration.getEnvironment(),
            configuration.getEnvironment().getCompilerDependencies().getRuntimeJar());
      }
      ClassFileFactory moduleFactory = compileModule(configuration, moduleBuilder, directory);
      if (moduleFactory == null) {
        return false;
      }
      if (outputDir != null) {
        CompileEnvironmentUtil.writeToOutputDirectory(moduleFactory, outputDir);
      } else {
        String path =
            jarPath != null
                ? jarPath
                : new File(directory, moduleBuilder.getModuleName() + ".jar").getPath();
        try {
          CompileEnvironmentUtil.writeToJar(
              moduleFactory, new FileOutputStream(path), null, jarRuntime);
        } catch (FileNotFoundException e) {
          throw new CompileEnvironmentException("Invalid jar path " + path, e);
        }
      }
    }
    return true;
  }
  @Nullable
  public static ClassLoader compileText(
      K2JVMCompileEnvironmentConfiguration configuration, String code) {
    configuration
        .getEnvironment()
        .addSources(
            new LightVirtualFile(
                "script" + LocalTimeCounter.currentTime() + ".kt", JetLanguage.INSTANCE, code));

    GenerationState generationState = analyzeAndGenerate(configuration);
    if (generationState == null) {
      return null;
    }
    return new GeneratedClassLoader(generationState.getFactory());
  }
  private static boolean compileBunchOfSources(
      K2JVMCompileEnvironmentConfiguration configuration,
      String jar,
      String outputDir,
      boolean includeRuntime) {
    FqName mainClass = null;
    for (JetFile file : configuration.getEnvironment().getSourceFiles()) {
      if (JetMainDetector.hasMain(file.getDeclarations())) {
        FqName fqName = JetPsiUtil.getFQName(file);
        mainClass = fqName.child(JvmAbi.PACKAGE_CLASS);
        break;
      }
    }

    GenerationState generationState = analyzeAndGenerate(configuration);
    if (generationState == null) {
      return false;
    }

    try {
      ClassFileFactory factory = generationState.getFactory();
      if (jar != null) {
        try {
          CompileEnvironmentUtil.writeToJar(
              factory, new FileOutputStream(jar), mainClass, includeRuntime);
        } catch (FileNotFoundException e) {
          throw new CompileEnvironmentException("Invalid jar path " + jar, e);
        }
      } else if (outputDir != null) {
        CompileEnvironmentUtil.writeToOutputDirectory(factory, outputDir);
      } else {
        throw new CompileEnvironmentException(
            "Output directory or jar file is not specified - no files will be saved to the disk");
      }
      return true;
    } finally {
      generationState.destroy();
    }
  }