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

Commit

Permalink
feat: recursive builds in extern_lib
Browse files Browse the repository at this point in the history
  • Loading branch information
tydeu committed Aug 4, 2022
1 parent d7b2dcf commit 40927d8
Show file tree
Hide file tree
Showing 12 changed files with 101 additions and 65 deletions.
8 changes: 7 additions & 1 deletion Lake/Build/Index.lean
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ dynamically typed equivalent.
[h : FamilyDef TargetData facet α] : IndexBuildM (TargetData facet) :=
cast (by rw [← h.family_key_eq_type]) build

def ExternLib.recBuildStatic (lib : ExternLib) : IndexBuildM (BuildJob FilePath) := do
if let some target := lib.pkg.findTargetConfig? (.str lib.name "static") then
lib.config.getJob <$> (target.build lib.pkg)
else
error "missing target for external library"

def ExternLib.recBuildShared (lib : ExternLib) : IndexBuildM (BuildJob FilePath) := do
buildLeanSharedLibOfStatic (← lib.static.recBuild) lib.linkArgs

Expand Down Expand Up @@ -61,7 +67,7 @@ def recBuildWithIndex : (info : BuildInfo) → IndexBuildM (BuildData info.key)
| .leanExe exe =>
mkTargetFacetBuild LeanExe.exeFacet exe.recBuildExe
| .staticExternLib lib =>
mkTargetFacetBuild ExternLib.staticFacet lib.build
mkTargetFacetBuild ExternLib.staticFacet lib.recBuildStatic
| .sharedExternLib lib =>
mkTargetFacetBuild ExternLib.sharedFacet lib.recBuildShared
| .dynlibExternLib lib =>
Expand Down
5 changes: 5 additions & 0 deletions Lake/Build/Info.lean
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Lake.Config.LeanExe
import Lake.Config.ExternLib
import Lake.Build.Facets
import Lake.Util.EquipT
import Lake.Util.Fact

/-!
# Build Info
Expand Down Expand Up @@ -82,6 +83,10 @@ instance [FamilyDef PackageData f α]
: FamilyDef BuildData (BuildInfo.key (.packageFacet p f)) α where
family_key_eq_type := by unfold BuildData; simp

instance [h : Fact (p.name = n)] [FamilyDef CustomData (n, t) α]
: FamilyDef BuildData (BuildInfo.key (.customTarget p t)) α where
family_key_eq_type := by unfold BuildData; simp [h.proof]

instance [FamilyDef CustomData (p.name, t) α]
: FamilyDef BuildData (BuildInfo.key (.customTarget p t)) α where
family_key_eq_type := by unfold BuildData; simp
Expand Down
18 changes: 6 additions & 12 deletions Lake/Config/ExternLib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,22 @@ namespace Lake
structure ExternLib where
/-- The package the library belongs to. -/
pkg : Package
/-- The library's user-defined configuration. -/
config : ExternLibConfig
/-- The external library's name. -/
name : Name
/-- The library's user-defined configuration. -/
config : ExternLibConfig pkg.name name
deriving Inhabited

/-- The external libraries of the package (as an Array). -/
@[inline] def Package.externLibs (self : Package) : Array ExternLib :=
self.externLibConfigs.fold (fun a _ v => a.push (⟨self, v⟩)) #[]
self.externLibConfigs.fold (fun a n v => a.push (⟨self, n, v⟩)) #[]

/-- Try to find a external library in the package with the given name. -/
@[inline] def Package.findExternLib? (name : Name) (self : Package) : Option ExternLib :=
self.externLibConfigs.find? name |>.map (⟨self, ·⟩)
self.externLibConfigs.find? name |>.map (⟨self, name, ·⟩)

namespace ExternLib

/-- The library's well-formed name. -/
@[inline] def name (self : ExternLib) : Name :=
self.config.name

/-- The external library's user-defined `build`. -/
@[inline] def build (self : ExternLib) : SchedulerM (BuildJob FilePath) :=
self.config.build

/--
The arguments to pass to `leanc` when linking the external library.
That is, the package's `moreLinkArgs`.
Expand Down
14 changes: 9 additions & 5 deletions Lake/Config/ExternLibConfig.lean
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ namespace Lake
open Lean System

/-- A external library's declarative configuration. -/
structure ExternLibConfig where
/-- The name of the target. -/
name : Name
/-- The library's build. -/
build : SchedulerM (BuildJob FilePath)
structure ExternLibConfig (pkgName name : Name) where
/-- The library's build data. -/
getJob : CustomData (pkgName, .str name "static") → BuildJob FilePath
deriving Inhabited

/-- A dependently typed configuration based on its registered package and name. -/
structure ExternLibDecl where
pkg : Name
name : Name
config : ExternLibConfig pkg name
2 changes: 1 addition & 1 deletion Lake/Config/Package.lean
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ structure Package where
/-- Lean binary executable configurations for the package. -/
leanExeConfigs : NameMap LeanExeConfig := {}
/-- External library targets for the package. -/
externLibConfigs : NameMap ExternLibConfig := {}
externLibConfigs : DNameMap (ExternLibConfig config.name) := {}
/-- (Opaque references to) targets defined in the package. -/
opaqueTargetConfigs : DNameMap (OpaqueTargetConfig config.name) := {}
/--
Expand Down
6 changes: 4 additions & 2 deletions Lake/Config/TargetConfig.lean
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ namespace Lake
/-- A custom target's declarative configuration. -/
structure TargetConfig (pkgName name : Name) : Type where
/-- The target's build function. -/
build : Package → IndexBuildM (CustomData (pkgName, name))
build : (pkg : Package) → [Fact (pkg.name = pkgName)] →
IndexBuildM (CustomData (pkgName, name))
/-- Does this target produce an associated asynchronous job? -/
getJob? : Option (CustomData (pkgName, name) → Job Unit)
deriving Inhabited

/-- A smart constructor for target configurations that generate CLI targets. -/
@[inline] def mkTargetJobConfig (build : Package → IndexBuildM (BuildJob α))
@[inline] def mkTargetJobConfig
(build : (pkg : Package) → [Fact (pkg.name = pkgName)] → IndexBuildM (BuildJob α))
[h : FamilyDef CustomData (pkgName, name) (BuildJob α)] : TargetConfig pkgName name where
build := cast (by rw [← h.family_key_eq_type]) build
getJob? := some fun data => discard <| ofFamily data
Expand Down
43 changes: 39 additions & 4 deletions Lake/DSL/Facets.lean
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ kw:"module_facet " sig:simpleDeclSig : command => do
$[$doc?]? @[$attrs,*] def $id : ModuleFacetDecl := {
name := $name
config := Lake.mkFacetJobConfig $defn
})
} $[$wds?]?)
| stx => Macro.throwErrorAt stx "ill-formed module facet declaration"

scoped macro (name := packageFacetDecl)
Expand All @@ -42,7 +42,7 @@ kw:"package_facet " sig:simpleDeclSig : command => do
$[$doc?]? @[$attrs,*] def $id : PackageFacetDecl := {
name := $name
config := Lake.mkFacetJobConfig $defn
})
} $[$wds?]?)
| stx => Macro.throwErrorAt stx "ill-formed package facet declaration"

scoped macro (name := libraryFacetDecl)
Expand All @@ -57,7 +57,7 @@ kw:"library_facet " sig:simpleDeclSig : command => do
$[$doc?]? @[$attrs,*] def $id : LibraryFacetDecl := {
name := $name
config := Lake.mkFacetJobConfig $defn
})
} $[$wds?]?)
| stx => Macro.throwErrorAt stx "ill-formed library facet declaration"

scoped macro (name := targetDecl)
Expand All @@ -74,5 +74,40 @@ kw:"target " sig:simpleDeclSig : command => do
pkg := $pkgName
name := $name
config := Lake.mkTargetJobConfig $defn
})
} $[$wds?]?)
| stx => Macro.throwErrorAt stx "ill-formed target declaration"

--------------------------------------------------------------------------------
/-! # External Library Target -/
--------------------------------------------------------------------------------

syntax externLibDeclSpec :=
ident declValSimple

/--
Define a new external library target for the package. Has one form:
```lean
extern_lib «target-name» := /- build function term -/
```
The term should be of type `Package → IndexBuildM (BuildJob FilePath)` and
build the external library's **static** library.
-/
scoped macro (name := externLibDecl)
doc?:optional(docComment) attrs?:optional(Term.attributes)
"extern_lib " spec:externLibDeclSpec : command => do
match spec with
| `(externLibDeclSpec| $id:ident := $defn $[$wds?]?) =>
let attr ← `(Term.attrInstance| externLib)
let attrs := #[attr] ++ expandAttrs attrs?
let pkgName := mkIdentFrom id `_package.name
let targetId := mkIdentFrom id <| id.getId.modifyBase (· ++ `static)
let name := Name.quoteFrom id id.getId
`(target $targetId : FilePath := $defn $[$wds?]?
$[$doc?:docComment]? @[$attrs,*] def $id : ExternLibDecl := {
pkg := $pkgName
name := $name
config := {getJob := ofFamily}
})
| stx => Macro.throwErrorAt stx "ill-formed external library declaration"
26 changes: 0 additions & 26 deletions Lake/DSL/Targets.lean
Original file line number Diff line number Diff line change
Expand Up @@ -55,29 +55,3 @@ doc?:optional(docComment) attrs?:optional(Term.attributes)
let ty := mkCIdentFrom (← getRef) ``LeanExeConfig
let attrs := #[attr] ++ expandAttrs attrs?
mkConfigStructDecl none doc? attrs ty sig

--------------------------------------------------------------------------------
/-! # External Library Target -/
--------------------------------------------------------------------------------

syntax externLibDeclSpec :=
ident optional(Term.typeSpec) declValSimple

/--
Define a new external library target for the package. Has one form:
```lean
extern_lib «target-name» := /- term of type `FileTarget` -/
```
-/
scoped macro (name := externLibDecl)
doc?:optional(docComment) attrs?:optional(Term.attributes)
"extern_lib " spec:externLibDeclSpec : command => do
match spec with
| `(externLibDeclSpec| $id:ident $[: $ty?]? := $defn $[$wds?]?) =>
let attr ← `(Term.attrInstance| externLib)
let ty := ty?.getD <| mkCIdentFrom (← getRef) ``ExternLibConfig
let attrs := #[attr] ++ expandAttrs attrs?
`($[$doc?]? @[$attrs,*] def $id : $ty :=
{name := $(quote id.getId), build := $defn} $[$wds?]?)
| stx => Macro.throwErrorAt stx "ill-formed external library declaration"
10 changes: 8 additions & 2 deletions Lake/Load/Package.lean
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,14 @@ def Package.finalize (self : Package) (deps : Array Package) : LogIO Package :=
evalConstCheck env opts LeanLibConfig ``LeanLibConfig name
let leanExeConfigs ← IO.ofExcept <| mkTagMap env leanExeAttr fun name =>
evalConstCheck env opts LeanExeConfig ``LeanExeConfig name
let externLibConfigs ← IO.ofExcept <| mkTagMap env externLibAttr fun name =>
evalConstCheck env opts ExternLibConfig ``ExternLibConfig name
let externLibConfigs ← mkDTagMap env externLibAttr fun name =>
match evalConstCheck env opts ExternLibDecl ``ExternLibDecl name with
| .ok decl =>
if h : decl.pkg = self.config.name ∧ decl.name = name then
return h.1 ▸ h.2 ▸ decl.config
else
error s!"target was defined as `{decl.pkg}/{decl.name}`, but was registered as `{self.name}/{name}`"
| .error e => error e
let opaqueTargetConfigs ← mkDTagMap env targetAttr fun name =>
match evalConstCheck env opts TargetDecl ``TargetDecl name with
| .ok decl =>
Expand Down
13 changes: 13 additions & 0 deletions Lake/Util/Fact.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/-
Copyright (c) 2022 Mac Malone. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mac Malone
-/

namespace Lake

/-- Type class synthesis of propositions. -/
class Fact (P : Prop) : Prop where
proof : P

instance : Fact (a = a) := ⟨rfl⟩
15 changes: 6 additions & 9 deletions examples/ffi/lib/lakefile.lean
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@ lean_lib FFI
root := `Main
}

def pkgDir := __dir__
def cSrcDir := pkgDir / "c"
def cBuildDir := pkgDir / _package.buildDir / "c"

def buildFfiO : SchedulerM (BuildJob FilePath) := do
let oFile := cBuildDir / "ffi.o"
let srcJob ← inputFile <| cSrcDir / "ffi.cpp"
target ffi.o : FilePath := fun pkg _ => do
let oFile := pkg.buildDir / "c" / "ffi.o"
let srcJob ← inputFile <| pkg.dir / "c" / "ffi.cpp"
buildFileAfterDep oFile srcJob fun srcFile => do
let flags := #["-I", (← getLeanIncludeDir).toString, "-fPIC"]
compileO "ffi.c" oFile srcFile flags "c++"

extern_lib libleanffi := do
extern_lib libleanffi := fun pkg _ => do
let name := nameToStaticLib "leanffi"
buildStaticLib (cBuildDir / name) #[← buildFfiO]
let ffiO ← recBuild <| pkg.customTarget ``ffi.o
buildStaticLib (pkg.buildDir / "c" / name) #[ffiO]
6 changes: 3 additions & 3 deletions examples/targets/lakefile.lean
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ lean_exe b
lean_exe c

@[defaultTarget]
target meow : Unit := fun pkg => do
target meow : Unit := fun pkg _ => do
IO.FS.writeFile (pkg.buildDir / "meow.txt") "Meow!"
return .nil

target bark : Unit := fun _pkg => do
target bark : Unit := fun _pkg _ => do
logInfo "Bark!"
return .nil

package_facet print_name : Unit := fun pkg => do
package_facet print_name : Unit := fun pkg _ => do
IO.println pkg.name
return .nil

0 comments on commit 40927d8

Please sign in to comment.