diff options
author | Eelco Dolstra <e.dolstra@tudelft.nl> | 2011-08-06T16·05+0000 |
---|---|---|
committer | Eelco Dolstra <e.dolstra@tudelft.nl> | 2011-08-06T16·05+0000 |
commit | 1ecc97b6bdb27e56d832ca48cdafd3dbb5185a04 (patch) | |
tree | 4de27ee42f04bb50766f33a58d830677bd8fa80b | |
parent | 54945a2950174ded83d58336061b4a9990cdbbfd (diff) |
* Add a Nix expression search path feature. Paths between angle
brackets, e.g. import <nixpkgs/pkgs/lib> are resolved by looking them up relative to the elements listed in the search path. This allows us to get rid of hacks like import "${builtins.getEnv "NIXPKGS_ALL"}/pkgs/lib" The search path can be specified through the ‘-I’ command-line flag and through the colon-separated ‘NIX_PATH’ environment variable, e.g., $ nix-build -I /etc/nixos ... If a file is not found in the search path, an error message is lazily thrown.
-rw-r--r-- | doc/manual/release-notes.xml | 4 | ||||
-rw-r--r-- | scripts/nix-build.in | 4 | ||||
-rw-r--r-- | src/libexpr/common-opts.cc | 12 | ||||
-rw-r--r-- | src/libexpr/common-opts.hh | 3 | ||||
-rw-r--r-- | src/libexpr/eval.cc | 6 | ||||
-rw-r--r-- | src/libexpr/eval.hh | 8 | ||||
-rw-r--r-- | src/libexpr/lexer.l | 2 | ||||
-rw-r--r-- | src/libexpr/parser.y | 47 | ||||
-rw-r--r-- | src/nix-env/nix-env.cc | 2 | ||||
-rw-r--r-- | src/nix-instantiate/nix-instantiate.cc | 2 | ||||
-rw-r--r-- | tests/lang.sh | 2 | ||||
-rw-r--r-- | tests/lang/dir1/a.nix | 1 | ||||
-rw-r--r-- | tests/lang/dir2/a.nix | 1 | ||||
-rw-r--r-- | tests/lang/dir2/b.nix | 1 | ||||
-rw-r--r-- | tests/lang/dir3/a.nix | 1 | ||||
-rw-r--r-- | tests/lang/dir3/b.nix | 1 | ||||
-rw-r--r-- | tests/lang/dir3/c.nix | 1 | ||||
-rw-r--r-- | tests/lang/dir4/a.nix | 1 | ||||
-rw-r--r-- | tests/lang/dir4/c.nix | 1 | ||||
-rw-r--r-- | tests/lang/eval-okay-search-path.exp | 1 | ||||
-rw-r--r-- | tests/lang/eval-okay-search-path.flags | 1 | ||||
-rw-r--r-- | tests/lang/eval-okay-search-path.nix | 3 | ||||
-rw-r--r-- | tests/lang/eval-okay-search-path.nix~ | 1 | ||||
-rw-r--r-- | tests/lang/eval-okay-search-path.out | 1 |
24 files changed, 98 insertions, 9 deletions
diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml index cf025aaf51b5..7d0bfe7fdc60 100644 --- a/doc/manual/release-notes.xml +++ b/doc/manual/release-notes.xml @@ -36,6 +36,10 @@ <para>TODO: “or” keyword.</para> </listitem> + <listitem> + <para>TODO: Nix expression search path (<literal>import <foo/bar.nix></literal>).</para> + </listitem> + </itemizedlist> </section> diff --git a/scripts/nix-build.in b/scripts/nix-build.in index f9d81b36c7a0..d9d1da73b12f 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 bab31f4935db..d029d2ec35b9 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 80298ce55d1b..6b7247fc3d89 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 5701452f94f1..674fa96f0bc0 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 e900217fa457..1583665bad19 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -213,11 +213,16 @@ private: std::map<Path, Expr *> 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 330c2bd54d3a..d46b66d9ff7c 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; } <IND_STRING>. 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 a64d327b454b..cd63666dc5f0 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("<let-body>")) { }; }; @@ -253,7 +256,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err %token <id> ID ATTRPATH %token <e> STR IND_STR %token <n> INT -%token <path> PATH +%token <path> PATH SPATH %token <uri> 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 4ea301def077..731f91bba058 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 1f9059539188..05b9d54797e4 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 fab8c6e0d7f9..11267a23fd19 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 000000000000..231f150c579c --- /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 000000000000..170df520ab68 --- /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 000000000000..19010cc35ca6 --- /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 000000000000..170df520ab68 --- /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 000000000000..170df520ab68 --- /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 000000000000..cdf158597eef --- /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 000000000000..170df520ab68 --- /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 000000000000..170df520ab68 --- /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 000000000000..d1cc1b4e5215 --- /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 000000000000..d7feb29e121e --- /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 000000000000..cc1df08f0185 --- /dev/null +++ b/tests/lang/eval-okay-search-path.nix @@ -0,0 +1,3 @@ +import <a.nix> + import <b.nix> + import <c.nix> + + diff --git a/tests/lang/eval-okay-search-path.nix~ b/tests/lang/eval-okay-search-path.nix~ new file mode 100644 index 000000000000..da52a6d398ed --- /dev/null +++ b/tests/lang/eval-okay-search-path.nix~ @@ -0,0 +1 @@ +(import <a.nix>) \ 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 000000000000..d1cc1b4e5215 --- /dev/null +++ b/tests/lang/eval-okay-search-path.out @@ -0,0 +1 @@ +"abc" |