lix/src/nix-prefetch-url/nix-prefetch-url.cc

234 lines
7.8 KiB
C++
Raw Normal View History

2015-10-01 14:47:43 +00:00
#include "hash.hh"
#include "shared.hh"
#include "download.hh"
#include "store-api.hh"
#include "eval.hh"
#include "eval-inline.hh"
#include "common-eval-args.hh"
#include "attr-path.hh"
#include "legacy.hh"
2018-10-20 06:11:22 +00:00
#include "finally.hh"
#include "progress-bar.hh"
#include "tarfile.hh"
2015-10-01 14:47:43 +00:00
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
2015-10-01 14:47:43 +00:00
using namespace nix;
/* If uri starts with mirror://, then resolve it using the list of
2015-10-01 14:47:43 +00:00
mirrors defined in Nixpkgs. */
string resolveMirrorUri(EvalState & state, string uri)
{
if (string(uri, 0, 9) != "mirror://") return uri;
string s(uri, 9);
auto p = s.find('/');
if (p == string::npos) throw Error("invalid mirror URI");
string mirrorName(s, 0, p);
Value vMirrors;
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
state.forceAttrs(vMirrors);
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
if (mirrorList == vMirrors.attrs->end())
throw Error(format("unknown mirror name '%1%'") % mirrorName);
2015-10-01 14:47:43 +00:00
state.forceList(*mirrorList->value);
if (mirrorList->value->listSize() < 1)
throw Error(format("mirror URI '%1%' did not expand to anything") % uri);
2015-10-01 14:47:43 +00:00
string mirror = state.forceString(*mirrorList->value->listElems()[0]);
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
}
static int _main(int argc, char * * argv)
2015-10-01 14:47:43 +00:00
{
{
2015-10-01 14:47:43 +00:00
HashType ht = htSHA256;
std::vector<string> args;
bool printPath = getEnv("PRINT_PATH") == "1";
bool fromExpr = false;
string attrPath;
bool unpack = false;
string name;
2015-10-01 14:47:43 +00:00
struct MyArgs : LegacyArgs, MixEvalArgs
{
using LegacyArgs::LegacyArgs;
};
MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](Strings::iterator & arg, const Strings::iterator & end) {
2015-10-01 14:47:43 +00:00
if (*arg == "--help")
showManPage("nix-prefetch-url");
else if (*arg == "--version")
printVersion("nix-prefetch-url");
else if (*arg == "--type") {
string s = getArg(*arg, arg, end);
ht = parseHashType(s);
if (ht == htUnknown)
throw UsageError(format("unknown hash type '%1%'") % s);
2015-10-01 14:47:43 +00:00
}
else if (*arg == "--print-path")
printPath = true;
else if (*arg == "--attr" || *arg == "-A") {
fromExpr = true;
attrPath = getArg(*arg, arg, end);
}
else if (*arg == "--unpack")
unpack = true;
else if (*arg == "--name")
name = getArg(*arg, arg, end);
2015-10-01 14:47:43 +00:00
else if (*arg != "" && arg->at(0) == '-')
return false;
else
args.push_back(*arg);
return true;
});
myArgs.parseCmdline(argvToStrings(argc, argv));
initPlugins();
if (args.size() > 2)
throw UsageError("too many arguments");
2015-10-01 14:47:43 +00:00
2018-10-20 06:11:22 +00:00
Finally f([]() { stopProgressBar(); });
if (isatty(STDERR_FILENO))
startProgressBar();
auto store = openStore();
auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
2015-10-01 14:47:43 +00:00
Bindings & autoArgs = *myArgs.getAutoArgs(*state);
/* If -A is given, get the URI from the specified Nix
expression. */
string uri;
if (!fromExpr) {
if (args.empty())
throw UsageError("you must specify a URI");
uri = args[0];
} else {
Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0]));
Value vRoot;
state->evalFile(path, vRoot);
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot));
state->forceAttrs(v);
/* Extract the URI. */
auto attr = v.attrs->find(state->symbols.create("urls"));
if (attr == v.attrs->end())
throw Error("attribute set does not contain a 'urls' attribute");
state->forceList(*attr->value);
if (attr->value->listSize() < 1)
throw Error("'urls' list is empty");
uri = state->forceString(*attr->value->listElems()[0]);
/* Extract the hash mode. */
attr = v.attrs->find(state->symbols.create("outputHashMode"));
if (attr == v.attrs->end())
printInfo("warning: this does not look like a fetchurl call");
else
unpack = state->forceString(*attr->value) == "recursive";
/* Extract the name. */
if (name.empty()) {
attr = v.attrs->find(state->symbols.create("name"));
if (attr != v.attrs->end())
name = state->forceString(*attr->value);
}
}
2015-10-01 14:47:43 +00:00
/* Figure out a name in the Nix store. */
if (name.empty())
name = baseNameOf(uri);
2015-10-01 14:47:43 +00:00
if (name.empty())
throw Error(format("cannot figure out file name for '%1%'") % uri);
2015-10-01 14:47:43 +00:00
/* If an expected hash is given, the file may already exist in
the store. */
Hash hash, expectedHash(ht);
std::optional<StorePath> storePath;
2015-10-01 14:47:43 +00:00
if (args.size() == 2) {
expectedHash = Hash(args[1], ht);
storePath = store->makeFixedOutputPath(unpack, expectedHash, name);
if (store->isValidPath(*storePath))
2015-10-01 14:47:43 +00:00
hash = expectedHash;
else
storePath.reset();
2015-10-01 14:47:43 +00:00
}
if (!storePath) {
2015-10-01 14:47:43 +00:00
auto actualUri = resolveMirrorUri(*state, uri);
2015-10-01 14:47:43 +00:00
AutoDelete tmpDir(createTempDir(), true);
Path tmpFile = (Path) tmpDir + "/tmp";
/* Download the file. */
{
AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0600);
if (!fd) throw SysError("creating temporary file '%s'", tmpFile);
FdSink sink(fd.get());
DownloadRequest req(actualUri);
req.decompress = false;
getDownloader()->download(std::move(req), sink);
}
2015-10-01 14:47:43 +00:00
/* Optionally unpack the file. */
if (unpack) {
printInfo("unpacking...");
Path unpacked = (Path) tmpDir + "/unpacked";
createDirs(unpacked);
2019-12-07 15:35:14 +00:00
unpackTarfile(tmpFile, unpacked);
/* If the archive unpacks to a single file/directory, then use
that as the top-level. */
auto entries = readDirectory(unpacked);
if (entries.size() == 1)
tmpFile = unpacked + "/" + entries[0].name;
else
tmpFile = unpacked;
}
2015-10-01 14:47:43 +00:00
/* FIXME: inefficient; addToStore() will also hash
this. */
hash = unpack ? hashPath(ht, tmpFile).first : hashFile(ht, tmpFile);
2015-10-01 14:47:43 +00:00
if (expectedHash != Hash(ht) && expectedHash != hash)
throw Error(format("hash mismatch for '%1%'") % uri);
2015-10-01 14:47:43 +00:00
/* Copy the file to the Nix store. FIXME: if RemoteStore
implemented addToStoreFromDump() and downloadFile()
supported a sink, we could stream the download directly
into the Nix store. */
storePath = store->addToStore(name, tmpFile, unpack, ht);
assert(*storePath == store->makeFixedOutputPath(unpack, hash, name));
2015-10-01 14:47:43 +00:00
}
stopProgressBar();
if (!printPath)
printInfo("path is '%s'", store->printStorePath(*storePath));
2015-10-01 14:47:43 +00:00
std::cout << printHash16or32(hash) << std::endl;
if (printPath)
std::cout << store->printStorePath(*storePath) << std::endl;
return 0;
}
2015-10-01 14:47:43 +00:00
}
static RegisterLegacyCommand s1("nix-prefetch-url", _main);