public ImmutableList getBuildSteps()

in src/com/facebook/buck/features/haskell/HaskellGhciRule.java [310:578]


  public ImmutableList<Step> getBuildSteps(
      BuildContext context, BuildableContext buildableContext) {

    SourcePathResolverAdapter resolver = context.getSourcePathResolver();

    String name = getBuildTarget().getShortName();
    Path dir = getOutputDir();
    Path so = resolver.getRelativePath(omnibusSharedObject.getSourcePathToOutput());
    Path packagesDir = dir.resolve(name + ".packages");
    Path symlinkDir = dir.resolve(HaskellGhciDescription.getSoLibsRelDir(getBuildTarget()));
    Path symlinkPreloadDir = dir.resolve(name + ".preload-symlinks");

    ImmutableList.Builder<String> compilerFlagsBuilder = ImmutableList.builder();
    compilerFlagsBuilder.addAll(compilerFlags);

    ImmutableList.Builder<Step> steps = ImmutableList.builder();
    steps.addAll(
        MakeCleanDirectoryStep.of(
            BuildCellRelativePath.fromCellRelativePath(
                context.getBuildCellRootPath(), getProjectFilesystem(), dir)));
    steps.addAll(
        MakeCleanDirectoryStep.of(
            BuildCellRelativePath.fromCellRelativePath(
                context.getBuildCellRootPath(), getProjectFilesystem(), symlinkDir)));
    steps.addAll(
        MakeCleanDirectoryStep.of(
            BuildCellRelativePath.fromCellRelativePath(
                context.getBuildCellRootPath(), getProjectFilesystem(), symlinkPreloadDir)));
    steps.addAll(
        MakeCleanDirectoryStep.of(
            BuildCellRelativePath.fromCellRelativePath(
                context.getBuildCellRootPath(), getProjectFilesystem(), packagesDir)));

    steps.add(CopyStep.forFile(getProjectFilesystem(), so, dir.resolve(so.getFileName())));

    symlinkLibs(resolver, symlinkDir, steps, solibs);
    symlinkLibs(resolver, symlinkPreloadDir, steps, preloadLibs);

    ImmutableSet.Builder<String> pkgdirs = ImmutableSet.builder();
    for (HaskellPackage pkg : prebuiltHaskellPackages) {
      try {
        pkgdirs.add(resolver.getRelativePath(pkg.getPackageDb()).toRealPath().toString());
      } catch (IOException ex) {
        throw new RuntimeException(ex);
      }
    }

    for (HaskellPackage pkg : haskellPackages) {
      String pkgname = pkg.getInfo().getName();
      Path pkgdir = packagesDir.resolve(pkgname);
      steps.addAll(
          MakeCleanDirectoryStep.of(
              BuildCellRelativePath.fromCellRelativePath(
                  context.getBuildCellRootPath(), getProjectFilesystem(), pkgdir)));

      Path pkgDbSrc = resolver.getRelativePath(pkg.getPackageDb());
      steps.add(
          CopyStep.forDirectory(
              getProjectFilesystem(),
              pkgDbSrc,
              pkgdir,
              CopyStep.DirectoryMode.DIRECTORY_AND_CONTENTS));

      ImmutableSet.Builder<SourcePath> artifacts = ImmutableSet.builder();
      artifacts.addAll(pkg.getLibraries());

      if (archiveContents == ArchiveContents.THIN) {
        // this is required because the .a files above are thin archives,
        // they merely point to the .o files via a relative path.
        artifacts.addAll(pkg.getObjects());
      }

      artifacts.addAll(pkg.getInterfaces());

      for (SourcePath artifact : artifacts.build()) {
        Path source = resolver.getRelativePath(artifact);
        Path destination = pkgdir.resolve(source.getParent().getFileName());
        steps.add(
            MkdirStep.of(
                BuildCellRelativePath.fromCellRelativePath(
                    context.getBuildCellRootPath(), getProjectFilesystem(), destination)));
        steps.add(
            CopyStep.forDirectory(
                getProjectFilesystem(),
                source,
                destination,
                CopyStep.DirectoryMode.DIRECTORY_AND_CONTENTS));
      }

      pkgdirs.add("${DIR}/" + dir.relativize(pkgdir.resolve(pkgDbSrc.getFileName())));
    }

    ImmutableSet.Builder<String> exposedPkgs = ImmutableSet.builder();
    for (HaskellPackage pkg : firstOrderHaskellPackages) {
      exposedPkgs.add(String.format("%s-%s", pkg.getInfo().getName(), pkg.getInfo().getVersion()));
    }

    // iserv script
    Optional<Path> iservScript = Optional.empty();

    if (!preloadLibs.isEmpty()) {
      iservScript = Optional.of(dir.resolve("iserv"));
      compilerFlagsBuilder.add("-fexternal-interpreter");
      steps.add(
          new AbstractExecutionStep("ghci_iserv_wrapper") {
            @Override
            public StepExecutionResult execute(ExecutionContext context) throws IOException {
              String template;
              template = new String(Files.readAllBytes(ghciIservScriptTemplate), Charsets.UTF_8);
              ST st = new ST(template);
              ImmutableSet.Builder<String> preloadLibrariesB = ImmutableSet.builder();
              for (String libPath : preloadLibs.keySet()) {
                preloadLibrariesB.add(
                    "${DIR}/" + dir.relativize(symlinkPreloadDir.resolve(libPath)));
              }
              ImmutableSet<String> preloadLibraries = preloadLibrariesB.build();
              st.add("name", name + "-iserv");
              st.add("preload_libs", Joiner.on(':').join(preloadLibraries));
              if (enableProfiling) {
                st.add("ghci_iserv_path", ghciIServProf.toRealPath().toString());
              } else {
                st.add("ghci_iserv_path", ghciIServ.toRealPath().toString());
              }
              Path actualIserv = dir.resolve("iserv");
              if (enableProfiling) {
                actualIserv = dir.resolve("iserv-prof");
              }
              return new WriteFileStep(
                      getProjectFilesystem(),
                      Objects.requireNonNull(st.render()),
                      actualIserv, /* executable */
                      true)
                  .execute(context);
            }
          });
    }

    // .ghci file
    StringBuilder startGhciContents = new StringBuilder();
    if (iservScript.isPresent()) {
      // Need to unset preloaded deps for `iserv`
      startGhciContents.append("System.Environment.unsetEnv \"LD_PRELOAD\"\n");
    }
    startGhciContents.append(":set ");
    startGhciContents.append(
        Joiner.on(' ')
            .join(
                ImmutableList.<String>builder()
                    .addAll(
                        MoreIterables.zipAndConcat(
                            Iterables.cycle("-package"), exposedPkgs.build()))
                    .build()));

    if (ghciInit.isPresent()) {
      try {
        startGhciContents.append('\n');
        List<String> lines =
            Files.readAllLines(resolver.getRelativePath(ghciInit.get()), StandardCharsets.UTF_8);
        startGhciContents.append(Joiner.on('\n').join(lines));
      } catch (IOException ex) {
        throw new RuntimeException(ex);
      }
    }

    Path startGhci = dir.resolve("start.ghci");
    steps.add(
        new WriteFileStep(
            getProjectFilesystem(),
            startGhciContents.toString(),
            startGhci,
            /* executable */ false));

    // ghciBinDep
    ImmutableList.Builder<String> srcpaths = ImmutableList.builder();
    for (SourcePath sp : srcs.getSourcePaths()) {
      srcpaths.add(resolver.getRelativePath(sp).toString());
    }

    String ghcPath = null;
    try {
      if (ghciBinDep.isPresent()) {

        Path binDir = dir.resolve(name + ".bin");
        Path bin = binDir.resolve("ghci");
        SourcePath sp = ghciBinDep.get();

        steps.addAll(
            MakeCleanDirectoryStep.of(
                BuildCellRelativePath.fromCellRelativePath(
                    context.getBuildCellRootPath(), getProjectFilesystem(), binDir)));

        steps.add(CopyStep.forFile(getProjectFilesystem(), resolver.getRelativePath(sp), bin));

        ghcPath = "${DIR}/" + dir.relativize(bin) + " -B" + ghciLib.toRealPath();
      } else {
        ghcPath = ghciGhc.toRealPath().toString();
      }
    } catch (IOException ex) {
      throw new RuntimeException(ex);
    }

    String pkgdbs =
        Joiner.on(' ')
            .join(
                ImmutableList.<String>builder()
                    .addAll(
                        MoreIterables.zipAndConcat(Iterables.cycle("-package-db"), pkgdirs.build()))
                    .build());
    String exposed =
        Joiner.on(' ')
            .join(
                ImmutableList.<String>builder()
                    .addAll(
                        MoreIterables.zipAndConcat(
                            Iterables.cycle("-expose-package"), exposedPkgs.build()))
                    .build());

    if (enableProfiling) {
      compilerFlagsBuilder.addAll(HaskellDescriptionUtils.PROF_FLAGS);
    }
    compilerFlagsBuilder.addAll(HaskellDescriptionUtils.PIC_FLAGS);

    String ghc = ghcPath;
    ImmutableMap.Builder<String, String> templateArgs = ImmutableMap.builder();
    try {
      templateArgs.put("name", name);
      templateArgs.put("start_ghci", dir.relativize(startGhci).toString());
      templateArgs.put("exposed_packages", exposed);
      templateArgs.put("package_dbs", pkgdbs);
      templateArgs.put("compiler_flags", Joiner.on(' ').join(compilerFlagsBuilder.build()));
      templateArgs.put("srcs", Joiner.on(' ').join(srcpaths.build()));
      templateArgs.put("squashed_so", dir.relativize(dir.resolve(so.getFileName())).toString());
      templateArgs.put("binutils_path", ghciBinutils.toRealPath().toString());
      // ghc_path points to the ghc tool for this binary
      templateArgs.put("ghc_path", ghciGhc.toRealPath().toString());
      // user_ghci_path points to user-defined ghci binary, which can be eithe
      // the same as ghc_path, or a haskell_binary itself compiled during this
      // buck run.
      templateArgs.put("user_ghci_path", ghc);
      templateArgs.put("cxx_path", ghciCxx.toRealPath().toString());
      templateArgs.put("cc_path", ghciCc.toRealPath().toString());
      templateArgs.put("cpp_path", ghciCpp.toRealPath().toString());
      templateArgs.put("ghc_pkg_path", ghciPackager.toRealPath().toString());
      if (iservScript.isPresent()) {
        templateArgs.put("iserv_path", dir.relativize(iservScript.get()).toString());
      }
    } catch (IOException ex) {
      throw new RuntimeException(ex);
    }

    Path script = scriptPath();
    steps.add(
        new StringTemplateStep(
            ghciScriptTemplate, getProjectFilesystem(), script, templateArgs.build()));
    steps.add(new MakeExecutableStep(getProjectFilesystem(), script));

    for (SourcePath s : extraScriptTemplates) {
      Path templateAbsPath = resolver.getAbsolutePath(s);
      Path extraScript = dir.resolve(templateAbsPath.getFileName());
      steps.add(
          new StringTemplateStep(
              templateAbsPath, getProjectFilesystem(), extraScript, templateArgs.build()));
      steps.add(new MakeExecutableStep(getProjectFilesystem(), extraScript));
    }

    buildableContext.recordArtifact(dir);

    return steps.build();
  }