feat: replace moreLibTargets w/ new extern_lib syntax

This commit is contained in:
tydeu 2022-06-10 12:08:58 -04:00
parent 8428ef7cd7
commit e5782adeff
11 changed files with 128 additions and 70 deletions

View file

@ -44,9 +44,6 @@ def Package.mkStaticLibTarget (self : Package) (lib : LeanLibConfig) : FileTarge
protected def Package.staticLibTarget (self : Package) : FileTarget :=
self.mkStaticLibTarget self.builtinLibConfig
def Package.staticLibTargets (self : Package) : Array FileTarget :=
#[self.staticLibTarget] ++ self.moreLibTargets
-- # Build Package Shared Lib
def Package.linkTargetsOf
@ -54,13 +51,13 @@ def Package.linkTargetsOf
let collect dep recurse := do
let pkg := (← getPackageByName? dep.name).get!
pkg.dependencies.forM fun dep => discard <| recurse dep
return pkg.oFileTargetsOf targetMap ++ pkg.moreLibTargets
return pkg.oFileTargetsOf targetMap ++ pkg.externLibTargets
let ⟨x, map⟩ ← RBTopT.run <| self.dependencies.forM fun dep =>
discard <| buildRBTop (cmp := Name.quickCmp) collect Dependency.name dep
match x with
| Except.ok _ =>
let ts := map.fold (fun acc _ vs => acc ++ vs) #[]
return self.oFileTargetsOf targetMap ++ self.moreLibTargets ++ ts
return self.oFileTargetsOf targetMap ++ self.externLibTargets ++ ts
| Except.error _ => panic! "dependency cycle emerged after resolution"
def Package.mkSharedLibTarget (self : Package) (lib : LeanLibConfig) : FileTarget :=

View file

@ -102,7 +102,7 @@ def Package.buildImportsAndDeps (imports : List String) (self : Package) : Build
else
-- build local imports from list
let infos := (← getWorkspace).processImportList imports
if self.exes.isEmpty && self.defaultFacet matches .none | .leanLib | .oleans then
if self.leanExes.isEmpty && self.defaultFacet matches .none | .leanLib | .oleans then
let build := recBuildModuleOleanTargetWithLocalImports depTarget
let targets ← buildModuleArray infos build
targets.forM (·.buildOpaque)

View file

@ -56,9 +56,14 @@ def resolveExeTarget (pkg : Package) (exe : LeanExeConfig) (facet : String) : Ex
throw <| CliError.unknownFacet "executable" facet
def resolveTargetInPackage (pkg : Package) (target : Name) (facet : String) : Except CliError OpaqueTarget :=
if let some exe := pkg.findExe? target then
if let some exe := pkg.findLeanExe? target then
resolveExeTarget pkg exe facet
else if let some lib := pkg.findLib? target then
else if let some lib := pkg.findExternLib? target then
if facet.isEmpty then
return lib.target.withoutInfo
else
throw <| CliError.invalidFacet target facet
else if let some lib := pkg.findLeanLib? target then
resolveLibTarget pkg lib facet
else if pkg.hasModule target then
resolveModuleTarget pkg target facet
@ -86,6 +91,23 @@ def resolvePackageTarget (pkg : Package) (facet : String) : Except CliError Opaq
else
throw <| CliError.unknownFacet "package" facet
def resolveTargetInWorkspace (ws : Workspace) (spec : String) (facet : String) : Except CliError OpaqueTarget :=
if let some (pkg, exe) := ws.findLeanExe? spec then
resolveExeTarget pkg exe facet
else if let some (_, lib) := ws.findExternLib? spec then
if facet.isEmpty then
return lib.target.withoutInfo
else
throw <| CliError.invalidFacet spec facet
else if let some (pkg, lib) := ws.findLeanLib? spec then
resolveLibTarget pkg lib facet
else if let some pkg := ws.packageByName? spec then
resolvePackageTarget pkg facet
else if let some pkg := ws.packageForModule? spec then
resolveModuleTarget pkg spec facet
else
throw <| CliError.unknownTarget spec
def resolveTargetBaseSpec (ws : Workspace) (spec : String) (facet := "") : Except CliError OpaqueTarget := do
match spec.splitOn "/" with
| [spec] =>
@ -101,16 +123,7 @@ def resolveTargetBaseSpec (ws : Workspace) (spec : String) (facet := "") : Excep
else
throw <| CliError.unknownModule mod
else
if let some (pkg, exe) := ws.findExe? spec then
resolveExeTarget pkg exe facet
else if let some (pkg, lib) := ws.findLib? spec then
resolveLibTarget pkg lib facet
else if let some pkg := ws.packageByName? spec then
resolvePackageTarget pkg facet
else if let some pkg := ws.packageForModule? spec then
resolveModuleTarget pkg spec facet
else
throw <| CliError.unknownTarget spec
resolveTargetInWorkspace ws spec facet
| [pkgSpec, targetSpec] =>
let pkgSpec := if pkgSpec.startsWith "@" then pkgSpec.drop 1 else pkgSpec
let pkg ← parsePackageSpec ws pkgSpec
@ -130,6 +143,3 @@ def parseTargetSpec (ws : Workspace) (spec : String) : Except CliError OpaqueTar
resolveTargetBaseSpec ws rootSpec facet
| _ =>
throw <| CliError.invalidTargetSpec spec ':'
def parseTargetSpecs (ws : Workspace) (specs : List String) : Except CliError (List OpaqueTarget) :=
specs.mapM <| parseTargetSpec ws

View file

@ -24,6 +24,7 @@ inductive CliError
| missingModule (pkg : Name) (mod : Name)
| missingTarget (pkg : Name) (spec : String)
| invalidTargetSpec (spec : String) (tooMany : Char)
| invalidFacet (target : Name) (facet : String)
/- Script CLI Error -/
| unknownScript (script : String)
| missingScriptDoc (script : String)
@ -51,6 +52,7 @@ def toString : CliError → String
| missingModule pkg mod => s!"package '{pkg.toString false}' has no module '{mod.toString false}'"
| missingTarget pkg spec => s!"package '{pkg.toString false}' has no target '{spec}'"
| invalidTargetSpec s c => s!"invalid script spec '{s}' (too many '{c}')"
| invalidFacet t f => s!"invalid facet `{f}`; target {t.toString false} has no facets"
| unknownScript s => s!"unknown script {s}"
| missingScriptDoc s => s!"no documentation provided for `{s}`"
| invalidScriptSpec s => s!"invalid script spec '{s}' (too many '/')"

View file

@ -97,7 +97,9 @@ unsafe def loadUnsafe (dir : FilePath) (args : List String := [])
| [pkgDeclName] =>
let config ← evalPackageDecl env pkgDeclName dir args leanOpts
unless config.dependencies.isEmpty do
logWarning "Syntax for package dependencies has changed. Use the new require syntax."
logWarning "Syntax for package dependencies has changed. Use the new `require` syntax."
unless config.moreLibTargets.isEmpty do
logWarning "Syntax for `moreLibTargets` has changed. Use the new `extern_lib` syntax."
if config.defaultFacet = PackageFacet.bin then
logWarning "The `bin` package facet has been deprecated in favor of `exe`."
if config.defaultFacet = PackageFacet.oleans then
@ -105,17 +107,22 @@ unsafe def loadUnsafe (dir : FilePath) (args : List String := [])
let config := {config with dependencies := depsExt.getState env ++ config.dependencies}
let scripts ← scriptAttr.ext.getState env |>.foldM (init := {})
fun m d => return m.insert d <| ← evalScriptDecl env d leanOpts
let libs ← leanLibAttr.ext.getState env |>.foldM (init := {}) fun m d =>
let leanLibs ← leanLibAttr.ext.getState env |>.foldM (init := {}) fun m d =>
let eval := env.evalConstCheck LeanLibConfig leanOpts ``LeanLibConfig d
return m.insert d <| ← IO.ofExcept eval.run.run
let exes ← leanExeAttr.ext.getState env |>.foldM (init := {}) fun m d =>
let leanExes ← leanExeAttr.ext.getState env |>.foldM (init := {}) fun m d =>
let eval := env.evalConstCheck LeanExeConfig leanOpts ``LeanExeConfig d
return m.insert d <| ← IO.ofExcept eval.run.run
if libs.isEmpty && exes.isEmpty && !config.defaultFacet = .none then
let externLibs ← externLibAttr.ext.getState env |>.foldM (init := {}) fun m d =>
let eval := env.evalConstCheck ExternLibConfig leanOpts ``ExternLibConfig d
return m.insert d <| ← IO.ofExcept eval.run.run
if leanLibs.isEmpty && leanExes.isEmpty && config.defaultFacet ≠ .none then
logWarning <| "Package targets are deprecated. " ++
"Add a `lean_exe` and/or `lean_lib` default target to the package instead."
let defaultTargets := defaultTargetAttr.ext.getState env |>.toArray
return {dir, config, scripts, libs, exes, defaultTargets}
return {dir, config, scripts, leanLibs, leanExes, externLibs, defaultTargets}
| _ => error s!"configuration file has multiple `package` declarations"
else
error s!"package configuration `{configFile}` has errors"

View file

@ -261,10 +261,12 @@ structure Package where
config : PackageConfig
/-- Scripts for the package. -/
scripts : NameMap Script := {}
/-- Additional Lean library targets for the package. -/
libs : NameMap LeanLibConfig := {}
/-- Additional Lean binary executable targets for the package. -/
exes : NameMap LeanExeConfig := {}
/-- Lean library targets for the package. -/
leanLibs : NameMap LeanLibConfig := {}
/-- Lean binary executable targets for the package. -/
leanExes : NameMap LeanExeConfig := {}
/-- External library targets for the package. -/
externLibs : NameMap ExternLibConfig := {}
/--
The names of the package's targets to build by default
(i.e., on a bare `lake build` of the package).
@ -323,13 +325,25 @@ def extraDepTarget (self : Package) : OpaqueTarget :=
def defaultFacet (self : Package) : PackageFacet :=
self.config.defaultFacet
/-- The package's `moreLibTargets` configuration. -/
def moreLibTargets (self : Package) : Array FileTarget :=
self.config.moreLibTargets
/-- Get the package's library configuration with the given name. -/
def findLib? (self : Package) (name : Name) : Option LeanLibConfig :=
self.libs.find? name
def findLeanLib? (name : Name) (self : Package) : Option LeanLibConfig :=
self.leanLibs.find? name
/-- Get the package's executable configuration with the given name. -/
def findExe? (self : Package) (name : Name) : Option LeanExeConfig :=
self.exes.find? name
def findLeanExe? (name : Name) (self : Package) : Option LeanExeConfig :=
self.leanExes.find? name
/-- Get the package's external library target with the given name. -/
def findExternLib? (name : Name) (self : Package) : Option ExternLibConfig :=
self.externLibs.find? name
/-- Get an `Array` of the package's external library targets. -/
def externLibTargets (self : Package) : Array FileTarget :=
self.externLibs.fold (fun xs _ x => xs.push x.target) #[] ++ self.moreLibTargets
/-- The package's `moreServerArgs` configuration. -/
def moreServerArgs (self : Package) : Array String :=
@ -403,10 +417,6 @@ def libDir (self : Package) : FilePath :=
def binDir (self : Package) : FilePath :=
self.buildDir / self.config.binDir
/-- The package's `moreLibTargets` configuration. -/
def moreLibTargets (self : Package) : Array FileTarget :=
self.config.moreLibTargets
/-- The library configuration built into the package configuration. -/
def builtinLibConfig (self : Package) : LeanLibConfig :=
self.config.toLeanLibConfig
@ -421,12 +431,12 @@ def getModuleArray (self : Package) : IO (Array Name) :=
/-- Whether the given module is considered local to the package. -/
def isLocalModule (mod : Name) (self : Package) : Bool :=
self.libs.any (fun _ lib => lib.isLocalModule mod) ||
self.leanLibs.any (fun _ lib => lib.isLocalModule mod) ||
self.builtinLibConfig.isLocalModule mod
/-- Whether the given module is in the package (i.e., can build it). -/
def hasModule (mod : Name) (self : Package) : Bool :=
self.libs.any (fun _ lib => lib.hasModule mod) ||
self.exes.any (fun _ exe => exe.root == mod) ||
self.leanLibs.any (fun _ lib => lib.hasModule mod) ||
self.leanExes.any (fun _ exe => exe.root == mod) ||
self.builtinLibConfig.hasModule mod ||
self.config.binRoot == mod

View file

@ -46,10 +46,9 @@ structure LeanLibConfig where
-/
libName := toUpperCamelCase name |>.toString (escape := false)
/--
/--
Additional arguments to pass to `leanc` while linking the shared library.
These will come *after* the paths of libraries built with the package's
`moreLibTargets`.
These will come *after* the paths of package's external libraries.
-/
moreLinkArgs : Array String := #[]
@ -132,8 +131,7 @@ structure LeanExeConfig where
/--
Additional arguments to pass to `leanc` when linking the binary executable.
These will come *after* the paths of libraries built with the package's
`moreLibTargets`.
These will come *after* the paths of the package's external libraries.
-/
moreLinkArgs : Array String := #[]
@ -159,3 +157,17 @@ def linkArgs (self : LeanExeConfig) : Array String :=
self.moreLinkArgs
end LeanExeConfig
--------------------------------------------------------------------------------
-- # External Library Build Configuration
--------------------------------------------------------------------------------
/-- A external library's declarative configuration. -/
structure ExternLibConfig where
/-- The name of the target. -/
name : Name
/-- The library's build target. -/
target : FileTarget
deriving Inhabited

View file

@ -96,12 +96,16 @@ def packageForModule? (mod : Name) (self : Workspace) : Option Package :=
self.findPackage? (·.isLocalModule mod)
/-- Get the workspace's library configuration with the given name. -/
def findLib? (name : Name) (self : Workspace) : Option (Package × LeanLibConfig) :=
self.packageArray.findSome? fun pkg => pkg.findLib? name <&> (pkg, ·)
def findLeanLib? (name : Name) (self : Workspace) : Option (Package × LeanLibConfig) :=
self.packageArray.findSome? fun pkg => pkg.findLeanLib? name <&> (pkg, ·)
/-- Get the workspace's executable configuration with the given name. -/
def findExe? (name : Name) (self : Workspace) : Option (Package × LeanExeConfig) :=
self.packageArray.findSome? fun pkg => pkg.findExe? name <&> (pkg, ·)
def findLeanExe? (name : Name) (self : Workspace) : Option (Package × LeanExeConfig) :=
self.packageArray.findSome? fun pkg => pkg.findLeanExe? name <&> (pkg, ·)
/-- Get the workspace's external library with the given name. -/
def findExternLib? (name : Name) (self : Workspace) : Option (Package × ExternLibConfig) :=
self.packageArray.findSome? fun pkg => pkg.findExternLib? name <&> (pkg, ·)
/-- The `LEAN_PATH` of the workspace. -/
def oleanPath (self : Workspace) : SearchPath :=

View file

@ -9,16 +9,16 @@ open Lean
namespace Lake
initialize packageAttr : TagAttribute ←
registerTagAttribute `package "mark a definition as a Lake package declaration"
registerTagAttribute `package "mark a definition as a Lake package configuration"
initialize scriptAttr : TagAttribute ←
registerTagAttribute `script "mark a definition as a Lake script"
initialize leanLibAttr : TagAttribute ←
registerTagAttribute `leanLib "mark a definition as a Lake Lean library target declaration"
registerTagAttribute `leanLib "mark a definition as a Lake Lean library target configuration"
initialize leanExeAttr : TagAttribute ←
registerTagAttribute `leanExe "mark a definition as a Lake Lean executable target declaration"
registerTagAttribute `leanExe "mark a definition as a Lake Lean executable target configuration"
initialize defaultTargetAttr : TagAttribute ←
registerTagAttribute `defaultTarget "mark a Lake target as the package's default"
@ -27,3 +27,6 @@ initialize defaultTargetAttr : TagAttribute ←
leanLibAttr.hasTag env name || leanExeAttr.hasTag env name
unless valid do
throwError "attribute `defaultTarget` can only be used on a target (e.g., `lean_lib`, `lean_exe`)"
initialize externLibAttr : TagAttribute ←
registerTagAttribute `externLib "mark a definition as a Lake external library target"

View file

@ -52,3 +52,19 @@ doc?:optional(docComment) attrs?:optional(Term.attributes)
let ty := mkCIdentFrom (← getRef) ``LeanExeConfig
let attrs := #[attr] ++ expandAttrs attrs?
mkTargetDecl doc? attrs ty spec
syntax externLibDeclSpec :=
ident optional(Term.typeSpec) declValSimple
/-- Define a new external library target for the package. -/
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?:docComment]? @[$attrs,*] def $id : $ty :=
{name := $(quote id.getId), target := $defn} $[$wds?]?)
| stx => Macro.throwErrorAt stx "ill-formed external library declaration"

View file

@ -1,25 +1,8 @@
import Lake
open System Lake DSL
def cDir : FilePath := "c"
def ffiSrc := cDir / "ffi.cpp"
def buildDir := defaultBuildDir
def ffiOTarget (pkgDir : FilePath) : FileTarget :=
let oFile := pkgDir / buildDir / cDir / "ffi.o"
let srcTarget := inputFileTarget <| pkgDir / ffiSrc
fileTargetWithDep oFile srcTarget fun srcFile => do
compileO oFile srcFile #["-I", (← getLeanIncludeDir).toString] "c++"
def cLibTarget (pkgDir : FilePath) : FileTarget :=
let libFile := pkgDir / buildDir / cDir / "libffi.a"
staticLibTarget libFile #[ffiOTarget pkgDir]
package ffi {
-- customize layout
srcDir := "lean"
-- specify the lib as an additional target
moreLibTargets := #[cLibTarget __dir__]
}
lean_lib FFI
@ -27,3 +10,17 @@ lean_lib FFI
@[defaultTarget] lean_exe test {
root := `Main
}
def pkgDir := __dir__
def cSrcDir := pkgDir / "c"
def cBuildDir := pkgDir / defaultBuildDir / "c"
def ffiOTarget : FileTarget :=
let oFile := cBuildDir / "ffi.o"
let srcTarget := inputFileTarget <| cSrcDir / "ffi.cpp"
fileTargetWithDep oFile srcTarget fun srcFile => do
compileO oFile srcFile #["-I", (← getLeanIncludeDir).toString] "c++"
extern_lib cLib :=
let libFile := cBuildDir / "libffi.a"
staticLibTarget libFile #[ffiOTarget]