about summary refs log tree commit diff
path: root/third_party/nix/src/nix-instantiate/nix-instantiate.cc
#include <iostream>
#include <map>

#include "libexpr/attr-path.hh"
#include "libexpr/common-eval-args.hh"
#include "libexpr/eval-inline.hh"
#include "libexpr/eval.hh"
#include "libexpr/get-drvs.hh"
#include "libexpr/value-to-json.hh"
#include "libexpr/value-to-xml.hh"
#include "libmain/shared.hh"
#include "libstore/globals.hh"
#include "libstore/store-api.hh"
#include "libutil/util.hh"
#include "nix/legacy.hh"

using namespace nix;

static Path gcRoot;
static int rootNr = 0;
static bool indirectRoot = false;

enum OutputKind { okPlain, okXML, okJSON };

void processExpr(EvalState& state, const Strings& attrPaths, bool parseOnly,
                 bool strict, Bindings& autoArgs, bool evalOnly,
                 OutputKind output, bool location, Expr* e) {
  if (parseOnly) {
    std::cout << format("%1%\n") % *e;
    return;
  }

  Value vRoot;
  state.eval(e, vRoot);

  for (auto& i : attrPaths) {
    Value& v(*findAlongAttrPath(state, i, autoArgs, vRoot));
    state.forceValue(v);

    PathSet context;
    if (evalOnly) {
      Value vRes;
      if (autoArgs.empty()) {
        vRes = v;
      } else {
        state.autoCallFunction(autoArgs, v, vRes);
      }
      if (output == okXML) {
        printValueAsXML(state, strict, location, vRes, std::cout, context);
      } else if (output == okJSON) {
        printValueAsJSON(state, strict, vRes, std::cout, context);
      } else {
        if (strict) {
          state.forceValueDeep(vRes);
        }
        std::cout << vRes << std::endl;
      }
    } else {
      DrvInfos drvs;
      getDerivations(state, v, "", autoArgs, drvs, false);
      for (auto& i : drvs) {
        Path drvPath = i.queryDrvPath();

        /* What output do we want? */
        std::string outputName = i.queryOutputName();
        if (outputName.empty()) {
          throw Error(
              format("derivation '%1%' lacks an 'outputName' attribute ") %
              drvPath);
        }

        if (gcRoot.empty()) {
          printGCWarning();
        } else {
          Path rootName = indirectRoot ? absPath(gcRoot) : gcRoot;
          if (++rootNr > 1) {
            rootName += "-" + std::to_string(rootNr);
          }
          auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
          if (store2) {
            drvPath = store2->addPermRoot(drvPath, rootName, indirectRoot);
          }
        }
        std::cout << format("%1%%2%\n") % drvPath %
                         (outputName != "out" ? "!" + outputName : "");
      }
    }
  }
}

static int _main(int argc, char** argv) {
  {
    Strings files;
    bool readStdin = false;
    bool fromArgs = false;
    bool findFile = false;
    bool evalOnly = false;
    bool parseOnly = false;
    OutputKind outputKind = okPlain;
    bool xmlOutputSourceLocation = true;
    bool strict = false;
    Strings attrPaths;
    bool wantsReadWrite = false;
    RepairFlag repair = NoRepair;

    struct MyArgs : LegacyArgs, MixEvalArgs {
      using LegacyArgs::LegacyArgs;
    };

    MyArgs myArgs(baseNameOf(argv[0]),
                  [&](Strings::iterator& arg, const Strings::iterator& end) {
                    if (*arg == "--help") {
                      showManPage("nix-instantiate");
                    } else if (*arg == "--version") {
                      printVersion("nix-instantiate");
                    } else if (*arg == "-") {
                      readStdin = true;
                    } else if (*arg == "--expr" || *arg == "-E") {
                      fromArgs = true;
                    } else if (*arg == "--eval" || *arg == "--eval-only") {
                      evalOnly = true;
                    } else if (*arg == "--read-write-mode") {
                      wantsReadWrite = true;
                    } else if (*arg == "--parse" || *arg == "--parse-only") {
                      parseOnly = evalOnly = true;
                    } else if (*arg == "--find-file") {
                      findFile = true;
                    } else if (*arg == "--attr" || *arg == "-A") {
                      attrPaths.push_back(getArg(*arg, arg, end));
                    } else if (*arg == "--add-root") {
                      gcRoot = getArg(*arg, arg, end);
                    } else if (*arg == "--indirect") {
                      indirectRoot = true;
                    } else if (*arg == "--xml") {
                      outputKind = okXML;
                    } else if (*arg == "--json") {
                      outputKind = okJSON;
                    } else if (*arg == "--no-location") {
                      xmlOutputSourceLocation = false;
                    } else if (*arg == "--strict") {
                      strict = true;
                    } else if (*arg == "--repair") {
                      repair = Repair;
                    } else if (*arg == "--dry-run") {
                      settings.readOnlyMode = true;
                    } else if (*arg != "" && arg->at(0) == '-') {
                      return false;
                    } else {
                      files.push_back(*arg);
                    }
                    return true;
                  });

    myArgs.parseCmdline(argvToStrings(argc, argv));

    initPlugins();

    if (evalOnly && !wantsReadWrite) {
      settings.readOnlyMode = true;
    }

    auto store = openStore();

    auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
    state->repair = repair;

    Bindings& autoArgs = *myArgs.getAutoArgs(*state);

    if (attrPaths.empty()) {
      attrPaths = {""};
    }

    if (findFile) {
      for (auto& i : files) {
        Path p = state->findFile(i);
        if (p.empty()) {
          throw Error(format("unable to find '%1%'") % i);
        }
        std::cout << p << std::endl;
      }
      return 0;
    }

    if (readStdin) {
      Expr* e = state->parseStdin();
      processExpr(*state, attrPaths, parseOnly, strict, autoArgs, evalOnly,
                  outputKind, xmlOutputSourceLocation, e);
    } else if (files.empty() && !fromArgs) {
      files.push_back("./default.nix");
    }

    for (auto& i : files) {
      Expr* e = fromArgs
                    ? state->parseExprFromString(i, absPath("."))
                    : state->parseExprFromFile(resolveExprPath(
                          state->checkSourcePath(lookupFileArg(*state, i))));
      processExpr(*state, attrPaths, parseOnly, strict, autoArgs, evalOnly,
                  outputKind, xmlOutputSourceLocation, e);
    }

    state->printStats();

    return 0;
  }
}

static RegisterLegacyCommand s1("nix-instantiate", _main);