From d8bf0d4859e28ddd23401fbe89f4e528aa09ddb3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 29 Apr 2016 21:04:40 +0200 Subject: [PATCH] Support Git repos in the Nix path E.g. $ nix-build -I nixpkgs=git://github.com/NixOS/nixpkgs '' -A hello This is not extremely useful yet because you can't specify a branch/revision. --- src/libexpr/parser.y | 13 ++++-- src/libexpr/primops/fetchgit.cc | 71 ++++++++++++++++++--------------- src/libexpr/primops/fetchgit.hh | 14 +++++++ src/libutil/util.cc | 6 +++ src/libutil/util.hh | 4 ++ 5 files changed, 71 insertions(+), 37 deletions(-) create mode 100644 src/libexpr/primops/fetchgit.hh diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 20ae1a696..776e5cb39 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -520,9 +520,10 @@ formal #include #include -#include -#include -#include +#include "eval.hh" +#include "download.hh" +#include "store-api.hh" +#include "primops/fetchgit.hh" namespace nix { @@ -657,7 +658,11 @@ std::pair EvalState::resolveSearchPathElem(const SearchPathEl if (isUri(elem.second)) { try { - res = { true, makeDownloader()->downloadCached(store, elem.second, true) }; + if (hasPrefix(elem.second, "git://") || hasSuffix(elem.second, ".git")) + // FIXME: support specifying revision/branch + res = { true, exportGit(store, elem.second, "master") }; + else + res = { true, makeDownloader()->downloadCached(store, elem.second, true) }; } catch (DownloadError & e) { printMsg(lvlError, format("warning: Nix search path entry ‘%1%’ cannot be downloaded, ignoring") % elem.second); res = { false, "" }; diff --git a/src/libexpr/primops/fetchgit.cc b/src/libexpr/primops/fetchgit.cc index e2a545ee0..bd440c8c6 100644 --- a/src/libexpr/primops/fetchgit.cc +++ b/src/libexpr/primops/fetchgit.cc @@ -5,6 +5,43 @@ namespace nix { +Path exportGit(ref store, const std::string & uri, const std::string & rev) +{ + if (!isUri(uri)) + throw EvalError(format("‘%s’ is not a valid URI") % uri); + + Path cacheDir = getCacheDir() + "/nix/git"; + + if (!pathExists(cacheDir)) { + createDirs(cacheDir); + runProgram("git", true, { "init", "--bare", cacheDir }); + } + + Activity act(*logger, lvlInfo, format("fetching Git repository ‘%s’") % uri); + + std::string localRef = "pid-" + std::to_string(getpid()); + Path localRefFile = cacheDir + "/refs/heads/" + localRef; + + runProgram("git", true, { "-C", cacheDir, "fetch", uri, rev + ":" + localRef }); + + std::string commitHash = chomp(readFile(localRefFile)); + + unlink(localRefFile.c_str()); + + debug(format("got revision ‘%s’") % commitHash); + + // FIXME: should pipe this, or find some better way to extract a + // revision. + auto tar = runProgram("git", true, { "-C", cacheDir, "archive", commitHash }); + + Path tmpDir = createTempDir(); + AutoDelete delTmpDir(tmpDir, true); + + runProgram("tar", true, { "x", "-C", tmpDir }, tar); + + return store->addToStore("git-export", tmpDir); +} + static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Value & v) { // FIXME: cut&paste from fetch(). @@ -35,39 +72,7 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va } else url = state.forceStringNoCtx(*args[0], pos); - if (!isUri(url)) - throw EvalError(format("‘%s’ is not a valid URI, at %s") % url % pos); - - Path cacheDir = getCacheDir() + "/nix/git"; - - if (!pathExists(cacheDir)) { - createDirs(cacheDir); - runProgram("git", true, { "init", "--bare", cacheDir }); - } - - Activity act(*logger, lvlInfo, format("fetching Git repository ‘%s’") % url); - - std::string localRef = "pid-" + std::to_string(getpid()); - Path localRefFile = cacheDir + "/refs/heads/" + localRef; - - runProgram("git", true, { "-C", cacheDir, "fetch", url, rev + ":" + localRef }); - - std::string commitHash = chomp(readFile(localRefFile)); - - unlink(localRefFile.c_str()); - - debug(format("got revision ‘%s’") % commitHash); - - // FIXME: should pipe this, or find some better way to extract a - // revision. - auto tar = runProgram("git", true, { "-C", cacheDir, "archive", commitHash }); - - Path tmpDir = createTempDir(); - AutoDelete delTmpDir(tmpDir, true); - - runProgram("tar", true, { "x", "-C", tmpDir }, tar); - - Path storePath = state.store->addToStore("git-export", tmpDir); + Path storePath = exportGit(state.store, url, rev); mkString(v, storePath, PathSet({storePath})); } diff --git a/src/libexpr/primops/fetchgit.hh b/src/libexpr/primops/fetchgit.hh new file mode 100644 index 000000000..6ffb21a96 --- /dev/null +++ b/src/libexpr/primops/fetchgit.hh @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "ref.hh" + +namespace nix { + +class Store; + +Path exportGit(ref store, + const std::string & uri, const std::string & rev); + +} diff --git a/src/libutil/util.cc b/src/libutil/util.cc index d73800905..67558cc0b 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1082,6 +1082,12 @@ bool statusOk(int status) } +bool hasPrefix(const string & s, const string & suffix) +{ + return s.compare(0, suffix.size(), suffix) == 0; +} + + bool hasSuffix(const string & s, const string & suffix) { return s.size() >= suffix.size() && string(s, s.size() - suffix.size()) == suffix; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 6e5ab55e3..f3f0f92a0 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -333,6 +333,10 @@ template bool string2Float(const string & s, N & n) } +/* Return true iff `s' starts with `prefix'. */ +bool hasPrefix(const string & s, const string & prefix); + + /* Return true iff `s' ends in `suffix'. */ bool hasSuffix(const string & s, const string & suffix);