nix-prefetch-url: Support unpacking tarballs
This allows nix-prefetch-url to prefetch the output of fetchzip and its wrappers (like fetchFromGitHub). For example: $ nix-prefetch-url --unpack https://github.com/NixOS/patchelf/archive/0.8.tar.gz or from a Nix expression: $ nix-prefetch-url -A nix-repl.src In the latter case, --unpack can be omitted because nix-repl.src is a fetchFromGitHub derivation and thus has "outputHashMode" set to "recursive".
This commit is contained in:
parent
1abda8e173
commit
b54f447df9
|
@ -81,6 +81,16 @@ downloaded file in the Nix store is also printed.</para>
|
||||||
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry><term><option>--unpack</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Unpack the archive (which must be a tarball or zip
|
||||||
|
file) and add the result to the Nix store. The resulting hash can
|
||||||
|
be used with functions such as Nixpkgs’s
|
||||||
|
<varname>fetchzip</varname> or
|
||||||
|
<varname>fetchFromGitHub</varname>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsection>
|
</refsection>
|
||||||
|
@ -94,7 +104,12 @@ $ nix-prefetch-url ftp://ftp.gnu.org/pub/gnu/hello/hello-2.10.tar.gz
|
||||||
|
|
||||||
$ nix-prefetch-url --print-path mirror://gnu/hello/hello-2.10.tar.gz
|
$ nix-prefetch-url --print-path mirror://gnu/hello/hello-2.10.tar.gz
|
||||||
0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
|
0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
|
||||||
/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz</screen>
|
/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz
|
||||||
|
|
||||||
|
$ nix-prefetch-url --unpack --print-path https://github.com/NixOS/patchelf/archive/0.8.tar.gz
|
||||||
|
079agjlv0hrv7fxnx9ngipx14gyncbkllxrp9cccnh3a50fxcmy7
|
||||||
|
/nix/store/19zrmhm3m40xxaw81c8cqm6aljgrnwj2-0.8.tar.gz
|
||||||
|
</screen>
|
||||||
|
|
||||||
</refsection>
|
</refsection>
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ int main(int argc, char * * argv)
|
||||||
bool fromExpr = false;
|
bool fromExpr = false;
|
||||||
string attrPath;
|
string attrPath;
|
||||||
std::map<string, string> autoArgs_;
|
std::map<string, string> autoArgs_;
|
||||||
|
bool unpack = false;
|
||||||
|
|
||||||
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
|
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
|
||||||
if (*arg == "--help")
|
if (*arg == "--help")
|
||||||
|
@ -71,6 +72,8 @@ int main(int argc, char * * argv)
|
||||||
fromExpr = true;
|
fromExpr = true;
|
||||||
attrPath = getArg(*arg, arg, end);
|
attrPath = getArg(*arg, arg, end);
|
||||||
}
|
}
|
||||||
|
else if (*arg == "--unpack")
|
||||||
|
unpack = true;
|
||||||
else if (parseAutoArgs(arg, end, autoArgs_))
|
else if (parseAutoArgs(arg, end, autoArgs_))
|
||||||
;
|
;
|
||||||
else if (parseSearchPathArg(arg, end, searchPath))
|
else if (parseSearchPathArg(arg, end, searchPath))
|
||||||
|
@ -103,13 +106,22 @@ int main(int argc, char * * argv)
|
||||||
state.evalFile(path, vRoot);
|
state.evalFile(path, vRoot);
|
||||||
Value & v(*findAlongAttrPath(state, attrPath, autoArgs, vRoot));
|
Value & v(*findAlongAttrPath(state, attrPath, autoArgs, vRoot));
|
||||||
state.forceAttrs(v);
|
state.forceAttrs(v);
|
||||||
auto urls = v.attrs->find(state.symbols.create("urls"));
|
|
||||||
if (urls == v.attrs->end())
|
/* 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");
|
throw Error("attribute set does not contain a ‘urls’ attribute");
|
||||||
state.forceList(*urls->value);
|
state.forceList(*attr->value);
|
||||||
if (urls->value->listSize() < 1)
|
if (attr->value->listSize() < 1)
|
||||||
throw Error("‘urls’ list is empty");
|
throw Error("‘urls’ list is empty");
|
||||||
uri = state.forceString(*urls->value->listElems()[0]);
|
uri = state.forceString(*attr->value->listElems()[0]);
|
||||||
|
|
||||||
|
/* Extract the hash mode. */
|
||||||
|
attr = v.attrs->find(state.symbols.create("outputHashMode"));
|
||||||
|
if (attr == v.attrs->end())
|
||||||
|
printMsg(lvlInfo, "warning: this does not look like a fetchurl call");
|
||||||
|
else
|
||||||
|
unpack = state.forceString(*attr->value) == "recursive";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Figure out a name in the Nix store. */
|
/* Figure out a name in the Nix store. */
|
||||||
|
@ -123,7 +135,7 @@ int main(int argc, char * * argv)
|
||||||
Path storePath;
|
Path storePath;
|
||||||
if (args.size() == 2) {
|
if (args.size() == 2) {
|
||||||
expectedHash = parseHash16or32(ht, args[1]);
|
expectedHash = parseHash16or32(ht, args[1]);
|
||||||
storePath = makeFixedOutputPath(false, ht, expectedHash, name);
|
storePath = makeFixedOutputPath(unpack, ht, expectedHash, name);
|
||||||
if (store->isValidPath(storePath))
|
if (store->isValidPath(storePath))
|
||||||
hash = expectedHash;
|
hash = expectedHash;
|
||||||
else
|
else
|
||||||
|
@ -134,28 +146,48 @@ int main(int argc, char * * argv)
|
||||||
|
|
||||||
auto actualUri = resolveMirrorUri(state, uri);
|
auto actualUri = resolveMirrorUri(state, uri);
|
||||||
|
|
||||||
if (uri != actualUri)
|
|
||||||
printMsg(lvlInfo, format("‘%1%’ expands to ‘%2%’") % uri % actualUri);
|
|
||||||
|
|
||||||
/* Download the file. */
|
/* Download the file. */
|
||||||
|
printMsg(lvlInfo, format("downloading ‘%1%’...") % actualUri);
|
||||||
auto result = downloadFile(actualUri);
|
auto result = downloadFile(actualUri);
|
||||||
|
|
||||||
|
AutoDelete tmpDir(createTempDir(), true);
|
||||||
|
Path tmpFile = (Path) tmpDir + "/tmp";
|
||||||
|
writeFile(tmpFile, result.data);
|
||||||
|
|
||||||
|
/* Optionally unpack the file. */
|
||||||
|
if (unpack) {
|
||||||
|
printMsg(lvlInfo, "unpacking...");
|
||||||
|
Path unpacked = (Path) tmpDir + "/unpacked";
|
||||||
|
createDirs(unpacked);
|
||||||
|
if (hasSuffix(baseNameOf(uri), ".zip"))
|
||||||
|
runProgram("unzip", true, {"-qq", tmpFile, "-d", unpacked}, "");
|
||||||
|
else
|
||||||
|
// FIXME: this requires GNU tar for decompression.
|
||||||
|
runProgram("tar", true, {"xf", tmpFile, "-C", 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: inefficient; addToStore() will also hash
|
||||||
|
this. */
|
||||||
|
hash = unpack ? hashPath(ht, tmpFile).first : hashString(ht, result.data);
|
||||||
|
|
||||||
|
if (expectedHash != Hash(ht) && expectedHash != hash)
|
||||||
|
throw Error(format("hash mismatch for ‘%1%’") % uri);
|
||||||
|
|
||||||
/* Copy the file to the Nix store. FIXME: if RemoteStore
|
/* Copy the file to the Nix store. FIXME: if RemoteStore
|
||||||
implemented addToStoreFromDump() and downloadFile()
|
implemented addToStoreFromDump() and downloadFile()
|
||||||
supported a sink, we could stream the download directly
|
supported a sink, we could stream the download directly
|
||||||
into the Nix store. */
|
into the Nix store. */
|
||||||
AutoDelete tmpDir(createTempDir(), true);
|
storePath = store->addToStore(name, tmpFile, unpack, ht);
|
||||||
Path tmpFile = (Path) tmpDir + "/tmp";
|
|
||||||
writeFile(tmpFile, result.data);
|
|
||||||
|
|
||||||
/* FIXME: inefficient; addToStore() will also hash
|
assert(storePath == makeFixedOutputPath(unpack, ht, hash, name));
|
||||||
this. */
|
|
||||||
hash = hashString(ht, result.data);
|
|
||||||
|
|
||||||
if (expectedHash != Hash(ht) && expectedHash != hash)
|
|
||||||
throw Error(format("hash mismatch for ‘%1%’") % uri);
|
|
||||||
|
|
||||||
storePath = store->addToStore(name, tmpFile, false, ht);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!printPath)
|
if (!printPath)
|
||||||
|
|
Loading…
Reference in a new issue