Skip to content
This repository was archived by the owner on Oct 25, 2023. It is now read-only.

Commit 99f96f3

Browse files
committed
feat: link libraries in a path and platform independent way
1 parent ff1f2c6 commit 99f96f3

File tree

5 files changed

+65
-34
lines changed

5 files changed

+65
-34
lines changed

Diff for: Lake/Build/Actions.lean

+14-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ def createParentDirs (path : FilePath) : IO PUnit := do
1212
if let some dir := path.parent then IO.FS.createDirAll dir
1313

1414
def proc (args : IO.Process.SpawnArgs) : BuildM PUnit := do
15-
let envStr := String.join <| args.env.toList.map fun (k, v) => s!"{k}={v.getD ""} "
15+
let envStr := String.join <| args.env.toList.filterMap fun (k, v) =>
16+
if k == "PATH" then none else some s!"{k}={v.getD ""} " -- PATH too big
1617
let cmdStr := " ".intercalate (args.cmd :: args.args.toList)
1718
logInfo <| "> " ++ envStr ++
1819
match args.cwd with
@@ -31,11 +32,16 @@ def proc (args : IO.Process.SpawnArgs) : BuildM PUnit := do
3132
logError s!"external command {args.cmd} exited with status {out.exitCode}"
3233
failure
3334

35+
def getSearchPath (envVar : String) : IO SearchPath := do
36+
match (← IO.getEnv envVar) with
37+
| some path => pure <| SearchPath.parse path
38+
| none => pure []
39+
3440
def compileLeanModule (leanFile : FilePath)
3541
(oleanFile? ileanFile? cFile? : Option FilePath)
3642
(oleanPath : SearchPath := []) (rootDir : FilePath := ".")
37-
(dynlibs : Array FilePath := #[]) (leanArgs : Array String := #[])
38-
(lean : FilePath := "lean")
43+
(dynlibs : Array FilePath := #[]) (dynlibPath : SearchPath := {})
44+
(leanArgs : Array String := #[]) (lean : FilePath := "lean")
3945
: BuildM PUnit := do
4046
let mut args := leanArgs ++
4147
#[leanFile.toString, "-R", rootDir.toString]
@@ -53,7 +59,11 @@ def compileLeanModule (leanFile : FilePath)
5359
proc {
5460
args
5561
cmd := lean.toString
56-
env := #[("LEAN_PATH", oleanPath.toString)]
62+
env := #[
63+
("LEAN_PATH", oleanPath.toString),
64+
("PATH", (← getSearchPath "PATH") ++ dynlibPath |>.toString), -- Windows
65+
("LD_LIBRARY_PATH", (← getSearchPath "LD_LIBRARY_PATH") ++ dynlibPath |>.toString) -- Unix
66+
]
5767
}
5868

5969
def compileO (oFile srcFile : FilePath)

Diff for: Lake/Build/Module1.lean

+36-18
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ namespace Lake
1414

1515
-- # Solo Module Targets
1616

17-
def Module.soloTarget (mod : Module)
18-
(dynlibs : Array FilePath) (depTarget : BuildTarget x) (c := true) : OpaqueTarget :=
17+
def Module.soloTarget (mod : Module) (dynlibs : Array FilePath)
18+
(dynlibPath : SearchPath) (depTarget : BuildTarget x) (c := true) : OpaqueTarget :=
1919
Target.opaque <| depTarget.bindOpaqueSync fun depTrace => do
2020
let argTrace : BuildTrace := pureHash mod.leanArgs
2121
let srcTrace : BuildTrace ← computeTrace mod.leanFile
@@ -25,12 +25,12 @@ def Module.soloTarget (mod : Module)
2525
let cUpToDate ← modTrace.checkAgainstFile mod.cFile mod.cTraceFile
2626
unless modUpToDate && cUpToDate do
2727
compileLeanModule mod.leanFile mod.oleanFile mod.ileanFile mod.cFile
28-
(← getOleanPath) mod.pkg.rootDir dynlibs mod.leanArgs (← getLean)
28+
(← getOleanPath) mod.pkg.rootDir dynlibs dynlibPath mod.leanArgs (← getLean)
2929
modTrace.writeToFile mod.cTraceFile
3030
else
3131
unless modUpToDate do
3232
compileLeanModule mod.leanFile mod.oleanFile mod.ileanFile none
33-
(← getOleanPath) mod.pkg.rootDir dynlibs mod.leanArgs (← getLean)
33+
(← getOleanPath) mod.pkg.rootDir dynlibs dynlibPath mod.leanArgs (← getLean)
3434
modTrace.writeToFile mod.traceFile
3535
return depTrace
3636

@@ -43,8 +43,15 @@ def Module.mkOTarget (cTarget : FileTarget) (self : Module) : FileTarget :=
4343
leanOFileTarget self.oFile cTarget self.leancArgs
4444

4545
@[inline]
46-
def Module.mkDynlibTarget (linkTargets : Array FileTarget) (self : Module) : FileTarget :=
47-
leanSharedLibTarget self.dynlib linkTargets self.linkArgs
46+
def Module.mkDynlibTarget (self : Module) (oTarget : FileTarget)
47+
(libDirs : Array FilePath) (libTargets : Array FileTarget) : FileTarget :=
48+
let libsTarget : BuildTarget _ := Target.collectArray libTargets
49+
Target.mk self.dynlib do
50+
oTarget.bindAsync fun oFile oTrace => do
51+
libsTarget.bindSync fun libFiles libTrace => do
52+
buildFileUnlessUpToDate self.dynlibFile (oTrace.mix libTrace) do
53+
let args := #[oFile.toString] ++ libDirs.map (s!"-L{·}") ++ libFiles.map (s!"-l:{·}")
54+
compileSharedLib self.dynlibFile args (← getLeanc)
4855

4956
-- # Recursive Building
5057

@@ -53,21 +60,33 @@ variable [Monad m] [MonadLiftT BuildM m] [MonadBuildStore m]
5360

5461
/-- Build the dynlibs of the imports that want precompilation. -/
5562
@[specialize] def recBuildPrecompileDynlibs (pkg : Package) (imports : Array Module)
56-
: IndexT m (Array ActiveFileTarget × Array ActiveFileTarget) := do
63+
: IndexT m (Array ActiveFileTarget × Array ActiveFileTarget × Array FilePath) := do
64+
have : MonadLift BuildM m := ⟨liftM⟩
65+
-- Build and collect precompiled imports
5766
let mut pkgs := #[]
58-
let mut pkgSet := PackageSet.empty
67+
let mut pkgSet := PackageSet.empty.insert pkg
5968
let mut modTargets := #[]
6069
for imp in imports do
6170
if imp.shouldPrecompile then
6271
unless pkgSet.contains imp.pkg do
6372
pkgSet := pkgSet.insert imp.pkg
6473
pkgs := pkgs.push imp.pkg
6574
modTargets := modTargets.push <| ← imp.recBuildFacet &`lean.dynlib
75+
pkgs := pkgs.push pkg
76+
-- Compute library directories and external library targets
77+
let mut libDirs := #[]
6678
let mut pkgTargets := #[]
6779
for pkg in pkgs do
68-
pkgTargets := pkgTargets.append <| ← pkg.recBuildFacet &`externSharedLibs
69-
pkgTargets := pkgTargets.append <| ← pkg.recBuildFacet &`externSharedLibs
70-
return (modTargets, pkgTargets)
80+
libDirs := libDirs.push pkg.libDir
81+
let externLibTargets ← pkg.recBuildFacet &`externSharedLibs
82+
for target in externLibTargets do
83+
if let some parent := target.info.parent then
84+
libDirs := libDirs.push parent
85+
if let some fileName := target.info.fileName then
86+
pkgTargets := pkgTargets.push <| target.withInfo fileName
87+
else
88+
logWarning s!"external library `{target.info}` was skipped because it has no file name"
89+
return (modTargets, pkgTargets, libDirs)
7190

7291
/--
7392
Recursively build a module and its (transitive, local) imports,
@@ -78,7 +97,7 @@ optionally outputting a `.c` file as well if `c` is set to `true`.
7897
have : MonadLift BuildM m := ⟨liftM⟩
7998
let extraDepTarget ← mod.pkg.recBuildFacet &`extraDep
8099
let (imports, transImports) ← mod.recBuildFacet &`lean.imports
81-
let (modTargets, pkgTargets) ← recBuildPrecompileDynlibs mod.pkg transImports
100+
let (modTargets, pkgTargets, libDirs) ← recBuildPrecompileDynlibs mod.pkg transImports
82101
-- Note: Lean wants the external library symbols before module symbols
83102
let dynlibsTarget ← ActiveTarget.collectArray <| pkgTargets ++ modTargets
84103
let importTarget ←
@@ -90,7 +109,7 @@ optionally outputting a `.c` file as well if `c` is set to `true`.
90109
<| ← imports.mapM (·.recBuildFacet &`lean)
91110
let depTarget := Target.active <| ← extraDepTarget.mixOpaqueAsync
92111
<| ← dynlibsTarget.mixOpaqueAsync importTarget
93-
let modTarget ← mod.soloTarget dynlibsTarget.info depTarget c |>.activate
112+
let modTarget ← mod.soloTarget dynlibsTarget.info libDirs.toList depTarget c |>.activate
94113
store (mod.mkBuildKey &`lean) modTarget
95114
store (mod.mkBuildKey &`olean) <| modTarget.withInfo mod.oleanFile
96115
store (mod.mkBuildKey &`ilean) <| modTarget.withInfo mod.ileanFile
@@ -132,9 +151,8 @@ Recursively build the shared library of a module (e.g., for `--load-dynlib`).
132151
@[specialize] def Module.recBuildDynLib (mod : Module)
133152
: IndexT m ActiveFileTarget := do
134153
have : MonadLift BuildM m := ⟨liftM⟩
135-
let oTarget ← mod.recBuildFacet &`lean.o
154+
let oTarget := Target.active <| ← mod.recBuildFacet &`lean.o
136155
let (_, transImports) ← mod.recBuildFacet &`lean.imports
137-
let (modTargets, pkgTargets) ← recBuildPrecompileDynlibs mod.pkg transImports
138-
let linkTargets := #[oTarget] ++ modTargets ++ pkgTargets
139-
let linkTargets := linkTargets.map Target.active
140-
mod.mkDynlibTarget linkTargets |>.activate
156+
let (modTargets, pkgTargets, libDirs) ← recBuildPrecompileDynlibs mod.pkg transImports
157+
let libTargets := modTargets ++ pkgTargets |>.map Target.active
158+
mod.mkDynlibTarget oTarget libDirs libTargets |>.activate

Diff for: Lake/Config/Module.lean

+13-9
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,33 @@ def Package.findModule? (mod : Name) (self : Package) : Option Module :=
2929

3030
namespace Module
3131

32-
def leanFile (self : Module) : FilePath :=
32+
@[inline] def leanFile (self : Module) : FilePath :=
3333
Lean.modToFilePath self.pkg.srcDir self.name "lean"
3434

35-
def oleanFile (self : Module) : FilePath :=
35+
@[inline] def oleanFile (self : Module) : FilePath :=
3636
Lean.modToFilePath self.pkg.oleanDir self.name "olean"
3737

38-
def ileanFile (self : Module) : FilePath :=
38+
@[inline] def ileanFile (self : Module) : FilePath :=
3939
Lean.modToFilePath self.pkg.oleanDir self.name "ilean"
4040

41-
def traceFile (self : Module) : FilePath :=
41+
@[inline] def traceFile (self : Module) : FilePath :=
4242
Lean.modToFilePath self.pkg.oleanDir self.name "trace"
4343

44-
def cFile (self : Module) : FilePath :=
44+
@[inline] def cFile (self : Module) : FilePath :=
4545
Lean.modToFilePath self.pkg.irDir self.name "c"
4646

47-
def cTraceFile (self : Module) : FilePath :=
47+
@[inline] def cTraceFile (self : Module) : FilePath :=
4848
Lean.modToFilePath self.pkg.irDir self.name "c.trace"
4949

50-
def oFile (self : Module) : FilePath :=
50+
@[inline] def oFile (self : Module) : FilePath :=
5151
Lean.modToFilePath self.pkg.irDir self.name "o"
5252

53-
def dynlib (self : Module) : FilePath :=
54-
Lean.modToFilePath self.pkg.oleanDir self.name sharedLibExt
53+
@[inline] def dynlib (self : Module) : FilePath :=
54+
-- NOTE: file name MUST be unique on Windows
55+
s!"{self.name}.{sharedLibExt}"
56+
57+
@[inline] def dynlibFile (self : Module) : FilePath :=
58+
self.pkg.libDir / self.dynlib
5559

5660
@[inline] def leanArgs (self : Module) : Array String :=
5761
self.pkg.moreLeanArgs

Diff for: examples/ffi/lib/lakefile.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ def ffiOTarget : FileTarget :=
2323
compileO oFile srcFile #["-I", (← getLeanIncludeDir).toString] "c++"
2424

2525
extern_lib cLib :=
26-
let libFile := cBuildDir / s!"libffi.a"
26+
let libFile := cBuildDir / s!"libleanffi.a"
2727
staticLibTarget libFile #[ffiOTarget]

Diff for: examples/ffi/test.sh

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@ set -e
55
./app/build/bin/app
66
./lib/build/bin/test
77

8-
cd app # Library loading needs this; TODO: fix this
9-
${LAKE:-../../../build/bin/lake} build Test
8+
${LAKE:-../../build/bin/lake} -d app build Test

0 commit comments

Comments
 (0)