@Nullable
  private static AnalysisResult analyze(
      @NotNull final KotlinCoreEnvironment environment, @Nullable String targetDescription) {
    MessageCollector collector =
        environment.getConfiguration().get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY);
    assert collector != null;

    long analysisStart = PerformanceCounter.Companion.currentTime();
    AnalyzerWithCompilerReport analyzerWithCompilerReport =
        new AnalyzerWithCompilerReport(collector);
    analyzerWithCompilerReport.analyzeAndReport(
        environment.getSourceFiles(),
        new Function0<AnalysisResult>() {
          @NotNull
          @Override
          public AnalysisResult invoke() {
            BindingTrace sharedTrace =
                new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace();
            ModuleContext moduleContext =
                TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(
                    environment.getProject(), ModuleNameKt.getModuleName(environment));

            return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegrationWithCustomContext(
                moduleContext,
                environment.getSourceFiles(),
                sharedTrace,
                environment.getConfiguration().get(JVMConfigurationKeys.MODULES),
                environment
                    .getConfiguration()
                    .get(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS),
                new JvmPackagePartProvider(environment));
          }
        });
    long analysisNanos = PerformanceCounter.Companion.currentTime() - analysisStart;
    String message =
        "ANALYZE: "
            + environment.getSourceFiles().size()
            + " files ("
            + environment.getSourceLinesOfCode()
            + " lines) "
            + (targetDescription != null ? targetDescription : "")
            + "in "
            + TimeUnit.NANOSECONDS.toMillis(analysisNanos)
            + " ms";
    K2JVMCompiler.Companion.reportPerf(environment.getConfiguration(), message);

    AnalysisResult result = analyzerWithCompilerReport.getAnalysisResult();
    assert result != null
        : "AnalysisResult should be non-null, compiling: " + environment.getSourceFiles();

    CompilerPluginContext context =
        new CompilerPluginContext(
            environment.getProject(), result.getBindingContext(), environment.getSourceFiles());
    for (CompilerPlugin plugin :
        environment.getConfiguration().getList(CLIConfigurationKeys.COMPILER_PLUGINS)) {
      plugin.processFiles(context);
    }

    return analyzerWithCompilerReport.hasErrors() ? null : result;
  }
  @Nullable
  public static Class<?> compileScript(
      @NotNull CompilerConfiguration configuration,
      @NotNull KotlinPaths paths,
      @NotNull KotlinCoreEnvironment environment) {
    List<AnalyzerScriptParameter> scriptParameters =
        environment.getConfiguration().getList(JVMConfigurationKeys.SCRIPT_PARAMETERS);
    if (!scriptParameters.isEmpty()) {
      KotlinScriptDefinitionProvider.getInstance(environment.getProject())
          .addScriptDefinition(new KotlinScriptDefinition(".kts", scriptParameters));
    }
    GenerationState state = analyzeAndGenerate(environment);
    if (state == null) {
      return null;
    }

    GeneratedClassLoader classLoader;
    try {
      List<URL> classPaths = Lists.newArrayList(paths.getRuntimePath().toURI().toURL());
      for (File file : JvmContentRootsKt.getJvmClasspathRoots(configuration)) {
        classPaths.add(file.toURI().toURL());
      }
      //noinspection UnnecessaryFullyQualifiedName
      classLoader =
          new GeneratedClassLoader(
              state.getFactory(),
              new URLClassLoader(classPaths.toArray(new URL[classPaths.size()]), null));

      FqName nameForScript =
          ScriptNameUtil.classNameForScript(environment.getSourceFiles().get(0).getScript());
      return classLoader.loadClass(nameForScript.asString());
    } catch (Exception e) {
      throw new RuntimeException("Failed to evaluate script: " + e, e);
    }
  }
  public static boolean compileBunchOfSources(
      @NotNull KotlinCoreEnvironment environment,
      @Nullable File jar,
      @Nullable File outputDir,
      @NotNull List<String> friendPaths,
      boolean includeRuntime) {

    ModuleVisibilityManager moduleVisibilityManager =
        ModuleVisibilityManager.SERVICE.getInstance(environment.getProject());

    for (String path : friendPaths) {
      moduleVisibilityManager.addFriendPath(path);
    }

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

    FqName mainClass = findMainClass(generationState, environment.getSourceFiles());

    try {
      writeOutput(
          environment.getConfiguration(),
          generationState.getFactory(),
          outputDir,
          jar,
          includeRuntime,
          mainClass);
      return true;
    } finally {
      generationState.destroy();
    }
  }
  @Nullable
  public static GenerationState analyzeAndGenerate(@NotNull KotlinCoreEnvironment environment) {
    AnalysisResult result = analyze(environment, null);

    if (result == null) {
      return null;
    }

    if (!result.getShouldGenerateCode()) return null;

    result.throwIfError();

    return generate(environment, result, environment.getSourceFiles(), null, null, null);
  }