diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml
index cf025aaf5..7d0bfe7fd 100644
--- a/doc/manual/release-notes.xml
+++ b/doc/manual/release-notes.xml
@@ -36,6 +36,10 @@
TODO: “or” keyword.
+
+ TODO: Nix expression search path (import <foo/bar.nix>).
+
+
diff --git a/scripts/nix-build.in b/scripts/nix-build.in
index f9d81b36c..d9d1da73b 100644
--- a/scripts/nix-build.in
+++ b/scripts/nix-build.in
@@ -76,10 +76,10 @@ EOF
$outLink = $ARGV[$n];
}
- elsif ($arg eq "--attr" or $arg eq "-A") {
+ elsif ($arg eq "--attr" or $arg eq "-A" or $arg eq "-I") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
- push @instArgs, ("--attr", $ARGV[$n]);
+ push @instArgs, ($arg, $ARGV[$n]);
}
elsif ($arg eq "--arg" || $arg eq "--argstr") {
diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc
index bab31f493..d029d2ec3 100644
--- a/src/libexpr/common-opts.cc
+++ b/src/libexpr/common-opts.cc
@@ -33,5 +33,15 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
return true;
}
-
+
+bool parseSearchPathArg(const string & arg, Strings::iterator & i,
+ const Strings::iterator & argsEnd, EvalState & state)
+{
+ if (arg != "-I") return false;
+ if (i == argsEnd) throw UsageError(format("`%1%' requires an argument") % arg);;
+ state.addToSearchPath(*i++);
+ return true;
+}
+
+
}
diff --git a/src/libexpr/common-opts.hh b/src/libexpr/common-opts.hh
index 80298ce55..6b7247fc3 100644
--- a/src/libexpr/common-opts.hh
+++ b/src/libexpr/common-opts.hh
@@ -11,6 +11,9 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
const Strings::iterator & argsEnd, EvalState & state,
Bindings & autoArgs);
+bool parseSearchPathArg(const string & arg, Strings::iterator & i,
+ const Strings::iterator & argsEnd, EvalState & state);
+
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 5701452f9..674fa96f0 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -181,6 +181,12 @@ EvalState::EvalState()
gcInitialised = true;
}
#endif
+
+ /* Initialise the Nix expression search path. */
+ searchPathInsertionPoint = searchPath.end();
+ Strings paths = tokenizeString(getEnv("NIX_PATH", ""), ":");
+ foreach (Strings::iterator, i, paths) addToSearchPath(*i);
+ searchPathInsertionPoint = searchPath.begin();
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index e900217fa..1583665ba 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -213,11 +213,16 @@ private:
std::map parseTrees;
+ Paths searchPath;
+ Paths::iterator searchPathInsertionPoint;
+
public:
EvalState();
~EvalState();
+ void addToSearchPath(const string & s);
+
/* Parse a Nix expression from the specified file. If `path'
refers to a directory, then "/default.nix" is appended. */
Expr * parseExprFromFile(Path path);
@@ -229,6 +234,9 @@ public:
form. */
void evalFile(const Path & path, Value & v);
+ /* Look up a file in the search path. */
+ Path findFile(const string & path);
+
/* Evaluate an expression to normal form, storing the result in
value `v'. */
void eval(Expr * e, Value & v);
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 330c2bd54..d46b66d9f 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -81,6 +81,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
ID [a-zA-Z\_][a-zA-Z0-9\_\']*
INT [0-9]+
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
+SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+
@@ -153,6 +154,7 @@ or { return OR_KW; }
. return yytext[0]; /* just in case: shouldn't be reached */
{PATH} { yylval->path = strdup(yytext); return PATH; }
+{SPATH} { yylval->path = strdup(yytext); return SPATH; }
{URI} { yylval->uri = strdup(yytext); return URI; }
[ \t\r\n]+ /* eat up whitespace */
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index a64d327b4..cd63666dc 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -17,19 +17,22 @@
#include "util.hh"
#include "nixexpr.hh"
+#include "eval.hh"
namespace nix {
struct ParseData
{
+ EvalState & state;
SymbolTable & symbols;
Expr * result;
Path basePath;
Path path;
string error;
Symbol sLetBody;
- ParseData(SymbolTable & symbols)
- : symbols(symbols)
+ ParseData(EvalState & state)
+ : state(state)
+ , symbols(state.symbols)
, sLetBody(symbols.create(""))
{ };
};
@@ -253,7 +256,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%token ID ATTRPATH
%token STR IND_STR
%token INT
-%token PATH
+%token PATH SPATH
%token URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
%token DOLLAR_CURLY /* == ${ */
@@ -350,6 +353,20 @@ expr_simple
$$ = stripIndentation(data->symbols, *$2);
}
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
+ | SPATH {
+ string path($1 + 1, strlen($1) - 2);
+ Path path2 = data->state.findFile(path);
+ /* The file wasn't found in the search path. However, we can't
+ throw an error here, because the expression might never be
+ evaluated. So return an expression that lazily calls
+ ‘abort’. */
+ $$ = path2 == ""
+ ? (Expr * ) new ExprApp(
+ new ExprVar(data->symbols.create("throw")),
+ new ExprString(data->symbols.create(
+ (format("file `%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)") % path).str())))
+ : (Expr * ) new ExprPath(path2);
+ }
| URI { $$ = new ExprString(data->symbols.create($1)); }
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
@@ -454,7 +471,7 @@ Expr * EvalState::parse(const char * text,
const Path & path, const Path & basePath)
{
yyscan_t scanner;
- ParseData data(symbols);
+ ParseData data(*this);
data.basePath = basePath;
data.path = path;
@@ -510,5 +527,25 @@ Expr * EvalState::parseExprFromString(const string & s, const Path & basePath)
return parse(s.c_str(), "(string)", basePath);
}
-
+
+void EvalState::addToSearchPath(const string & s)
+{
+ Path path = absPath(s);
+ if (pathExists(path)) {
+ debug(format("adding path `%1%' to the search path") % path);
+ searchPath.insert(searchPathInsertionPoint, path);
+ }
+}
+
+
+Path EvalState::findFile(const string & path)
+{
+ foreach (Paths::iterator, i, searchPath) {
+ Path res = *i + "/" + path;
+ if (pathExists(res)) return canonPath(res);
+ }
+ return "";
+}
+
+
}
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 4ea301def..731f91bba 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -1253,6 +1253,8 @@ void run(Strings args)
else if (parseOptionArg(arg, i, args.end(),
globals.state, globals.instSource.autoArgs))
;
+ else if (parseSearchPathArg(arg, i, args.end(), globals.state))
+ ;
else if (arg == "--force-name") // undocumented flag for nix-install-package
globals.forceName = needArg(i, args, arg);
else if (arg == "--uninstall" || arg == "-e")
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index 1f9059539..05b9d5479 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -107,6 +107,8 @@ void run(Strings args)
}
else if (parseOptionArg(arg, i, args.end(), state, autoArgs))
;
+ else if (parseSearchPathArg(arg, i, args.end(), state))
+ ;
else if (arg == "--add-root") {
if (i == args.end())
throw UsageError("`--add-root' requires an argument");
diff --git a/tests/lang.sh b/tests/lang.sh
index fab8c6e0d..11267a23f 100644
--- a/tests/lang.sh
+++ b/tests/lang.sh
@@ -40,7 +40,7 @@ for i in lang/eval-okay-*.nix; do
if test -e lang/$i.flags; then
flags=$(cat lang/$i.flags)
fi
- if ! $nixinstantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then
+ if ! NIX_PATH=lang/dir3:lang/dir4 $nixinstantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then
echo "FAIL: $i should evaluate"
fail=1
elif ! diff lang/$i.out lang/$i.exp; then
diff --git a/tests/lang/dir1/a.nix b/tests/lang/dir1/a.nix
new file mode 100644
index 000000000..231f150c5
--- /dev/null
+++ b/tests/lang/dir1/a.nix
@@ -0,0 +1 @@
+"a"
diff --git a/tests/lang/dir2/a.nix b/tests/lang/dir2/a.nix
new file mode 100644
index 000000000..170df520a
--- /dev/null
+++ b/tests/lang/dir2/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/dir2/b.nix b/tests/lang/dir2/b.nix
new file mode 100644
index 000000000..19010cc35
--- /dev/null
+++ b/tests/lang/dir2/b.nix
@@ -0,0 +1 @@
+"b"
diff --git a/tests/lang/dir3/a.nix b/tests/lang/dir3/a.nix
new file mode 100644
index 000000000..170df520a
--- /dev/null
+++ b/tests/lang/dir3/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/dir3/b.nix b/tests/lang/dir3/b.nix
new file mode 100644
index 000000000..170df520a
--- /dev/null
+++ b/tests/lang/dir3/b.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/dir3/c.nix b/tests/lang/dir3/c.nix
new file mode 100644
index 000000000..cdf158597
--- /dev/null
+++ b/tests/lang/dir3/c.nix
@@ -0,0 +1 @@
+"c"
diff --git a/tests/lang/dir4/a.nix b/tests/lang/dir4/a.nix
new file mode 100644
index 000000000..170df520a
--- /dev/null
+++ b/tests/lang/dir4/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/dir4/c.nix b/tests/lang/dir4/c.nix
new file mode 100644
index 000000000..170df520a
--- /dev/null
+++ b/tests/lang/dir4/c.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/eval-okay-search-path.exp b/tests/lang/eval-okay-search-path.exp
new file mode 100644
index 000000000..d1cc1b4e5
--- /dev/null
+++ b/tests/lang/eval-okay-search-path.exp
@@ -0,0 +1 @@
+"abc"
diff --git a/tests/lang/eval-okay-search-path.flags b/tests/lang/eval-okay-search-path.flags
new file mode 100644
index 000000000..d7feb29e1
--- /dev/null
+++ b/tests/lang/eval-okay-search-path.flags
@@ -0,0 +1 @@
+-I lang/dir1 -I lang/dir2
\ No newline at end of file
diff --git a/tests/lang/eval-okay-search-path.nix b/tests/lang/eval-okay-search-path.nix
new file mode 100644
index 000000000..cc1df08f0
--- /dev/null
+++ b/tests/lang/eval-okay-search-path.nix
@@ -0,0 +1,3 @@
+import + import + import
+
+
diff --git a/tests/lang/eval-okay-search-path.nix~ b/tests/lang/eval-okay-search-path.nix~
new file mode 100644
index 000000000..da52a6d39
--- /dev/null
+++ b/tests/lang/eval-okay-search-path.nix~
@@ -0,0 +1 @@
+(import )
\ No newline at end of file
diff --git a/tests/lang/eval-okay-search-path.out b/tests/lang/eval-okay-search-path.out
new file mode 100644
index 000000000..d1cc1b4e5
--- /dev/null
+++ b/tests/lang/eval-okay-search-path.out
@@ -0,0 +1 @@
+"abc"