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:
Mac Malone 2024-09-02 12:07:08 -04:00 committed by GitHub
parent 830b1191b3
commit df9cdcd1b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 543 additions and 32 deletions

1
debug.log Normal file
View file

@ -0,0 +1 @@
[0829/202002.254:ERROR:crashpad_client_win.cc(868)] not connected

View file

@ -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

View file

@ -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]

View file

@ -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

View file

@ -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

View file

@ -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)⟩

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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 =>

View file

@ -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) =>

View file

@ -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]

View file

@ -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

View file

@ -0,0 +1,7 @@
import Lake
open System Lake DSL
package test
@[default_target]
target foo : Unit := pure .nil

View file

@ -0,0 +1,2 @@
name = "test"
defaultTargets = ["bogus"]

View file

@ -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

View file

@ -0,0 +1 @@
rm -rf .lake lake-manifest.json .git

View 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"}

View file

@ -0,0 +1,7 @@
import Lake
open Lake DSL
package test where
keywords := #["test-case"]
homepage := "https://example.com"
description := "Tests Reservoir configuration"

View 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

View file

@ -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"

View file

@ -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"

View file

@ -0,0 +1 @@
rm -rf .lake lake-manifest.json .git

View file

@ -0,0 +1,4 @@
import Lake
open Lake DSL
package test

View 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