feat: lake: Reservoir-related configuration for packages (#4770)
Adds additional fields to the package configuration which will be used by Reservoir: * `version`: The version of the package. Follows Lean's model of `<major>.<minor>.<patch>[-<specialDescr>]`. * `versionTags`: A pattern matching the set of Git tags Reservoir should consider package version revisions. * `description`: A short description for the package. Takes precedence over the GitHub's description. * `keywords`: An array of package keywords that will be used to group packages into categories on Reservoir. Takes precedence over labels on the repository. * `homepage`: A URL to a website for the package. Takes precedence over GitHub's homepage. * `license`: An SPFX license identifier for the package's license (not verified to be well-formed). * `licenseFiles`: An array of (relative) files the contain license information (e.g., `#["LICENSE", "NOTICE"]` for Apache 2.0). * `readmeFile`: Relative path to the package's readme (enables non-standard README locations). * `reservoir`: Reservoir will use this setting to determine whether to include packages in its index. Also adds two new CLI commands: * `lake reservoir-config`: Used by Reservoir to extract a package's configuration. * `lake check-build`: Determines whether the package has any default build targets configured. The Reservoir configuration also makes uses of the exiting `name` and `platformIndependent` fields.
This commit is contained in:
parent
830b1191b3
commit
df9cdcd1b7
25 changed files with 543 additions and 32 deletions
1
debug.log
Normal file
1
debug.log
Normal file
|
|
@ -0,0 +1 @@
|
|||
[0829/202002.254:ERROR:crashpad_client_win.cc(868)] not connected
|
||||
|
|
@ -18,6 +18,7 @@ COMMANDS:
|
|||
init <name> <temp> create a Lean package in the current directory
|
||||
build <targets>... build targets
|
||||
exe <exe> <args>... build an exe and run it in Lake's environment
|
||||
check-build check if any default build targets are configured
|
||||
test test the package using the configured test driver
|
||||
check-test check if there is a properly configured test driver
|
||||
lint lint the package using the configured lint driver
|
||||
|
|
@ -134,6 +135,19 @@ TARGET EXAMPLES: build the ...
|
|||
A bare `lake build` command will build the default facet of the root package.
|
||||
Package dependencies are not updated during a build."
|
||||
|
||||
def helpCheckBuild :=
|
||||
"Check if any default build targets are configured
|
||||
|
||||
USAGE:
|
||||
lake check-build
|
||||
|
||||
Exits with code 0 if the workspace's root package has any
|
||||
default targets configured. Errors (with code 1) otherwise.
|
||||
|
||||
Does NOT verify that the configured default targets are valid.
|
||||
It merely verifies that some are specified.
|
||||
"
|
||||
|
||||
def helpUpdate :=
|
||||
"Update dependencies and save them to the manifest
|
||||
|
||||
|
|
@ -383,6 +397,7 @@ def help : (cmd : String) → String
|
|||
| "new" => helpNew
|
||||
| "init" => helpInit
|
||||
| "build" => helpBuild
|
||||
| "check-build" => helpCheckBuild
|
||||
| "update" | "upgrade" => helpUpdate
|
||||
| "pack" => helpPack
|
||||
| "unpack" => helpUnpack
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ s!"import Lake
|
|||
open Lake DSL
|
||||
|
||||
package {repr pkgName} where
|
||||
-- add package configuration options here
|
||||
version := v!\"0.1.0\"
|
||||
|
||||
lean_lib {libRoot} where
|
||||
-- add library configuration options here
|
||||
|
|
@ -64,6 +64,7 @@ lean_exe {repr exeName} where
|
|||
|
||||
def stdTomlConfigFileContents (pkgName libRoot exeName : String) :=
|
||||
s!"name = {repr pkgName}
|
||||
version = \"0.1.0\"
|
||||
defaultTargets = [{repr exeName}]
|
||||
|
||||
[[lean_lib]]
|
||||
|
|
@ -79,7 +80,7 @@ s!"import Lake
|
|||
open Lake DSL
|
||||
|
||||
package {repr pkgName} where
|
||||
-- add package configuration options here
|
||||
version := v!\"0.1.0\"
|
||||
|
||||
@[default_target]
|
||||
lean_exe {repr exeName} where
|
||||
|
|
@ -88,6 +89,7 @@ lean_exe {repr exeName} where
|
|||
|
||||
def exeTomlConfigFileContents (pkgName exeName : String) :=
|
||||
s!"name = {repr pkgName}
|
||||
version = \"0.1.0\"
|
||||
defaultTargets = [{repr exeName}]
|
||||
|
||||
[[lean_exe]]
|
||||
|
|
@ -100,7 +102,7 @@ s!"import Lake
|
|||
open Lake DSL
|
||||
|
||||
package {repr pkgName} where
|
||||
-- add package configuration options here
|
||||
version := v!\"0.1.0\"
|
||||
|
||||
@[default_target]
|
||||
lean_lib {libRoot} where
|
||||
|
|
@ -109,6 +111,7 @@ lean_lib {libRoot} where
|
|||
|
||||
def libTomlConfigFileContents (pkgName libRoot : String) :=
|
||||
s!"name = {repr pkgName}
|
||||
version = \"0.1.0\"
|
||||
defaultTargets = [{repr libRoot}]
|
||||
|
||||
[[lean_lib]]
|
||||
|
|
@ -120,11 +123,11 @@ s!"import Lake
|
|||
open Lake DSL
|
||||
|
||||
package {repr pkgName} where
|
||||
-- Settings applied to both builds and interactive editing
|
||||
version := v!\"0.1.0\"
|
||||
keywords := #[\"math\"]
|
||||
leanOptions := #[
|
||||
⟨`pp.unicode.fun, true⟩ -- pretty-prints `fun a ↦ b`
|
||||
]
|
||||
-- add any additional package configuration options here
|
||||
|
||||
require \"leanprover-community\" / \"mathlib\"
|
||||
|
||||
|
|
@ -135,6 +138,8 @@ lean_lib {libRoot} where
|
|||
|
||||
def mathTomlConfigFileContents (pkgName libRoot : String) :=
|
||||
s!"name = {repr pkgName}
|
||||
version = \"0.1.0\"
|
||||
keywords = [\"math\"]
|
||||
defaultTargets = [{repr libRoot}]
|
||||
|
||||
[leanOptions]
|
||||
|
|
|
|||
|
|
@ -337,6 +337,11 @@ protected def build : CliM PUnit := do
|
|||
if showProgress then
|
||||
IO.println "Build completed successfully."
|
||||
|
||||
protected def checkBuild : CliM PUnit := do
|
||||
processOptions lakeOption
|
||||
let pkg ← loadPackage (← mkLoadConfig (← getThe LakeOptions))
|
||||
noArgsRem do exit <| if pkg.defaultTargets.isEmpty then 1 else 0
|
||||
|
||||
protected def resolveDeps : CliM PUnit := do
|
||||
processOptions lakeOption
|
||||
let opts ← getThe LakeOptions
|
||||
|
|
@ -500,6 +505,66 @@ protected def translateConfig : CliM PUnit := do
|
|||
if outFile?.isNone then
|
||||
IO.FS.rename pkg.configFile (pkg.configFile.addExtension "bak")
|
||||
|
||||
def ReservoirConfig.currentSchemaVersion : StdVer := v!"1.0.0"
|
||||
|
||||
structure ReservoirConfig where
|
||||
name : String
|
||||
version : StdVer
|
||||
versionTags : List String
|
||||
description : String
|
||||
keywords : Array String
|
||||
homepage : String
|
||||
platformIndependent : Option Bool
|
||||
license : String
|
||||
licenseFiles : Array FilePath
|
||||
readmeFile : Option FilePath
|
||||
doIndex : Bool
|
||||
schemaVersion := ReservoirConfig.currentSchemaVersion
|
||||
deriving Lean.ToJson
|
||||
|
||||
protected def reservoirConfig : CliM PUnit := do
|
||||
processOptions lakeOption
|
||||
let opts ← getThe LakeOptions
|
||||
let cfg ← mkLoadConfig opts
|
||||
let _ ← id do
|
||||
let some verStr ← takeArg?
|
||||
| return ReservoirConfig.currentSchemaVersion
|
||||
match StdVer.parse verStr with
|
||||
| .ok ver => return ver
|
||||
| .error e => error s!"invalid target version: {e}"
|
||||
noArgsRem do
|
||||
let pkg ← loadPackage cfg
|
||||
let repoTags ← GitRepo.getTags pkg.dir
|
||||
let licenseFiles ← pkg.licenseFiles.filterMapM fun relPath => do
|
||||
return if (← (pkg.dir / relPath).pathExists) then some relPath else none
|
||||
let readmeFile :=
|
||||
if (← pkg.readmeFile.pathExists) then some pkg.relReadmeFile else none
|
||||
let cfg : ReservoirConfig := {
|
||||
name := pkg.name.toString
|
||||
version := pkg.version
|
||||
versionTags := repoTags.filter pkg.versionTags.matches
|
||||
description := pkg.description
|
||||
homepage := pkg.homepage
|
||||
keywords := pkg.keywords
|
||||
platformIndependent := pkg.platformIndependent
|
||||
license := pkg.license
|
||||
licenseFiles := licenseFiles
|
||||
readmeFile := readmeFile
|
||||
doIndex := pkg.reservoir
|
||||
}
|
||||
IO.println (toJson cfg).pretty
|
||||
|
||||
protected def versionTags : CliM PUnit := do
|
||||
processOptions lakeOption
|
||||
let opts ← getThe LakeOptions
|
||||
let cfg ← mkLoadConfig opts
|
||||
noArgsRem do
|
||||
let pkg ← loadPackage cfg
|
||||
let tags ← GitRepo.getTags pkg.dir
|
||||
for tag in tags do
|
||||
if pkg.versionTags.matches tag then
|
||||
IO.println tag
|
||||
|
||||
protected def selfCheck : CliM PUnit := do
|
||||
processOptions lakeOption
|
||||
noArgsRem do verifyInstall (← getThe LakeOptions)
|
||||
|
|
@ -513,6 +578,7 @@ def lakeCli : (cmd : String) → CliM PUnit
|
|||
| "new" => lake.new
|
||||
| "init" => lake.init
|
||||
| "build" => lake.build
|
||||
| "check-build" => lake.checkBuild
|
||||
| "update" | "upgrade" => lake.update
|
||||
| "resolve-deps" => lake.resolveDeps
|
||||
| "pack" => lake.pack
|
||||
|
|
@ -532,6 +598,8 @@ def lakeCli : (cmd : String) → CliM PUnit
|
|||
| "exe" | "exec" => lake.exe
|
||||
| "lean" => lake.lean
|
||||
| "translate-config" => lake.translateConfig
|
||||
| "reservoir-config" => lake.reservoirConfig
|
||||
| "version-tags" => lake.versionTags
|
||||
| "self-check" => lake.selfCheck
|
||||
| "help" => lake.help
|
||||
| cmd => throw <| CliError.unknownCommand cmd
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ open DSL System Lean Syntax Parser Module
|
|||
private local instance : Quote FilePath where
|
||||
quote path := quote path.toString
|
||||
|
||||
private local instance [Quote α] : Quote (Array α) where
|
||||
quote xs := let xs : Array Term := xs.map quote; Unhygienic.run `(#[$xs,*])
|
||||
|
||||
private local instance : BEq FilePath where
|
||||
beq a b := a.normalize == b.normalize
|
||||
|
||||
|
|
@ -67,6 +70,13 @@ def quoteLeanOption (opt : LeanOption) : Term := Unhygienic.run do
|
|||
|
||||
private local instance : Quote LeanOption := ⟨quoteLeanOption⟩
|
||||
|
||||
protected def LeanVer.quote (v : LeanVer) : Term := Unhygienic.run do
|
||||
let lit := Syntax.mkLit interpolatedStrLitKind v.toString.quote
|
||||
let stx := mkNode interpolatedStrKind #[lit]
|
||||
`(v!$stx)
|
||||
|
||||
private local instance : Quote LeanVer := ⟨LeanVer.quote⟩
|
||||
|
||||
/-! ## Configuration Encoders -/
|
||||
|
||||
def WorkspaceConfig.addDeclFields (cfg : WorkspaceConfig) (fs : Array DeclField) : Array DeclField :=
|
||||
|
|
@ -91,7 +101,8 @@ def LeanConfig.addDeclFields (cfg : LeanConfig) (fs : Array DeclField) : Array D
|
|||
|
||||
def PackageConfig.mkSyntax (cfg : PackageConfig)
|
||||
(testDriver := cfg.testDriver) (lintDriver := cfg.lintDriver)
|
||||
: PackageDecl := Unhygienic.run do
|
||||
: PackageDecl := Unhygienic.run do
|
||||
have : Quote Term := ⟨id⟩
|
||||
let declVal? := mkDeclValWhere? <| Array.empty
|
||||
|> addDeclFieldD `precompileModules cfg.precompileModules false
|
||||
|> addDeclFieldD `moreGlobalServerArgs cfg.moreGlobalServerArgs #[]
|
||||
|
|
@ -108,9 +119,26 @@ def PackageConfig.mkSyntax (cfg : PackageConfig)
|
|||
|> addDeclFieldD `testDriverArgs cfg.testDriverArgs #[]
|
||||
|> addDeclFieldD `lintDriver lintDriver ""
|
||||
|> addDeclFieldD `lintDriverArgs cfg.lintDriverArgs #[]
|
||||
|> addDeclFieldD `version cfg.version v!"0.0.0"
|
||||
|> addDeclField? `versionTags (quoteVerTags? cfg.versionTags)
|
||||
|> addDeclFieldD `description cfg.description ""
|
||||
|> addDeclFieldD `keywords cfg.keywords #[]
|
||||
|> addDeclFieldD `homepage cfg.homepage ""
|
||||
|> addDeclFieldD `license cfg.license ""
|
||||
|> addDeclFieldD `licenseFiles cfg.licenseFiles #["LICENSE"]
|
||||
|> addDeclFieldD `readmeFile cfg.readmeFile "README.md"
|
||||
|> addDeclFieldD `reservoir cfg.reservoir true
|
||||
|> cfg.toWorkspaceConfig.addDeclFields
|
||||
|> cfg.toLeanConfig.addDeclFields
|
||||
`(packageDecl|package $(mkIdent cfg.name):ident $[$declVal?]?)
|
||||
where
|
||||
quoteVerTags? (pat : StrPat) : Option Term :=
|
||||
match pat with
|
||||
| .mem xs => if xs.isEmpty then Unhygienic.run `(∅) else some (quote xs)
|
||||
| .startsWith pre => Unhygienic.run `(.$(mkIdent `startsWith) $(quote pre))
|
||||
| .satisfies _ n =>
|
||||
if n.isAnonymous || n == `default then none else
|
||||
Unhygienic.run `(.$(mkIdent n))
|
||||
|
||||
private def getEscapedNameParts? (acc : List String) : Name → Option (List String)
|
||||
| Name.anonymous => if acc.isEmpty then none else some acc
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ def LeanConfig.toToml (cfg : LeanConfig) (t : Table := {}) : Table :=
|
|||
|>.smartInsert `weakLinkArgs cfg.weakLinkArgs
|
||||
|
||||
instance : ToToml LeanConfig := ⟨(toToml ·.toToml)⟩
|
||||
instance : ToToml LeanVer := ⟨(toToml <| toString ·)⟩
|
||||
|
||||
protected def PackageConfig.toToml (cfg : PackageConfig) (t : Table := {}) : Table :=
|
||||
t.insert `name cfg.name
|
||||
|
|
@ -75,8 +76,25 @@ protected def PackageConfig.toToml (cfg : PackageConfig) (t : Table := {}) : Tab
|
|||
|>.smartInsert `releaseRepo (cfg.releaseRepo <|> cfg.releaseRepo?)
|
||||
|>.insertD `buildArchive (cfg.buildArchive?.getD cfg.buildArchive) (defaultBuildArchive cfg.name)
|
||||
|>.insertD `preferReleaseBuild cfg.preferReleaseBuild false
|
||||
|>.insertD `version cfg.version v!"0.0.0"
|
||||
|> smartInsertVerTags cfg.versionTags
|
||||
|>.smartInsert `keywords cfg.description
|
||||
|>.smartInsert `keywords cfg.keywords
|
||||
|>.smartInsert `homepage cfg.homepage
|
||||
|>.smartInsert `license cfg.license
|
||||
|>.insertD `licenseFiles cfg.licenseFiles #["LICENSE"]
|
||||
|>.insertD `readmeFile cfg.readmeFile "README.md"
|
||||
|>.insertD `reservoir cfg.reservoir true
|
||||
|> cfg.toWorkspaceConfig.toToml
|
||||
|> cfg.toLeanConfig.toToml
|
||||
where
|
||||
smartInsertVerTags (pat : StrPat) (t : Table) : Table :=
|
||||
match pat with
|
||||
| .mem s => t.insert `versionTags (toToml s)
|
||||
| .startsWith p => t.insert `versionTags.startsWith (toToml p)
|
||||
| .satisfies _ n =>
|
||||
if n.isAnonymous || n == `default then t else
|
||||
t.insert `versionTags.preset (toToml n)
|
||||
|
||||
instance : ToToml PackageConfig := ⟨(toToml ·.toToml)⟩
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import Lake.Config.Script
|
|||
import Lake.Load.Config
|
||||
import Lake.Util.DRBMap
|
||||
import Lake.Util.OrdHashSet
|
||||
import Lake.Util.Version
|
||||
|
||||
open System Lean
|
||||
|
||||
|
|
@ -23,6 +24,56 @@ namespace Lake
|
|||
@[inline] def defaultBuildArchive (name : Name) : String :=
|
||||
s!"{name.toString false}-{System.Platform.target}.tar.gz"
|
||||
|
||||
/-- A `String` pattern. Matches some subset of strings. -/
|
||||
inductive StrPat
|
||||
/--
|
||||
Matches a string that satisfies an arbitrary predicate
|
||||
(optionally identified by a `Name`).
|
||||
-/
|
||||
| satisfies (f : String → Bool) (name := Name.anonymous)
|
||||
/-- Matches a string that is a member of the the array -/
|
||||
| mem (xs : Array String)
|
||||
/-- Matches a string that starts with this prefix. -/
|
||||
| startsWith (pre : String)
|
||||
deriving Inhabited
|
||||
|
||||
instance : Coe (Array String) StrPat := ⟨.mem⟩
|
||||
instance : Coe (String → Bool) StrPat := ⟨.satisfies⟩
|
||||
|
||||
/-- Matches nothing. -/
|
||||
def StrPat.none : StrPat := .mem #[]
|
||||
|
||||
instance : EmptyCollection StrPat := ⟨.none⟩
|
||||
|
||||
/--
|
||||
Whether a string is "version-like".
|
||||
That is, a `v` followed by a digit.
|
||||
-/
|
||||
def isVerLike (s : String) : Bool :=
|
||||
if h : s.utf8ByteSize ≥ 2 then
|
||||
s.get' 0 (by simp [String.atEnd]; omega) == 'v' &&
|
||||
(s.get' ⟨1⟩ (by simp [String.atEnd]; omega)).isDigit
|
||||
else
|
||||
false
|
||||
|
||||
/-- Matches a "version-like" string: a `v` followed by a digit. -/
|
||||
def StrPat.verLike : StrPat := .satisfies isVerLike `verLike
|
||||
|
||||
/-- Default string pattern for a Package's `versionTags`. -/
|
||||
def defaultVersionTags := StrPat.satisfies isVerLike `default
|
||||
|
||||
/-- Builtin `StrPat` presets available to TOML for `versionTags`. -/
|
||||
def versionTagPresets :=
|
||||
NameMap.empty
|
||||
|>.insert `verLike .verLike
|
||||
|>.insert `default defaultVersionTags
|
||||
|
||||
/-- Returns whether the string `s` matches the pattern. -/
|
||||
def StrPat.matches (s : String) : (self : StrPat) → Bool
|
||||
| .satisfies f _ => f s
|
||||
| .mem xs => xs.contains s
|
||||
| .startsWith p => p.isPrefixOf s
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
/-! # PackageConfig -/
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
@ -183,6 +234,127 @@ structure PackageConfig extends WorkspaceConfig, LeanConfig where
|
|||
-/
|
||||
lintDriverArgs : Array String := #[]
|
||||
|
||||
/--
|
||||
The package version. Versions have the form:
|
||||
|
||||
```
|
||||
v!"<major>.<minor>.<patch>[-<specialDescr>]"
|
||||
```
|
||||
|
||||
A version with a `-` suffix is considered a "prerelease".
|
||||
|
||||
Lake suggest the following guidelines for incrementing versions:
|
||||
|
||||
* **Major version increment** *(e.g., v1.3.0 → v2.0.0)*
|
||||
Indicates significant breaking changes in the package.
|
||||
Package users are not expected to update to the new version
|
||||
without manual intervention.
|
||||
|
||||
* **Minor version increment** *(e.g., v1.3.0 → v1.4.0)*
|
||||
Denotes notable changes that are expected to be
|
||||
generally backwards compatible.
|
||||
Package users are expected to update to this version automatically
|
||||
and should be able to fix any breakages and/or warnings easily.
|
||||
|
||||
* **Patch version increment** *(e.g., v1.3.0 → v1.3.1)*
|
||||
Reserved for bug fixes and small touchups.
|
||||
Package users are expected to update automatically and should not expect
|
||||
significant breakage, except in the edge case of users relying on the
|
||||
behavior of patched bugs.
|
||||
|
||||
**Note that backwards-incompatible changes may occur at any version increment.**
|
||||
The is because the current nature of Lean (e.g., transitive imports,
|
||||
rich metaprogramming, reducibility in proofs), makes it infeasible to
|
||||
define a completely stable interface for a package.
|
||||
Instead, the different version levels indicate a change's intended significance
|
||||
and how difficult migration is expected to be.
|
||||
|
||||
Versions of form the `0.x.x` are considered development versions prior to
|
||||
first official release. Like prerelease, they are not expected to closely
|
||||
follow the above guidelines.
|
||||
|
||||
Packages without a defined version default to `0.0.0`.
|
||||
-/
|
||||
version : StdVer := v!"0.0.0"
|
||||
|
||||
/--
|
||||
Git tags of this package's repository that should be treated as versions.
|
||||
Package indices (e.g., Reservoir) can make use of this information to determine
|
||||
the Git revisions corresponding to released versions.
|
||||
|
||||
Defaults to tags that are "version-like".
|
||||
That is, start with a `v` and are followed by a digit.
|
||||
-/
|
||||
versionTags : StrPat := defaultVersionTags
|
||||
|
||||
/-- A short description for the package (e.g., for Reservoir). -/
|
||||
description : String := ""
|
||||
|
||||
/--
|
||||
Custom keywords associated with the package.
|
||||
Reservoir can make use of a package's keywords to group related packages
|
||||
together and make it easier for users to discover them.
|
||||
|
||||
Good keywords include the domain (e.g., `math`, `software-verification`,
|
||||
`devtool`), specific subtopics (e.g., `topology`, `cryptology`), and
|
||||
significant implementation details (e.g., `dsl`, `ffi`, `cli`).
|
||||
For instance, Lake's keywords could be `devtool`, `cli`, `dsl`,
|
||||
`package-manager`, `build-system`.
|
||||
-/
|
||||
keywords : Array String := #[]
|
||||
|
||||
/--
|
||||
A URL to information about the package.
|
||||
|
||||
Reservoir will already include a link to the package's GitHub repository
|
||||
(if the package is sourced from there). Thus, users are advised to specify
|
||||
something else for this link (if anything).
|
||||
-/
|
||||
homepage : String := ""
|
||||
|
||||
/--
|
||||
The package's license (if one).
|
||||
Should be a valid [SPDX License Expression][1].
|
||||
|
||||
Reservoir requires that packages uses an OSI-approved license to be
|
||||
included in its index, and currently only supports single identifier
|
||||
SPDX expressions. For, a list of OSI-approved SPDX license identifiers,
|
||||
see the [SPDX LIcense List][2].
|
||||
|
||||
[1]: https://spdx.github.io/spdx-spec/v3.0/annexes/SPDX-license-expressions/
|
||||
[2]: https://spdx.org/licenses/
|
||||
-/
|
||||
license : String := ""
|
||||
|
||||
/--
|
||||
Files containing licensing information for the package.
|
||||
|
||||
These should be the license files that users are expected to include when
|
||||
distributing package sources, which may be more then one file for some licenses.
|
||||
For example, the Apache 2.0 license requires the reproduction of a `NOTICE`
|
||||
file along with the license (if such a file exists).
|
||||
|
||||
Defaults to `#["LICENSE"]`.
|
||||
-/
|
||||
licenseFiles : Array FilePath := #["LICENSE"]
|
||||
|
||||
/--
|
||||
The path to the package's README.
|
||||
A README should be a markdown file containing an overview of the package.
|
||||
Reservoir displays the rendered HTML of this README on a package's page.
|
||||
|
||||
Defaults to `README.md`.
|
||||
-/
|
||||
readmeFile : FilePath := "README.md"
|
||||
|
||||
/--
|
||||
Whether Reservoir should include the package in its index.
|
||||
When set to `false`, Reservoir will not add the package to its index
|
||||
and will remove it if it was already there (when Reservoir is next updated).
|
||||
-/
|
||||
reservoir : Bool := true
|
||||
|
||||
|
||||
deriving Inhabited
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
@ -287,6 +459,50 @@ structure PostUpdateHookDecl where
|
|||
|
||||
namespace Package
|
||||
|
||||
/-- The package version. -/
|
||||
@[inline] def version (self : Package) : LeanVer :=
|
||||
self.config.version
|
||||
|
||||
/-- The package's `versionTags` configuration. -/
|
||||
@[inline] def versionTags (self : Package) : StrPat :=
|
||||
self.config.versionTags
|
||||
|
||||
/-- The package's `description` configuration. -/
|
||||
@[inline] def description (self : Package) : String :=
|
||||
self.config.description
|
||||
|
||||
/-- The package's `keywords` configuration. -/
|
||||
@[inline] def keywords (self : Package) : Array String :=
|
||||
self.config.keywords
|
||||
|
||||
/-- The package's `homepage` configuration. -/
|
||||
@[inline] def homepage (self : Package) : String :=
|
||||
self.config.homepage
|
||||
|
||||
/-- The package's `reservoir` configuration. -/
|
||||
@[inline] def reservoir (self : Package) : Bool :=
|
||||
self.config.reservoir
|
||||
|
||||
/-- The package's `license` configuration. -/
|
||||
@[inline] def license (self : Package) : String :=
|
||||
self.config.license
|
||||
|
||||
/-- The package's `licenseFiles` configuration. -/
|
||||
@[inline] def relLicenseFiles (self : Package) : Array FilePath :=
|
||||
self.config.licenseFiles
|
||||
|
||||
/-- The package's `dir` joined with each of its `relLicenseFiles`. -/
|
||||
@[inline] def licenseFiles (self : Package) : Array FilePath :=
|
||||
self.relLicenseFiles.map (self.dir / ·)
|
||||
|
||||
/-- The package's `readmeFile` configuration. -/
|
||||
@[inline] def relReadmeFile (self : Package) : FilePath :=
|
||||
self.config.readmeFile
|
||||
|
||||
/-- The package's `dir` joined with its `relReadmeFile`. -/
|
||||
@[inline] def readmeFile (self : Package) : FilePath :=
|
||||
self.dir / self.config.readmeFile
|
||||
|
||||
/-- The package's direct dependencies. -/
|
||||
@[inline] def deps (self : Package) : Array Package :=
|
||||
self.opaqueDeps.map (·.get)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ That is, Lake ignores the `-` suffix.
|
|||
- `"1.0.0"`: Switches to a semantic versioning scheme
|
||||
- `"1.1.0"`: Add optional `scope` package entry field
|
||||
-/
|
||||
@[inline] def Manifest.version : LeanVer := v!"1.1.0"
|
||||
@[inline] def Manifest.version : StdVer := v!"1.1.0"
|
||||
|
||||
/-- Manifest version `0.6.0` package entry. For backwards compatibility. -/
|
||||
inductive PackageEntryV6
|
||||
|
|
@ -195,7 +195,7 @@ protected def fromJson? (json : Json) : Except String Manifest := do
|
|||
let ver : SemVerCore ←
|
||||
match (← obj.get "version" : Json) with
|
||||
| (n : Nat) => pure {minor := n}
|
||||
| (s : String) => LeanVer.parse s
|
||||
| (s : String) => StdVer.parse s
|
||||
| ver => throw s!"unknown manifest version format '{ver}'; \
|
||||
you may need to update your 'lean-toolchain'"
|
||||
if ver.major > 1 then
|
||||
|
|
|
|||
|
|
@ -136,6 +136,30 @@ def decodeLeanOptions (v : Value) : Except (Array DecodeError) (Array LeanOption
|
|||
| v =>
|
||||
throw #[.mk v.ref "expected array or table"]
|
||||
|
||||
protected def StdVer.decodeToml (v : Value) : Except (Array DecodeError) LeanVer := do
|
||||
match StdVer.parse (← v.decodeString) with
|
||||
| .ok v => return v
|
||||
| .error e => throw #[.mk v.ref e]
|
||||
|
||||
instance : DecodeToml StdVer := ⟨(StdVer.decodeToml ·)⟩
|
||||
|
||||
protected def StrPat.decodeToml (v : Value) (presets : NameMap StrPat := {}) : Except (Array DecodeError) StrPat :=
|
||||
match v with
|
||||
| .array _ vs => .mem <$> decodeArray vs
|
||||
| .table r t => do
|
||||
if let some pre ← t.decode? `startsWith then
|
||||
return .startsWith pre
|
||||
else if let some name ← t.decode? `preset then
|
||||
if let some preset := presets.find? name then
|
||||
return preset
|
||||
else
|
||||
throw #[.mk r s!"unknown preset '{name}'"]
|
||||
else
|
||||
throw #[.mk r "expected 'startsWith' or 'preset'"]
|
||||
| v => throw #[.mk v.ref "expected array or table"]
|
||||
|
||||
instance : DecodeToml StrPat := ⟨(StrPat.decodeToml ·)⟩
|
||||
|
||||
/-! ## Configuration Decoders -/
|
||||
|
||||
protected def WorkspaceConfig.decodeToml (t : Table) : Except (Array DecodeError) WorkspaceConfig := ensureDecode do
|
||||
|
|
@ -182,13 +206,25 @@ protected def PackageConfig.decodeToml (t : Table) (ref := Syntax.missing) : Exc
|
|||
let testDriverArgs ← t.tryDecodeD `testDriverArgs #[]
|
||||
let lintDriver ← t.tryDecodeD `lintDriver ""
|
||||
let lintDriverArgs ← t.tryDecodeD `lintDriverArgs #[]
|
||||
let version : StdVer ← t.tryDecodeD `version v!"0.0.0"
|
||||
let versionTags ← optDecodeD defaultVersionTags (t.find? `versionTags)
|
||||
<| StrPat.decodeToml (presets := versionTagPresets)
|
||||
let description ← t.tryDecodeD `description ""
|
||||
let keywords ← t.tryDecodeD `keywords #[]
|
||||
let homepage ← t.tryDecodeD `homepage ""
|
||||
let license ← t.tryDecodeD `license ""
|
||||
let licenseFiles : Array FilePath ← t.tryDecodeD `licenseFiles #["LICENSE"]
|
||||
let readmeFile ← t.tryDecodeD `readmeFile "README.md"
|
||||
let reservoir ← t.tryDecodeD `reservoir true
|
||||
let toLeanConfig ← tryDecode <| LeanConfig.decodeToml t
|
||||
let toWorkspaceConfig ← tryDecode <| WorkspaceConfig.decodeToml t
|
||||
return {
|
||||
name, precompileModules, moreGlobalServerArgs,
|
||||
srcDir, buildDir, leanLibDir, nativeLibDir, binDir, irDir,
|
||||
name, precompileModules, moreGlobalServerArgs
|
||||
srcDir, buildDir, leanLibDir, nativeLibDir, binDir, irDir
|
||||
releaseRepo, buildArchive?, preferReleaseBuild
|
||||
testDriver, testDriverArgs, lintDriver, lintDriverArgs
|
||||
version, versionTags, description, keywords, homepage, reservoir
|
||||
license, licenseFiles, readmeFile
|
||||
toLeanConfig, toWorkspaceConfig
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,9 @@ def ppTable (t : Table) : String :=
|
|||
let (ts, fs) := t.items.foldl (init := ("", "")) fun (ts, fs) (k,v) =>
|
||||
match v with
|
||||
| .array _ vs =>
|
||||
if vs.all (· matches .table ..) then
|
||||
if vs.isEmpty then
|
||||
(ts.append s!"{ppKey k} = []\n", fs)
|
||||
else if vs.all (· matches .table ..) then
|
||||
let fs := vs.foldl (init := fs) fun s v =>
|
||||
match v with
|
||||
| .table _ t =>
|
||||
|
|
|
|||
|
|
@ -158,6 +158,10 @@ instance : DecodeToml Table := ⟨(·.decodeTable)⟩
|
|||
|
||||
end Value
|
||||
|
||||
def decodeKeyval [dec : DecodeToml α] (k : Name) (v : Value) : Except (Array DecodeError) α := do
|
||||
dec.decode v |>.mapError fun xs => xs.map fun x =>
|
||||
{x with msg := s!"key {ppKey k}: " ++ x.msg}
|
||||
|
||||
namespace Table
|
||||
|
||||
@[inline] def decodeValue (t : Table) (k : Name) (ref := Syntax.missing) : Except DecodeError Value := do
|
||||
|
|
@ -167,8 +171,10 @@ namespace Table
|
|||
|
||||
def decode [dec : DecodeToml α] (t : Table) (k : Name) (ref := Syntax.missing) : Except (Array DecodeError) α := do
|
||||
let a ← t.decodeValue k ref
|
||||
dec.decode a |>.mapError fun xs => xs.map fun x =>
|
||||
{x with msg := s!"key {ppKey k}: " ++ x.msg}
|
||||
decodeKeyval (dec := dec) k a
|
||||
|
||||
def decode? [dec : DecodeToml α] (t : Table) (k : Name) : Except (Array DecodeError) (Option α) := do
|
||||
t.find? k |>.mapM fun v => decodeKeyval (dec := dec) k v
|
||||
|
||||
def decodeNameMap [dec : DecodeToml α] (t : Toml.Table) : Except (Array DecodeError) (NameMap α) := do
|
||||
t.items.foldl (init := Except.ok {}) fun m (k,v) =>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ end Git
|
|||
structure GitRepo where
|
||||
dir : FilePath
|
||||
|
||||
instance : Coe FilePath GitRepo := ⟨(⟨·⟩)⟩
|
||||
instance : ToString GitRepo := ⟨(·.dir.toString)⟩
|
||||
|
||||
namespace GitRepo
|
||||
|
|
@ -97,6 +98,10 @@ def findRemoteRevision (repo : GitRepo) (rev? : Option String := none) (remote :
|
|||
@[inline] def revisionExists (rev : String) (repo : GitRepo) : BaseIO Bool := do
|
||||
repo.testGit #["rev-parse", "--verify", rev ++ "^{commit}"]
|
||||
|
||||
@[inline] def getTags (repo : GitRepo) : BaseIO (List String) := do
|
||||
let some out ← repo.captureGit? #["tag"] | return []
|
||||
return out.split (· == '\n')
|
||||
|
||||
@[inline] def findTag? (rev : String := "HEAD") (repo : GitRepo) : BaseIO (Option String) := do
|
||||
repo.captureGit? #["describe", "--tags", "--exact-match", rev]
|
||||
|
||||
|
|
|
|||
|
|
@ -60,18 +60,21 @@ instance : ToExpr SemVerCore where
|
|||
A Lean-style semantic version.
|
||||
A major-minor-patch triple with an optional arbitrary `-` suffix.
|
||||
-/
|
||||
structure LeanVer extends SemVerCore where
|
||||
structure StdVer extends SemVerCore where
|
||||
specialDescr : String := ""
|
||||
deriving Inhabited, Repr, DecidableEq
|
||||
|
||||
instance : Coe LeanVer SemVerCore := ⟨LeanVer.toSemVerCore⟩
|
||||
/-- A Lean version. -/
|
||||
abbrev LeanVer := StdVer
|
||||
|
||||
@[inline] protected def LeanVer.ofSemVerCore (ver : SemVerCore) : LeanVer :=
|
||||
instance : Coe StdVer SemVerCore := ⟨StdVer.toSemVerCore⟩
|
||||
|
||||
@[inline] protected def StdVer.ofSemVerCore (ver : SemVerCore) : StdVer :=
|
||||
{toSemVerCore := ver, specialDescr := ""}
|
||||
|
||||
instance : Coe SemVerCore LeanVer := ⟨LeanVer.ofSemVerCore⟩
|
||||
instance : Coe SemVerCore StdVer := ⟨StdVer.ofSemVerCore⟩
|
||||
|
||||
protected def LeanVer.compare (a b : LeanVer) : Ordering :=
|
||||
protected def StdVer.compare (a b : StdVer) : Ordering :=
|
||||
match compare a.toSemVerCore b.toSemVerCore with
|
||||
| .eq =>
|
||||
match a.specialDescr, b.specialDescr with
|
||||
|
|
@ -81,14 +84,14 @@ protected def LeanVer.compare (a b : LeanVer) : Ordering :=
|
|||
| a, b => compare a b
|
||||
| ord => ord
|
||||
|
||||
instance : Ord LeanVer := ⟨LeanVer.compare⟩
|
||||
instance : Ord StdVer := ⟨StdVer.compare⟩
|
||||
|
||||
instance : LT LeanVer := ltOfOrd
|
||||
instance : LE LeanVer := leOfOrd
|
||||
instance : Min LeanVer := minOfLe
|
||||
instance : Max LeanVer := maxOfLe
|
||||
instance : LT StdVer := ltOfOrd
|
||||
instance : LE StdVer := leOfOrd
|
||||
instance : Min StdVer := minOfLe
|
||||
instance : Max StdVer := maxOfLe
|
||||
|
||||
def LeanVer.parse (ver : String) : Except String LeanVer := do
|
||||
def StdVer.parse (ver : String) : Except String StdVer := do
|
||||
let sepPos := ver.find (· == '-')
|
||||
if h : ver.atEnd sepPos then
|
||||
SemVerCore.parse ver
|
||||
|
|
@ -99,20 +102,20 @@ def LeanVer.parse (ver : String) : Except String LeanVer := do
|
|||
throw "invalid Lean version: '-' suffix cannot be empty"
|
||||
return {toSemVerCore := core, specialDescr}
|
||||
|
||||
protected def LeanVer.toString (ver : LeanVer) : String :=
|
||||
protected def StdVer.toString (ver : StdVer) : String :=
|
||||
if ver.specialDescr.isEmpty then
|
||||
ver.toSemVerCore.toString
|
||||
else
|
||||
s!"{ver.toSemVerCore}-{ver.specialDescr}"
|
||||
|
||||
instance : ToString LeanVer := ⟨LeanVer.toString⟩
|
||||
instance : ToJson LeanVer := ⟨(·.toString)⟩
|
||||
instance : FromJson LeanVer := ⟨(do LeanVer.parse <| ← fromJson? ·)⟩
|
||||
instance : ToString StdVer := ⟨StdVer.toString⟩
|
||||
instance : ToJson StdVer := ⟨(·.toString)⟩
|
||||
instance : FromJson StdVer := ⟨(do StdVer.parse <| ← fromJson? ·)⟩
|
||||
|
||||
instance : ToExpr LeanVer where
|
||||
toExpr ver := mkAppN (mkConst ``LeanVer.mk)
|
||||
instance : ToExpr StdVer where
|
||||
toExpr ver := mkAppN (mkConst ``StdVer.mk)
|
||||
#[toExpr ver.toSemVerCore, toExpr ver.specialDescr]
|
||||
toTypeExpr := mkConst ``LeanVer
|
||||
toTypeExpr := mkConst ``StdVer
|
||||
|
||||
/-! ## Version Literals
|
||||
|
||||
|
|
@ -130,7 +133,7 @@ class DecodeVersion (α : Type u) where
|
|||
export DecodeVersion (decodeVersion)
|
||||
|
||||
instance : DecodeVersion SemVerCore := ⟨SemVerCore.parse⟩
|
||||
instance : DecodeVersion LeanVer := ⟨LeanVer.parse⟩
|
||||
@[default_instance] instance : DecodeVersion StdVer := ⟨StdVer.parse⟩
|
||||
|
||||
private def toResultExpr [ToExpr α] (x : Except String α) : Except String Expr :=
|
||||
Functor.map toExpr x
|
||||
|
|
|
|||
7
src/lake/tests/driver/build.lean
Normal file
7
src/lake/tests/driver/build.lean
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import Lake
|
||||
open System Lake DSL
|
||||
|
||||
package test
|
||||
|
||||
@[default_target]
|
||||
target foo : Unit := pure .nil
|
||||
2
src/lake/tests/driver/build.toml
Normal file
2
src/lake/tests/driver/build.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
name = "test"
|
||||
defaultTargets = ["bogus"]
|
||||
|
|
@ -84,3 +84,9 @@ $LAKE -f none.lean check-test && exit 1 || true
|
|||
$LAKE -f none.toml check-test && exit 1 || true
|
||||
$LAKE -f none.lean check-lint && exit 1 || true
|
||||
$LAKE -f none.toml check-lint && exit 1 || true
|
||||
|
||||
# Build checker
|
||||
$LAKE -f build.lean check-build
|
||||
$LAKE -f build.toml check-build
|
||||
$LAKE -f none.lean check-build && exit 1 || true
|
||||
$LAKE -f none.toml check-build && exit 1 || true
|
||||
|
|
|
|||
1
src/lake/tests/reservoirConfig/clean.sh
Executable file
1
src/lake/tests/reservoirConfig/clean.sh
Executable file
|
|
@ -0,0 +1 @@
|
|||
rm -rf .lake lake-manifest.json .git
|
||||
12
src/lake/tests/reservoirConfig/expected.json
Normal file
12
src/lake/tests/reservoirConfig/expected.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{"versionTags": ["v1", "v2"],
|
||||
"version": "0.0.0",
|
||||
"schemaVersion": "1.0.0",
|
||||
"readmeFile": null,
|
||||
"platformIndependent": null,
|
||||
"name": "test",
|
||||
"licenseFiles": [],
|
||||
"license": "",
|
||||
"keywords": ["test-case"],
|
||||
"homepage": "https://example.com",
|
||||
"doIndex": true,
|
||||
"description": "Tests Reservoir configuration"}
|
||||
7
src/lake/tests/reservoirConfig/lakefile.lean
Normal file
7
src/lake/tests/reservoirConfig/lakefile.lean
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import Lake
|
||||
open Lake DSL
|
||||
|
||||
package test where
|
||||
keywords := #["test-case"]
|
||||
homepage := "https://example.com"
|
||||
description := "Tests Reservoir configuration"
|
||||
23
src/lake/tests/reservoirConfig/test.sh
Executable file
23
src/lake/tests/reservoirConfig/test.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euxo pipefail
|
||||
|
||||
LAKE=${LAKE:-../../.lake/build/bin/lake}
|
||||
|
||||
./clean.sh
|
||||
|
||||
git init
|
||||
git checkout -b master
|
||||
git config user.name test
|
||||
git config user.email test@example.com
|
||||
git add --all
|
||||
git commit -m "commit 1"
|
||||
git tag v1
|
||||
git commit --allow-empty -m "commit 2"
|
||||
git tag v2
|
||||
git commit --allow-empty -m "commit 3"
|
||||
git tag etc
|
||||
|
||||
$LAKE reservoir-config | diff -u --strip-trailing-cr expected.json -
|
||||
|
||||
# Cleanup git repo
|
||||
rm -rf .git
|
||||
|
|
@ -31,6 +31,15 @@ package test where
|
|||
packagesDir := defaultPackagesDir
|
||||
leanOptions := #[⟨`pp.unicode.fun, true⟩]
|
||||
lintDriver := "b"
|
||||
version := v!"0.0.0"
|
||||
versionTags := fun _ : String => false
|
||||
description := ""
|
||||
keywords := #[]
|
||||
homepage := ""
|
||||
license := ""
|
||||
licenseFiles := #["LICENSE"]
|
||||
readmeFile := "README.md"
|
||||
reservoir := true
|
||||
|
||||
require "foo" / "baz" @ "git#abcdef"
|
||||
require foo from "dir" with NameMap.empty.insert `foo "bar"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,15 @@ moreLinkArgs = []
|
|||
weakLinkArgs = []
|
||||
backend = "default"
|
||||
platformIndependent = true
|
||||
version = "0.0.0"
|
||||
versionTags = {preset = "default"}
|
||||
description = ""
|
||||
keywords = []
|
||||
homepage = ""
|
||||
license = ""
|
||||
licenseFiles = ["LICENSE"]
|
||||
readmeFile = "README.md"
|
||||
reservoir = true
|
||||
|
||||
[[lean_lib]]
|
||||
name = "A"
|
||||
|
|
|
|||
1
src/lake/tests/versionTags/clean.sh
Executable file
1
src/lake/tests/versionTags/clean.sh
Executable file
|
|
@ -0,0 +1 @@
|
|||
rm -rf .lake lake-manifest.json .git
|
||||
4
src/lake/tests/versionTags/lakefile.lean
Normal file
4
src/lake/tests/versionTags/lakefile.lean
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import Lake
|
||||
open Lake DSL
|
||||
|
||||
package test
|
||||
27
src/lake/tests/versionTags/test.sh
Executable file
27
src/lake/tests/versionTags/test.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euxo pipefail
|
||||
|
||||
LAKE=${LAKE:-../../.lake/build/bin/lake}
|
||||
|
||||
./clean.sh
|
||||
|
||||
git init
|
||||
git checkout -b master
|
||||
git config user.name test
|
||||
git config user.email test@example.com
|
||||
git add --all
|
||||
git commit -m "commit 1"
|
||||
git tag v1
|
||||
git commit --allow-empty -m "commit 2"
|
||||
git tag v2
|
||||
git commit --allow-empty -m "commit 3"
|
||||
git tag etc
|
||||
|
||||
$LAKE version-tags | diff -u --strip-trailing-cr <(cat << 'EOF'
|
||||
v1
|
||||
v2
|
||||
EOF
|
||||
) -
|
||||
|
||||
# Cleanup git repo
|
||||
rm -rf .git
|
||||
Loading…
Add table
Reference in a new issue