Skip to content

Commit 8a536d0

Browse files
authored
feat: lake env w/o configuration + more (#2428)
* feat: `lake env` w/o configuration + more * chore: `lake env printenv DYLD_LIBRARY_PATH` illegal on MacOS
1 parent 898cd0b commit 8a536d0

File tree

6 files changed

+193
-60
lines changed

6 files changed

+193
-60
lines changed

src/lake/Lake/CLI/Help.lean

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,18 @@ OPTIONS:
2626
--update, -U update manifest before building
2727
2828
COMMANDS:
29-
new <name> [<temp>] create a Lean package in a new directory
30-
init <name> [<temp>] create a Lean package in the current directory
31-
build [<targets>...] build targets
29+
new <name> <temp> create a Lean package in a new directory
30+
init <name> <temp> create a Lean package in the current directory
31+
build <targets>... build targets
3232
update update dependencies and save them to the manifest
3333
upload <tag> upload build artifacts to a GitHub release
3434
clean remove build outputs
3535
script manage and run workspace scripts
3636
scripts shorthand for `lake script list`
3737
run <script> shorthand for `lake script run`
3838
serve start the Lean language server
39-
env <cmd> [<args>...] execute a command in the workspace's environment
40-
exe <exe> [<args>...] build an exe and run it in the workspace's environment
39+
env <cmd> <args>... execute a command in Lake's environment
40+
exe <exe> <args>... build an exe and run it in Lake's environment
4141
4242
See `lake help <command>` for more information on a specific command."
4343

@@ -97,7 +97,7 @@ TARGET EXAMPLES: build the ...
9797
a/+A:c C file of module `A` of package `a`
9898
:foo facet `foo` of the root package
9999
100-
A bare `build` command will build the default facet of the root package.
100+
A bare `lake build` command will build the default facet of the root package.
101101
Package dependencies are not updated during a build."
102102

103103
def helpUpdate :=
@@ -161,12 +161,12 @@ def helpScriptRun :=
161161
"Run a script
162162
163163
USAGE:
164-
lake script run [<package>/]<script> [<args>...]
164+
lake script run [[<package>/]<script>] [<args>...]
165165
166166
This command runs the given `script` from `package`, passing `args` to it.
167167
Defaults to the root package.
168168
169-
A bare `run` command will run the default script(s) of the root package
169+
A bare `lake run` command will run the default script(s) of the root package
170170
(with no arguments)."
171171

172172
def helpScriptDoc :=
@@ -188,38 +188,42 @@ with the package configuration's `moreServerArgs` field and `args`.
188188
"
189189

190190
def helpEnv :=
191-
"Execute a command in the workspace's environment
191+
"Execute a command in Lake's environment
192192
193193
USAGE:
194-
lake env <cmd> [<args>...]
194+
lake env [<cmd>] [<args>...]
195195
196196
Spawns a new process executing `cmd` with the given `args` and with
197-
the environment set based on the workspace configuration and the detected
198-
Lean/Lake installations.
197+
the environment set based on the detected Lean/Lake installations and
198+
the workspace configuration (if it exists).
199199
200200
Specifically, this command sets the following environment variables:
201201
202202
LAKE set to the detected Lake executable
203203
LAKE_HOME set to the detected Lake home
204-
LEAN_SYSROOT set to the detected Lean sysroot
204+
LEAN_SYSROOT set to the detected Lean toolchain directory
205205
LEAN_AR set to the detected Lean `ar` binary
206-
LEAN_CC set to the detected `cc` (if not using bundled one)
207-
LEAN_PATH adds the workspace's library directories
208-
LEAN_SRC_PATH adds the workspace's source directories
209-
PATH adds the workspace's library directories (Windows)
210-
DYLD_LIBRARY_PATH adds the workspace's library directories (MacOS)
211-
LD_LIBRARY_PATH adds the workspace's library directories (other Unix)"
206+
LEAN_CC set to the detected `cc` (if not using the bundled one)
207+
LEAN_PATH adds Lake's and the workspace's Lean library dirs
208+
LEAN_SRC_PATH adds Lake's and the workspace's source dirs
209+
PATH adds Lean's, Lake's, and the workspace's binary dirs
210+
PATH adds Lean's and the workspace's library dirs (Windows)
211+
DYLD_LIBRARY_PATH adds Lean's and the workspace's library dirs (MacOS)
212+
LD_LIBRARY_PATH adds Lean's and the workspace's library dirs (other)
213+
214+
A bare `lake env` will print out the variables set and their values,
215+
using the form NAME=VALUE like the POSIX `env` command."
212216

213217
def helpExe :=
214-
"Build an executable target and run it in the workspace's environment
218+
"Build an executable target and run it in Lake's environment
215219
216220
USAGE:
217221
lake exe <exe-target> [<args>...]
218222
219223
Looks for the executable target in the workspace (see `lake help build` to
220224
learn how to specify targets), builds it if it is out of date, and then runs
221-
it with the given `args` in the workspace's environment (see `lake help env`
222-
for how the environment is set)."
225+
it with the given `args` in Lake's environment (see `lake help env` for how
226+
the environment is set up)."
223227

224228
def helpScript : (cmd : String) → String
225229
| "list" => helpScriptList

src/lake/Lake/CLI/Main.lean

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,11 +328,19 @@ protected def serve : CliM PUnit := do
328328
noArgsRem do exit <| ← serve config args
329329

330330
protected def env : CliM PUnit := do
331-
let cmd ← takeArg "command"; let args ← takeArgs
332331
let config ← mkLoadConfig (← getThe LakeOptions)
333-
let ws ← loadWorkspace config
334-
let ctx := mkLakeContext ws
335-
exit <| ← (env cmd args.toArray).run ctx
332+
let env ← do
333+
if (← config.configFile.pathExists) then
334+
pure (← loadWorkspace config).augmentedEnvVars
335+
else
336+
pure config.env.vars
337+
if let some cmd ← takeArg? then
338+
let child ← IO.Process.spawn {cmd, args := (← takeArgs).toArray, env}
339+
exit <| ← child.wait
340+
else
341+
env.forM fun (var, val?) =>
342+
IO.println s!"{var}={val?.getD ""}"
343+
exit 0
336344

337345
protected def exe : CliM PUnit := do
338346
let exeName ← takeArg "executable name"

src/lake/Lake/Config/Env.lean

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,82 @@ import Lake.Config.InstallPath
88

99
open System
1010

11+
/-! # Lake's Environment
12+
Definitions related to a Lake environment.
13+
A Lake environment is computed on Lake's startup from
14+
user-specified CLI options and the process environment.
15+
-/
16+
1117
namespace Lake
1218

13-
/-- The detected Lake environment. -/
19+
/-- A Lake environment. -/
1420
structure Env where
21+
/-- The Lake installation of the environment. -/
1522
lake : LakeInstall
23+
/-- The Lean installation of the environment. -/
1624
lean : LeanInstall
17-
leanPath : SearchPath
18-
leanSrcPath : SearchPath
19-
sharedLibPath : SearchPath
25+
/-- The initial Lean library search path of the environment (i.e., `LEAN_PATH`). -/
26+
initLeanPath : SearchPath
27+
/-- The initial Lean source search path of the environment (i.e., `LEAN_SRC_PATH`). -/
28+
initLeanSrcPath : SearchPath
29+
/-- The initial shared library search path of the environment. -/
30+
initSharedLibPath : SearchPath
31+
/-- The initial binary search path of the environment (i.e., `PATH`). -/
32+
initPath : SearchPath
2033
deriving Inhabited, Repr
2134

2235
namespace Env
2336

2437
/-- Compute an `Lake.Env` object from the given installs and set environment variables. -/
2538
def compute (lake : LakeInstall) (lean : LeanInstall) : BaseIO Env :=
2639
return {
27-
lake, lean
28-
leanPath := ← getSearchPath "LEAN_PATH",
29-
leanSrcPath := ← getSearchPath "LEAN_SRC_PATH",
30-
sharedLibPath := ← getSearchPath sharedLibPathEnvVar
40+
lake, lean,
41+
initLeanPath := ← getSearchPath "LEAN_PATH",
42+
initLeanSrcPath := ← getSearchPath "LEAN_SRC_PATH",
43+
initSharedLibPath := ← getSearchPath sharedLibPathEnvVar,
44+
initPath := ← getSearchPath "PATH"
3145
}
3246

33-
/-- Environment variable settings based only on the given Lean and Lake installations. -/
47+
/--
48+
The Lean library search path of the environment (i.e., `LEAN_PATH`).
49+
Combines the initial path of the environment with that of the Lake installation.
50+
-/
51+
def path (env : Env) : SearchPath :=
52+
if env.lake.binDir = env.lean.binDir then
53+
env.lean.binDir :: env.initPath
54+
else
55+
env.lake.binDir :: env.lean.binDir :: env.initPath
56+
57+
/-
58+
We include Lake's installation in the cases below to ensure that the
59+
Lake being used to build is available to the environment (and thus, e.g.,
60+
the Lean server). Otherwise, it may fall back on whatever the default Lake
61+
instance is.
62+
-/
63+
64+
/--
65+
The Lean library search path of the environment (i.e., `LEAN_PATH`).
66+
Combines the initial path of the environment with that of the Lake installation.
67+
-/
68+
def leanPath (env : Env) : SearchPath :=
69+
env.lake.libDir :: env.initLeanPath
70+
71+
/--
72+
The Lean source search path of the environment (i.e., `LEAN_SRC_PATH`).
73+
Combines the initial path of the environment with that of the Lake abd Lean
74+
installations.
75+
-/
76+
def leanSrcPath (env : Env) : SearchPath :=
77+
env.lake.srcDir :: env.initLeanSrcPath
78+
79+
/--
80+
The shared library search path of the environment.
81+
Combines the initial path of the environment with that of the Lean installation.
82+
-/
83+
def sharedLibPath (env : Env) : SearchPath :=
84+
env.lean.sharedLibPath ++ env.initSharedLibPath
85+
86+
/-- Environment variable settings based only on the Lean and Lake installations. -/
3487
def installVars (env : Env) : Array (String × Option String) :=
3588
#[
3689
("LAKE", env.lake.lake.toString),
@@ -42,11 +95,15 @@ def installVars (env : Env) : Array (String × Option String) :=
4295

4396
/-- Environment variable settings for the `Lake.Env`. -/
4497
def vars (env : Env) : Array (String × Option String) :=
45-
env.installVars ++ #[
98+
let vars := env.installVars ++ #[
4699
("LEAN_PATH", some env.leanPath.toString),
47100
("LEAN_SRC_PATH", some env.leanSrcPath.toString),
48-
(sharedLibPathEnvVar, some env.sharedLibPath.toString)
101+
("PATH", some env.path.toString)
49102
]
103+
if Platform.isWindows then
104+
vars
105+
else
106+
vars.push (sharedLibPathEnvVar, some <| env.sharedLibPath.toString)
50107

51108
/--
52109
The default search path the Lake executable

src/lake/Lake/Config/InstallPath.lean

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ structure LeanInstall where
4141
leanLibDir := sysroot / "lib" / "lean"
4242
includeDir := sysroot / "include"
4343
systemLibDir := sysroot / "lib"
44+
binDir := sysroot / "bin"
4445
lean := leanExe sysroot
4546
leanc := leancExe sysroot
4647
sharedLib := leanSharedLib sysroot
@@ -49,6 +50,16 @@ structure LeanInstall where
4950
customCc : Bool
5051
deriving Inhabited, Repr
5152

53+
/--
54+
A `SearchPath` including the Lean installation's shared library directories
55+
(i.e., the system library and Lean library directories).
56+
-/
57+
def LeanInstall.sharedLibPath (self : LeanInstall) : SearchPath :=
58+
if Platform.isWindows then
59+
[self.binDir]
60+
else
61+
[self.leanLibDir, self.systemLibDir]
62+
5263
/-- The `LEAN_CC` of the Lean installation. -/
5364
def LeanInstall.leanCc? (self : LeanInstall) : Option String :=
5465
if self.customCc then self.cc.toString else none
@@ -61,6 +72,7 @@ def lakeExe (buildHome : FilePath) :=
6172
structure LakeInstall where
6273
home : FilePath
6374
srcDir := home
75+
binDir := home / "build" / "bin"
6476
libDir := home / "build" / "lib"
6577
lake := lakeExe <| home / "build"
6678
deriving Inhabited, Repr
@@ -218,6 +230,7 @@ def findInstall? : BaseIO (Option LeanInstall × Option LakeInstall) := do
218230
some {
219231
home,
220232
srcDir := lean.srcDir / "lake",
233+
binDir := lean.binDir,
221234
libDir := lean.leanLibDir,
222235
lake := lakeExe home
223236
}

src/lake/Lake/Config/Workspace.lean

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,21 @@ def addLibraryFacetConfig (cfg : LibraryFacetConfig name) (self : Workspace) : W
124124
@[inline] def findLibraryFacetConfig? (name : Name) (self : Workspace) : Option (LibraryFacetConfig name) :=
125125
self.libraryFacetConfigs.find? name
126126

127-
/-- The workspace's binary Lean library paths (which are added to `LEAN_PATH`). -/
127+
/-- The workspace's binary directories (which are added to `Path`). -/
128+
def binPath (self : Workspace) : SearchPath :=
129+
self.packageList.map (·.binDir)
130+
131+
/-- The workspace's Lean library directories (which are added to `LEAN_PATH`). -/
128132
def leanPath (self : Workspace) : SearchPath :=
129133
self.packageList.map (·.leanLibDir)
130134

131-
/-- The workspace's source directories (which are added to `LEAN_SRC_PATH`). -/
132-
def leanSrcPath (self : Workspace) : SearchPath :=
133-
self.packageList.map (·.srcDir)
135+
/-- The workspace's source directories (which are added to `LEAN_SRC_PATH`). -/
136+
def leanSrcPath (self : Workspace) : SearchPath := Id.run do
137+
let mut path : SearchPath := {}
138+
for pkg in self.packageArray do
139+
for (_, config) in pkg.leanLibConfigs do
140+
path := pkg.srcDir / config.srcDir :: path
141+
return path
134142

135143
/--
136144
The workspace's shared library path (e.g., for `--load-dynlib`).
@@ -140,44 +148,47 @@ def sharedLibPath (self : Workspace) : SearchPath :=
140148
self.packageList.map (·.nativeLibDir)
141149

142150
/--
143-
The detected `LEAN_PATH` of the environment
144-
augmented with the workspace's `leanPath` and Lake's `libDir`.
151+
The detected `PATH` of the environment augmented with
152+
the workspace's `binDir` and Lean and Lake installations' `binDir`.
153+
-/
154+
def augmentedPath (self : Workspace) : SearchPath :=
155+
self.binPath ++ self.lakeEnv.path
145156

146-
We include Lake's `oleanDir` at the end to ensure that same Lake package being
147-
used to build is available to the environment (and thus, e.g., the Lean server).
148-
Otherwise, it may fall back on whatever the default Lake instance is.
157+
/--
158+
The detected `LEAN_PATH` of the environment augmented with
159+
the workspace's `leanPath` and Lake's `libDir`.
149160
-/
150161
def augmentedLeanPath (self : Workspace) : SearchPath :=
151-
self.lakeEnv.leanPath ++ self.leanPath ++ [self.lakeEnv.lake.libDir]
162+
self.leanPath ++ self.lakeEnv.leanPath
152163

153164
/--
154-
The detected `LEAN_SRC_PATH` of the environment
155-
augmented with the workspace's `leanSrcPath` and Lake's `srcDir`.
156-
157-
We include Lake's `srcDir` at the end to ensure that same Lake package being
158-
used to build is available to the environment (and thus, e.g., the Lean server).
159-
Otherwise, it may fall back on whatever the default Lake instance is.
165+
The detected `LEAN_SRC_PATH` of the environment augmented with
166+
the workspace's `leanSrcPath` and Lake's `srcDir`.
160167
-/
161168
def augmentedLeanSrcPath (self : Workspace) : SearchPath :=
162-
self.lakeEnv.leanSrcPath ++ self.leanSrcPath ++ [self.lakeEnv.lake.srcDir]
169+
self.leanSrcPath ++ self.lakeEnv.leanSrcPath
163170

164171
/-
165-
The detected `sharedLibPathEnv` value of the environment
166-
augmented with the workspace's `libPath`.
172+
The detected `sharedLibPathEnv` value of the environment augmented with
173+
the workspace's `libPath` and Lean installation's shared library directories.
167174
-/
168175
def augmentedSharedLibPath (self : Workspace) : SearchPath :=
169-
self.lakeEnv.sharedLibPath ++ self.sharedLibPath
176+
self.sharedLibPath ++ self.lakeEnv.sharedLibPath
170177

171178
/--
172-
The detected environment augmented with the Workspace's paths.
179+
The detected environment augmented with Lake's and the workspace's paths.
173180
These are the settings use by `lake env` / `Lake.env` to run executables.
174181
-/
175182
def augmentedEnvVars (self : Workspace) : Array (String × Option String) :=
176-
self.lakeEnv.installVars ++ #[
183+
let vars := self.lakeEnv.installVars ++ #[
177184
("LEAN_PATH", some self.augmentedLeanPath.toString),
178185
("LEAN_SRC_PATH", some self.augmentedLeanSrcPath.toString),
179-
(sharedLibPathEnvVar, some self.augmentedSharedLibPath.toString)
186+
("PATH", some self.augmentedPath.toString)
180187
]
188+
if Platform.isWindows then
189+
vars
190+
else
191+
vars.push (sharedLibPathEnvVar, some self.augmentedSharedLibPath.toString)
181192

182193
/-- Remove all packages' build outputs (i.e., delete their build directories). -/
183194
def clean (self : Workspace) : IO Unit := do

0 commit comments

Comments
 (0)