diff options
-rw-r--r-- | doc/manual/expressions/builtins.xml | 27 | ||||
-rw-r--r-- | src/libexpr/common-eval-args.hh | 2 | ||||
-rw-r--r-- | src/libexpr/primops/fetchMercurial.cc | 28 | ||||
-rw-r--r-- | src/libexpr/primops/fetchgit.cc | 2 | ||||
-rw-r--r-- | src/libutil/util.cc | 41 | ||||
-rw-r--r-- | src/libutil/util.hh | 17 | ||||
-rw-r--r-- | tests/fetchMercurial.sh | 18 |
7 files changed, 96 insertions, 39 deletions
diff --git a/doc/manual/expressions/builtins.xml b/doc/manual/expressions/builtins.xml index 615314880aba..5e88b88561fc 100644 --- a/doc/manual/expressions/builtins.xml +++ b/doc/manual/expressions/builtins.xml @@ -264,13 +264,13 @@ stdenv.mkDerivation { … } Note that when obtaining the hash with <varname>nix-prefetch-url </varname> the option <varname>--unpack</varname> is required. - </para> - - <para>This function can also verify the contents against a hash. - In that case, the function takes a set instead of a URL. The set + </para> + + <para>This function can also verify the contents against a hash. + In that case, the function takes a set instead of a URL. The set requires the attribute <varname>url</varname> and the attribute <varname>sha256</varname>, e.g. - + <programlisting> with import (fetchTarball { url = https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz; @@ -279,7 +279,7 @@ with import (fetchTarball { stdenv.mkDerivation { … } </programlisting> - + </para> <para>This function is not available if <link @@ -600,6 +600,16 @@ x: x + 456</programlisting> </varlistentry> + <varlistentry><term><function>builtins.isFloat</function> + <replaceable>e</replaceable></term> + + <listitem><para>Return <literal>true</literal> if + <replaceable>e</replaceable> evaluates to a float, and + <literal>false</literal> otherwise.</para></listitem> + + </varlistentry> + + <varlistentry><term><function>builtins.isBool</function> <replaceable>e</replaceable></term> @@ -1245,8 +1255,9 @@ stdenv.mkDerivation (rec { <replaceable>e</replaceable>, namely <literal>"int"</literal>, <literal>"bool"</literal>, <literal>"string"</literal>, <literal>"path"</literal>, <literal>"null"</literal>, - <literal>"set"</literal>, <literal>"list"</literal> or - <literal>"lambda"</literal>.</para></listitem> + <literal>"set"</literal>, <literal>"list"</literal>, + <literal>"lambda"</literal> or + <literal>"float"</literal>.</para></listitem> </varlistentry> diff --git a/src/libexpr/common-eval-args.hh b/src/libexpr/common-eval-args.hh index 09fa406b2cdc..be7fda783783 100644 --- a/src/libexpr/common-eval-args.hh +++ b/src/libexpr/common-eval-args.hh @@ -6,7 +6,7 @@ namespace nix { class Store; class EvalState; -struct Bindings; +class Bindings; struct MixEvalArgs : virtual Args { diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 391ce45a0a98..e68d1a2a5539 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -22,6 +22,8 @@ struct HgInfo uint64_t revCount = 0; }; +std::regex commitHashRegex("^[0-9a-fA-F]{40}$"); + HgInfo exportMercurial(ref<Store> store, const std::string & uri, std::string rev, const std::string & name) { @@ -66,20 +68,28 @@ HgInfo exportMercurial(ref<Store> store, const std::string & uri, Path stampFile = fmt("%s/.hg/%s.stamp", cacheDir, hashString(htSHA512, rev).to_string(Base32, false)); /* If we haven't pulled this repo less than ‘tarball-ttl’ seconds, - do so now. FIXME: don't do this if "rev" is a hash and we - fetched it previously */ + do so now. */ time_t now = time(0); struct stat st; if (stat(stampFile.c_str(), &st) != 0 || st.st_mtime < now - settings.tarballTtl) { - Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", uri)); - - if (pathExists(cacheDir)) { - runProgram("hg", true, { "pull", "-R", cacheDir, "--", uri }); - } else { - createDirs(dirOf(cacheDir)); - runProgram("hg", true, { "clone", "--noupdate", "--", uri, cacheDir }); + /* Except that if this is a commit hash that we already have, + we don't have to pull again. */ + if (!(std::regex_match(rev, commitHashRegex) + && pathExists(cacheDir) + && runProgram( + RunOptions("hg", { "log", "-R", cacheDir, "-r", rev, "--template", "1" }) + .killStderr(true)).second == "1")) + { + Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", uri)); + + if (pathExists(cacheDir)) { + runProgram("hg", true, { "pull", "-R", cacheDir, "--", uri }); + } else { + createDirs(dirOf(cacheDir)); + runProgram("hg", true, { "clone", "--noupdate", "--", uri, cacheDir }); + } } writeFile(stampFile, ""); diff --git a/src/libexpr/primops/fetchgit.cc b/src/libexpr/primops/fetchgit.cc index 879e3656a25a..4b5ead320c81 100644 --- a/src/libexpr/primops/fetchgit.cc +++ b/src/libexpr/primops/fetchgit.cc @@ -56,7 +56,7 @@ GitInfo exportGit(ref<Store> store, const std::string & uri, } } - if (!ref) ref = "master"; + if (!ref) ref = "master"s; if (rev != "") { std::regex revRegex("^[0-9a-fA-F]{40}$"); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 9346d5dc4cf8..f56153cd4a8a 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -896,31 +896,45 @@ std::vector<char *> stringsToCharPtrs(const Strings & ss) string runProgram(Path program, bool searchPath, const Strings & args, const std::experimental::optional<std::string> & input) { + RunOptions opts(program, args); + opts.searchPath = searchPath; + opts.input = input; + + auto res = runProgram(opts); + + if (!statusOk(res.first)) + throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first))); + + return res.second; +} + +std::pair<int, std::string> runProgram(const RunOptions & options) +{ checkInterrupt(); /* Create a pipe. */ Pipe out, in; out.create(); - if (input) in.create(); + if (options.input) in.create(); /* Fork. */ Pid pid = startProcess([&]() { if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) throw SysError("dupping stdout"); - if (input && dup2(in.readSide.get(), STDIN_FILENO) == -1) + if (options.input && dup2(in.readSide.get(), STDIN_FILENO) == -1) throw SysError("dupping stdin"); - Strings args_(args); - args_.push_front(program); + Strings args_(options.args); + args_.push_front(options.program); restoreSignals(); - if (searchPath) - execvp(program.c_str(), stringsToCharPtrs(args_).data()); + if (options.searchPath) + execvp(options.program.c_str(), stringsToCharPtrs(args_).data()); else - execv(program.c_str(), stringsToCharPtrs(args_).data()); + execv(options.program.c_str(), stringsToCharPtrs(args_).data()); - throw SysError(format("executing '%1%'") % program); + throw SysError("executing '%1%'", options.program); }); out.writeSide = -1; @@ -935,11 +949,11 @@ string runProgram(Path program, bool searchPath, const Strings & args, }); - if (input) { + if (options.input) { in.readSide = -1; writerThread = std::thread([&]() { try { - writeFull(in.writeSide.get(), *input); + writeFull(in.writeSide.get(), *options.input); promise.set_value(); } catch (...) { promise.set_exception(std::current_exception()); @@ -952,14 +966,11 @@ string runProgram(Path program, bool searchPath, const Strings & args, /* Wait for the child to finish. */ int status = pid.wait(); - if (!statusOk(status)) - throw ExecError(status, format("program '%1%' %2%") - % program % statusToString(status)); /* Wait for the writer thread to finish. */ - if (input) promise.get_future().get(); + if (options.input) promise.get_future().get(); - return result; + return {status, result}; } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 63a93f2ca6a7..a3494e09b09b 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -245,6 +245,23 @@ string runProgram(Path program, bool searchPath = false, const Strings & args = Strings(), const std::experimental::optional<std::string> & input = {}); +struct RunOptions +{ + Path program; + bool searchPath = true; + Strings args; + std::experimental::optional<std::string> input; + bool _killStderr = false; + + RunOptions(const Path & program, const Strings & args) + : program(program), args(args) { }; + + RunOptions & killStderr(bool v) { _killStderr = true; return *this; } +}; + +std::pair<int, std::string> runProgram(const RunOptions & options); + + class ExecError : public Error { public: diff --git a/tests/fetchMercurial.sh b/tests/fetchMercurial.sh index 5e4782658a81..83bf1331a409 100644 --- a/tests/fetchMercurial.sh +++ b/tests/fetchMercurial.sh @@ -9,7 +9,7 @@ clearStore repo=$TEST_ROOT/hg -rm -rfv $repo ${repo}-tmp $TEST_HOME/.cache/nix/hg +rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix/hg hg init $repo echo '[ui]' >> $repo/.hg/hgrc @@ -24,13 +24,14 @@ echo world > $repo/hello hg commit --cwd $repo -m 'Bla2' rev2=$(hg log --cwd $repo -r tip --template '{node}') -hg log --cwd $repo - -hg log --cwd $repo -r tip --template '{node}\n' - +# Fetch the default branch. path=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath") [[ $(cat $path/hello) = world ]] +# Fetch using an explicit revision hash. +path2=$(nix eval --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath") +[[ $path = $path2 ]] + # Fetch again. This should be cached. mv $repo ${repo}-tmp path2=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath") @@ -43,6 +44,13 @@ path2=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath") # But with TTL 0, it should fail. (! nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial file://$repo)") +# Fetching with a explicit hash should succeed. +path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath") +[[ $path = $path2 ]] + +path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev1\"; }).outPath") +[[ $(cat $path2/hello) = utrecht ]] + mv ${repo}-tmp $repo # Using a clean working tree should produce the same result. |