diff --git a/debug.log b/debug.log new file mode 100644 index 000000000000..3e3495567796 --- /dev/null +++ b/debug.log @@ -0,0 +1 @@ +[0829/202002.254:ERROR:crashpad_client_win.cc(868)] not connected diff --git a/src/lake/Lake/CLI/Help.lean b/src/lake/Lake/CLI/Help.lean index 5bc0902d57c1..af988757a565 100644 --- a/src/lake/Lake/CLI/Help.lean +++ b/src/lake/Lake/CLI/Help.lean @@ -18,6 +18,7 @@ COMMANDS: init create a Lean package in the current directory build ... build targets exe ... 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 @@ -126,6 +127,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 @@ -375,6 +389,7 @@ def help : (cmd : String) → String | "new" => helpNew | "init" => helpInit | "build" => helpBuild +| "check-build" => helpCheckBuild | "update" | "upgrade" => helpUpdate | "pack" => helpPack | "unpack" => helpUnpack diff --git a/src/lake/Lake/CLI/Init.lean b/src/lake/Lake/CLI/Init.lean index 5ee656e089cd..cce91653967d 100644 --- a/src/lake/Lake/CLI/Init.lean +++ b/src/lake/Lake/CLI/Init.lean @@ -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] diff --git a/src/lake/Lake/CLI/Main.lean b/src/lake/Lake/CLI/Main.lean index 8e3b3df4f1c1..477951c30263 100644 --- a/src/lake/Lake/CLI/Main.lean +++ b/src/lake/Lake/CLI/Main.lean @@ -316,6 +316,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 @@ -478,6 +483,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) @@ -491,6 +556,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 @@ -510,6 +576,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 diff --git a/src/lake/Lake/CLI/Translate/Lean.lean b/src/lake/Lake/CLI/Translate/Lean.lean index 2f2ddc657418..a18d6426df70 100644 --- a/src/lake/Lake/CLI/Translate/Lean.lean +++ b/src/lake/Lake/CLI/Translate/Lean.lean @@ -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 diff --git a/src/lake/Lake/CLI/Translate/Toml.lean b/src/lake/Lake/CLI/Translate/Toml.lean index c35fe583bf8d..8a7bc9a55764 100644 --- a/src/lake/Lake/CLI/Translate/Toml.lean +++ b/src/lake/Lake/CLI/Translate/Toml.lean @@ -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)⟩ diff --git a/src/lake/Lake/Config/Package.lean b/src/lake/Lake/Config/Package.lean index 5d81785290c3..7e2b67d7cc2a 100644 --- a/src/lake/Lake/Config/Package.lean +++ b/src/lake/Lake/Config/Package.lean @@ -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!"..[-]" + ``` + + 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) diff --git a/src/lake/Lake/Load/Manifest.lean b/src/lake/Lake/Load/Manifest.lean index 6fee2fa8b680..9fad82437a60 100644 --- a/src/lake/Lake/Load/Manifest.lean +++ b/src/lake/Lake/Load/Manifest.lean @@ -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 diff --git a/src/lake/Lake/Load/Toml.lean b/src/lake/Lake/Load/Toml.lean index 02ad78e05d3e..519231a7c989 100644 --- a/src/lake/Lake/Load/Toml.lean +++ b/src/lake/Lake/Load/Toml.lean @@ -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 } diff --git a/src/lake/Lake/Toml/Data/Value.lean b/src/lake/Lake/Toml/Data/Value.lean index fb0618837d69..be64ef8d875d 100644 --- a/src/lake/Lake/Toml/Data/Value.lean +++ b/src/lake/Lake/Toml/Data/Value.lean @@ -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 => diff --git a/src/lake/Lake/Toml/Decode.lean b/src/lake/Lake/Toml/Decode.lean index 9a3837482541..5c2b3559448f 100644 --- a/src/lake/Lake/Toml/Decode.lean +++ b/src/lake/Lake/Toml/Decode.lean @@ -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) => diff --git a/src/lake/Lake/Util/Git.lean b/src/lake/Lake/Util/Git.lean index ef8333b0f00f..4aba5477b888 100644 --- a/src/lake/Lake/Util/Git.lean +++ b/src/lake/Lake/Util/Git.lean @@ -38,6 +38,7 @@ end Git structure GitRepo where dir : FilePath +instance : Coe FilePath GitRepo := ⟨(⟨·⟩)⟩ instance : ToString GitRepo := ⟨(·.dir.toString)⟩ namespace GitRepo @@ -101,6 +102,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] diff --git a/src/lake/Lake/Util/Version.lean b/src/lake/Lake/Util/Version.lean index 4a81f6102dd4..1a96a5d3644c 100644 --- a/src/lake/Lake/Util/Version.lean +++ b/src/lake/Lake/Util/Version.lean @@ -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 diff --git a/src/lake/tests/driver/build.lean b/src/lake/tests/driver/build.lean new file mode 100644 index 000000000000..b139f75ece72 --- /dev/null +++ b/src/lake/tests/driver/build.lean @@ -0,0 +1,7 @@ +import Lake +open System Lake DSL + +package test + +@[default_target] +target foo : Unit := pure .nil diff --git a/src/lake/tests/driver/build.toml b/src/lake/tests/driver/build.toml new file mode 100644 index 000000000000..a16b885e4dd3 --- /dev/null +++ b/src/lake/tests/driver/build.toml @@ -0,0 +1,2 @@ +name = "test" +defaultTargets = ["bogus"] diff --git a/src/lake/tests/driver/test.sh b/src/lake/tests/driver/test.sh index 29f1538f1072..38ef61d505ec 100755 --- a/src/lake/tests/driver/test.sh +++ b/src/lake/tests/driver/test.sh @@ -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 diff --git a/src/lake/tests/reservoirConfig/clean.sh b/src/lake/tests/reservoirConfig/clean.sh new file mode 100755 index 000000000000..8e49561ad81b --- /dev/null +++ b/src/lake/tests/reservoirConfig/clean.sh @@ -0,0 +1 @@ +rm -rf .lake lake-manifest.json .git diff --git a/src/lake/tests/reservoirConfig/expected.json b/src/lake/tests/reservoirConfig/expected.json new file mode 100644 index 000000000000..5f5a734ed823 --- /dev/null +++ b/src/lake/tests/reservoirConfig/expected.json @@ -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"} diff --git a/src/lake/tests/reservoirConfig/lakefile.lean b/src/lake/tests/reservoirConfig/lakefile.lean new file mode 100644 index 000000000000..47fd098fc94f --- /dev/null +++ b/src/lake/tests/reservoirConfig/lakefile.lean @@ -0,0 +1,7 @@ +import Lake +open Lake DSL + +package test where + keywords := #["test-case"] + homepage := "https://example.com" + description := "Tests Reservoir configuration" diff --git a/src/lake/tests/reservoirConfig/test.sh b/src/lake/tests/reservoirConfig/test.sh new file mode 100755 index 000000000000..bf47fa04c45d --- /dev/null +++ b/src/lake/tests/reservoirConfig/test.sh @@ -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 diff --git a/src/lake/tests/translateConfig/source.lean b/src/lake/tests/translateConfig/source.lean index 0ba45edf6e7f..a156ace83626 100644 --- a/src/lake/tests/translateConfig/source.lean +++ b/src/lake/tests/translateConfig/source.lean @@ -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" diff --git a/src/lake/tests/translateConfig/source.toml b/src/lake/tests/translateConfig/source.toml index 885c786c6930..6eadac1cfc9d 100644 --- a/src/lake/tests/translateConfig/source.toml +++ b/src/lake/tests/translateConfig/source.toml @@ -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" diff --git a/src/lake/tests/versionTags/clean.sh b/src/lake/tests/versionTags/clean.sh new file mode 100755 index 000000000000..8e49561ad81b --- /dev/null +++ b/src/lake/tests/versionTags/clean.sh @@ -0,0 +1 @@ +rm -rf .lake lake-manifest.json .git diff --git a/src/lake/tests/versionTags/lakefile.lean b/src/lake/tests/versionTags/lakefile.lean new file mode 100644 index 000000000000..eadfb1bf2b12 --- /dev/null +++ b/src/lake/tests/versionTags/lakefile.lean @@ -0,0 +1,4 @@ +import Lake +open Lake DSL + +package test diff --git a/src/lake/tests/versionTags/test.sh b/src/lake/tests/versionTags/test.sh new file mode 100755 index 000000000000..b223f5dcd522 --- /dev/null +++ b/src/lake/tests/versionTags/test.sh @@ -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