Add primop ‘scopedImport’
‘scopedImport’ works like ‘import’, except that it takes a set of
attributes to be added to the lexical scope of the expression,
essentially extending or overriding the builtin variables.  For
instance, the expression
  scopedImport { x = 1; } ./foo.nix
where foo.nix contains ‘x’, will evaluate to 1.
This has a few applications:
* It allows getting rid of function argument specifications in package
  expressions. For instance, a package expression like:
    { stdenv, fetchurl, libfoo }:
    stdenv.mkDerivation { ... buildInputs = [ libfoo ]; }
  can now we written as just
    stdenv.mkDerivation { ... buildInputs = [ libfoo ]; }
  and imported in all-packages.nix as:
    bar = scopedImport pkgs ./bar.nix;
  So whereas we once had dependencies listed in three places
  (buildInputs, the function, and the call site), they now only need
  to appear in one place.
* It allows overriding builtin functions. For instance, to trace all
  calls to ‘map’:
  let
    overrides = {
      map = f: xs: builtins.trace "map called!" (map f xs);
      # Ensure that our override gets propagated by calls to
      # import/scopedImport.
      import = fn: scopedImport overrides fn;
      scopedImport = attrs: fn: scopedImport (overrides // attrs) fn;
      # Also update ‘builtins’.
      builtins = builtins // overrides;
    };
  in scopedImport overrides ./bla.nix
* Similarly, it allows extending the set of builtin functions. For
  instance, during Nixpkgs/NixOS evaluation, the Nixpkgs library
  functions could be added to the default scope.
There is a downside: calls to scopedImport are not memoized, unlike
import. So importing a file multiple times leads to multiple parsings
/ evaluations. It would be possible to construct the AST only once,
but that would require careful handling of variables/environments.
			
			
This commit is contained in:
		
							parent
							
								
									f0fdbd0897
								
							
						
					
					
						commit
						c273c15cb1
					
				
					 8 changed files with 50 additions and 3 deletions
				
			
		|  | @ -124,6 +124,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     /* Parse a Nix expression from the specified file. */ |     /* Parse a Nix expression from the specified file. */ | ||||||
|     Expr * parseExprFromFile(const Path & path); |     Expr * parseExprFromFile(const Path & path); | ||||||
|  |     Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv); | ||||||
| 
 | 
 | ||||||
|     /* Parse a Nix expression from the specified string. */ |     /* Parse a Nix expression from the specified string. */ | ||||||
|     Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv); |     Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv); | ||||||
|  |  | ||||||
|  | @ -336,5 +336,4 @@ struct StaticEnv | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -592,7 +592,13 @@ Path resolveExprPath(Path path) | ||||||
| 
 | 
 | ||||||
| Expr * EvalState::parseExprFromFile(const Path & path) | Expr * EvalState::parseExprFromFile(const Path & path) | ||||||
| { | { | ||||||
|     return parse(readFile(path).c_str(), path, dirOf(path), staticBaseEnv); |     return parseExprFromFile(path, staticBaseEnv); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv) | ||||||
|  | { | ||||||
|  |     return parse(readFile(path).c_str(), path, dirOf(path), staticEnv); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -93,6 +93,30 @@ static void prim_import(EvalState & state, const Pos & pos, Value * * args, Valu | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args, Value & v) | ||||||
|  | { | ||||||
|  |     PathSet context; | ||||||
|  |     state.forceAttrs(*args[0]); | ||||||
|  |     Path path = resolveExprPath(state.coerceToPath(pos, *args[1], context)); | ||||||
|  | 
 | ||||||
|  |     Env * env = &state.allocEnv(args[0]->attrs->size()); | ||||||
|  |     env->up = &state.baseEnv; | ||||||
|  | 
 | ||||||
|  |     StaticEnv staticEnv(false, &state.staticBaseEnv); | ||||||
|  | 
 | ||||||
|  |     unsigned int displ = 0; | ||||||
|  |     for (auto & attr : *args[0]->attrs) { | ||||||
|  |         staticEnv.vars[attr.name] = displ; | ||||||
|  |         env->values[displ++] = attr.value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); | ||||||
|  |     Expr * e = state.parseExprFromFile(path, staticEnv); | ||||||
|  | 
 | ||||||
|  |     e->eval(state, *env, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Return a string representing the type of the expression. */ | /* Return a string representing the type of the expression. */ | ||||||
| static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v) | static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v) | ||||||
| { | { | ||||||
|  | @ -1247,6 +1271,7 @@ void EvalState::createBaseEnv() | ||||||
| 
 | 
 | ||||||
|     // Miscellaneous
 |     // Miscellaneous
 | ||||||
|     addPrimOp("import", 1, prim_import); |     addPrimOp("import", 1, prim_import); | ||||||
|  |     addPrimOp("scopedImport", 2, prim_scopedImport); | ||||||
|     addPrimOp("__typeOf", 1, prim_typeOf); |     addPrimOp("__typeOf", 1, prim_typeOf); | ||||||
|     addPrimOp("isNull", 1, prim_isNull); |     addPrimOp("isNull", 1, prim_isNull); | ||||||
|     addPrimOp("__isFunction", 1, prim_isFunction); |     addPrimOp("__isFunction", 1, prim_isFunction); | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-okay-import.exp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-okay-import.exp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | [ 1 2 3 4 5 6 7 8 9 10 ] | ||||||
							
								
								
									
										11
									
								
								tests/lang/eval-okay-import.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tests/lang/eval-okay-import.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | let | ||||||
|  | 
 | ||||||
|  |   overrides = { | ||||||
|  |     import = fn: scopedImport overrides fn; | ||||||
|  | 
 | ||||||
|  |     scopedImport = attrs: fn: scopedImport (overrides // attrs) fn; | ||||||
|  | 
 | ||||||
|  |     builtins = builtins // overrides; | ||||||
|  |   } // import ./lib.nix; | ||||||
|  | 
 | ||||||
|  | in scopedImport overrides ./imported.nix | ||||||
							
								
								
									
										3
									
								
								tests/lang/imported.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/lang/imported.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | # The function ‘range’ comes from lib.nix and was added to the lexical | ||||||
|  | # scope by scopedImport. | ||||||
|  | range 1 5 ++ import ./imported2.nix | ||||||
							
								
								
									
										1
									
								
								tests/lang/imported2.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/imported2.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | range 6 10 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue