refactor: touchup require syntax & docs (#4496)

A set of general tweaks of the `require` syntax and docs that provide a
base for #4495.

The sole significant behavioral change is that the `name` field of a
`require` in TOML now falls back to being interpreted as a simple string
name if the value is not a valid Lean identifier. This means that a
require for a package like `doc-gen4` can be written without French
quotes.
This commit is contained in:
Mac Malone 2024-06-18 22:49:57 -04:00 committed by GitHub
parent 1b5b91cccf
commit f9952e8c39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 158 additions and 76 deletions

View file

@ -163,15 +163,16 @@ protected def LeanExeConfig.mkSyntax
`(leanExeDecl|$[$attrs?:attributes]? lean_exe $(mkIdent cfg.name):ident $[$declVal?]?)
protected def Dependency.mkSyntax (cfg : Dependency) : RequireDecl := Unhygienic.run do
let src ←
match cfg.src with
| .path dir =>
`(fromSource|$(quote dir):term)
| .git url rev? subDir? =>
`(fromSource|git $(quote url) $[@ $(rev?.map quote)]? $[/ $(subDir?.map quote)]?)
let opts? := if cfg.opts.isEmpty then none else some <| Unhygienic.run do
cfg.opts.foldM (init := mkCIdent ``NameMap.empty) fun stx opt val =>
`($stx |>.insert $(quote opt) $(quote val))
match cfg.src with
| .path dir =>
`(requireDecl|require $(mkIdent cfg.name) from $(quote dir):term $[with $opts?]?)
| .git url rev? subDir? =>
`(requireDecl|require $(mkIdent cfg.name) from git $(quote url)
$[@ $(rev?.map quote)]? $[/ $(subDir?.map quote)]? $[with $opts?]?)
`(requireDecl|require $(mkIdent cfg.name):ident from $src $[with $opts?]?)
/-! ## Root Encoder -/

View file

@ -5,34 +5,46 @@ Authors: Gabriel Ebner, Sebastian Ullrich, Mac Malone
-/
import Lean.Data.NameMap
/- # Package Dependency Configuration
This module the defines the data types which encode a dependency of a
Lake package (i.e., the information contained in the `require` DSL syntax).
-/
open System Lean
namespace Lake
open Lean System
/--
The `src` of a `Dependency`.
In Lake, dependency sources currently come into flavors:
* Local `path`s relative to the package's directory.
* Remote `git` repositories that are download from a given `url`
into the workspace's `packagesDir`.
The source of a `Dependency`.
That is, where Lake should look to materialize the dependency.
-/
inductive Source where
inductive DependencySrc where
/- A package located a fixed path relative to the dependent package's directory. -/
| path (dir : FilePath)
/- A package cloned from a Git repository available at a fixed Git `url`. -/
| git (url : String) (rev : Option String) (subDir : Option FilePath)
deriving Inhabited, Repr
/-- A `Dependency` of a package. -/
/--
A `Dependency` of a package.
It specifies a package which another package depends on.
This encodes the information contained in the `require` DSL syntax.
-/
structure Dependency where
/--
A `Name` for the dependency.
The names of a package's dependencies cannot clash.
The package name of the dependency.
This name must match the one declared in its configuration file,
as that name is used to index its target data types. For this reason,
the package name must also be unique across packages in the dependency graph.
-/
name : Name
/--
The source of a dependency.
See the documentation of `Source` for more information.
See the documentation of `DependencySrc` for supported sources.
-/
src : Source
src : DependencySrc
/--
Arguments to pass to the dependency's package configuration.
-/

View file

@ -8,54 +8,96 @@ import Lake.Config.Dependency
import Lake.DSL.Extensions
import Lake.DSL.DeclUtil
namespace Lake.DSL
/-! # The `require` syntax
This module contains the macro definition of the `require` DSL syntax
used to specify package dependencies.
-/
open Lean Parser Command
namespace Lake.DSL
syntax fromPath :=
term
syntax fromGit :=
&" git " term:max ("@" term:max)? ("/" term)?
&"git " term:max ("@" term:max)? ("/" term)?
syntax depSpec :=
ident " from " (fromGit <|> fromPath) (" with " term)?
def expandDepSpec (stx : TSyntax ``depSpec) (doc? : Option DocComment) : MacroM Command := do
let depTy := mkCIdent ``Dependency
match stx with
| `(depSpec| $name:ident from git $url $[@ $rev?]? $[/ $path?]? $[with $opts?]?) =>
let rev ← match rev? with | some rev => `(some $rev) | none => `(none)
let path ← match path? with | some path => `(some $path) | none => `(none)
let opts := opts?.getD <| ← `({})
`($[$doc?:docComment]? @[package_dep] def $name : $depTy := {
name := $(quote name.getId),
src := Source.git $url $rev $path,
opts := $opts
})
| `(depSpec| $name:ident from $path:term $[with $opts?]?) => do
let opts := opts?.getD <| ← `({})
`($[$doc?:docComment]? @[package_dep] def $name : $depTy := {
name := $(quote name.getId),
src := Source.path $path,
opts := $opts
})
| _ => Macro.throwErrorAt stx "ill-formed require syntax"
syntax fromSource :=
fromGit <|> fromPath
/--
Adds a new package dependency to the workspace. Has two forms:
Specifies a specific source from which to draw the package dependency.
Dependencies that are downloaded from a remote source will be placed
into the workspace's `packagesDir`.
```lean
require foo from "path"/"to"/"local"/"package" with NameMap.empty
require bar from git "url.git"@"rev"/"optional"/"path-to"/"dir-with-pkg"
**Path Dependencies**
```
from <path>
```
Either form supports the optional `with` clause.
The `@"rev"` and `/"path"/"dir"` parts of the git form of `require`
are optional.
Lake loads the package located a fixed `path` relative to the
requiring package's directory.
The elements of both the `from` and `with` clauses are proper terms so
normal computation is supported within them (though parentheses made be
required to disambiguate the syntax).
**Git Dependencies**
```
from git <url> [@ <rev>] [/ <subDir>]
```
Lake clones the Git repository available at the specified fixed Git `url`,
and checks out the specified revision `rev`. The revision can be a commit hash,
branch, or tag. If none is provided, Lake defaults to `master`. After checkout,
Lake loads the package located in `subDir` (or the repository root if no
subdirectory is specified).
-/
syntax fromClause :=
" from " fromSource
syntax withClause :=
" with " term
syntax depSpec :=
identOrStr fromClause (withClause)?
@[inline] private def quoteOptTerm [Monad m] [MonadQuotation m] (term? : Option Term) : m Term :=
if let some term := term? then withRef term `(some $term) else `(none)
def expandDepSpec (stx : TSyntax ``depSpec) (doc? : Option DocComment) : MacroM Command := do
let `(depSpec| $nameStx from $src $[with $opts?]?) := stx
| Macro.throwErrorAt stx "ill-formed require syntax"
let src ←
match src with
| `(fromSource|git%$tk $url $[@ $rev?]? $[/ $subDir?]?) => withRef tk do
let rev ← quoteOptTerm rev?
let subDir ← quoteOptTerm subDir?
``(DependencySrc.git $url $rev $subDir)
| `(fromSource|$path:term) => withRef src do
``(DependencySrc.path $path)
| _ => Macro.throwErrorAt src "ill-formed from syntax"
let name := expandIdentOrStrAsIdent nameStx
`($[$doc?:docComment]? @[package_dep] def $name : $(mkCIdent ``Dependency) := {
name := $(quote name.getId),
src := $src,
opts := $(opts?.getD <| ← `({})),
})
/--
Adds a new package dependency to the workspace. The general syntax is:
```
require <pkg-name> from <source> [with <options>]
```
The `from` clause tells Lake where to locate the dependency.
See the `fromClause` syntax documentation (e.g., hover over it) to see
the different forms this clause can take.
The `with` clause specifies a `NameMap String` of Lake options
used to configure the dependency. This is equivalent to passing `-K`
options to the dependency on the command line.
-/
scoped macro (name := requireDecl)
doc?:(docComment)? kw:"require " spec:depSpec : command => withRef kw do

View file

@ -221,29 +221,29 @@ protected def LeanExeConfig.decodeToml (t : Table) (ref := Syntax.missing) : Exc
instance : DecodeToml LeanExeConfig := ⟨fun v => do LeanExeConfig.decodeToml (← v.decodeTable) v.ref⟩
protected def Source.decodeToml (t : Table) (ref := Syntax.missing) : Except (Array DecodeError) Source := do
protected def DependencySrc.decodeToml (t : Table) (ref := Syntax.missing) : Except (Array DecodeError) DependencySrc := do
let typeVal ← t.decodeValue `type
match (← typeVal.decodeString) with
| "path" =>
return Source.path (← t.decode `dir)
return .path (← t.decode `dir)
| "git" => ensureDecode do
return Source.git (← t.tryDecode `url ref) (← t.tryDecode? `rev) (← t.tryDecode? `subDir)
return .git (← t.tryDecode `url ref) (← t.tryDecode? `rev) (← t.tryDecode? `subDir)
| _ =>
throw #[DecodeError.mk typeVal.ref "expected one of 'path' or 'git'"]
instance : DecodeToml Source := ⟨fun v => do Source.decodeToml (← v.decodeTable) v.ref⟩
instance : DecodeToml DependencySrc := ⟨fun v => do DependencySrc.decodeToml (← v.decodeTable) v.ref⟩
protected def Dependency.decodeToml (t : Table) (ref := Syntax.missing) : Except (Array DecodeError) Dependency := ensureDecode do
let name t.tryDecode `name ref
let src ← id do
let name ← stringToLegalOrSimpleName <$> t.tryDecode `name ref
let src : DependencySrc ← id do
if let some dir ← t.tryDecode? `path then
return Source.path dir
return .path dir
else if let some g := t.find? `git then
match g with
| .string _ url =>
return Source.git url (← t.tryDecode? `rev) (← t.tryDecode? `subDir)
return .git url (← t.tryDecode? `rev) (← t.tryDecode? `subDir)
| .table ref t =>
return Source.git (← t.tryDecode `url ref) (← t.tryDecode? `rev) (← t.tryDecode? `subDir)
return .git (← t.tryDecode `url ref) (← t.tryDecode? `rev) (← t.tryDecode? `subDir)
| _ =>
modify (·.push <| .mk g.ref "expected string or table")
return default

View file

@ -24,7 +24,9 @@ Each `lakefile.lean` includes a `package` declaration (akin to `main`) which def
+ [Custom Targets](#custom-targets)
* [Defining New Facets](#defining-new-facets)
* [Adding Dependencies](#adding-dependencies)
+ [Syntax of `require`](#syntax-of-require)
+ [Lean `require`](#lean-require)
+ [Supported Sources](#supported-sources)
+ [TOML `require`](#toml-require)
* [GitHub Release Builds](#github-release-builds)
* [Writing and Running Scripts](#writing-and-running-scripts)
* [Building and Running Lake from the Source](#building-and-running-lake-from-the-source)
@ -336,30 +338,55 @@ For theorem proving packages which depend on `mathlib`, you can also run `lake n
**NOTE:** For mathlib in particular, you should run `lake exe cache get` prior to a `lake build` after adding or updating a mathlib dependency. Otherwise, it will be rebuilt from scratch (which can take hours). For more information, see mathlib's [wiki page](https://github.com/leanprover-community/mathlib4/wiki/Using-mathlib4-as-a-dependency) on using it as a dependency.
### Syntax of `require`
## Lean `require`
The `require` command has two forms:
The `require` command in Lean Lake configuration follows the general syntax:
```lean
require foo from "path"/"to"/"local"/"package" with NameMap.empty
require bar from git "url.git"@"rev"/"optional"/"path-to"/"dir-with-pkg"
require <pkg-name> from <source> [with <options>]
```
The first form adds a local dependency and the second form adds a Git dependency. For a Git dependency, the revision can be a commit hash, branch, or tag. Also, the `@"rev"` and `/"path-to"/"term"` parts of the `require` are optional.
The `from` clause tells Lake where to locate the dependency.
The `with` clause specifies a `NameMap String` of Lake options used to configure the dependency. This is equivalent to passing `-K` options to the dependency on the command line.
Both forms also support an optional `with` clause to specify arguments to pass to the dependency's package configuration (i.e., same as `args` in a `lake build -- <args...>` invocation). The elements of both the `from` and `with` clauses are proper terms so normal computation is supported within them (though parentheses made be required to disambiguate the syntax).
## Supported Sources
To `require` a package in a TOML configuration, the equivalent syntax is:
Lake supports the following types of dependencies as sources in a `from` clause.
### Path Dependencies
```
from <path>
```
Lake loads the package located a fixed `path` relative to the requiring package's directory.
### Git Dependencies
```
from git <url> [@ <rev>] [/ <subDir>]
```
Lake clones the Git repository available at the specified fixed Git `url`, and checks out the specified revision `rev`. The revision can be a commit hash, branch, or tag. If none is provided, Lake defaults to `master`. After checkout, Lake loads the package located in `subDir` (or the repository root if no subdirectory is specified).
## TOML `require`
To `require` a package in a TOML configuration, the parallel syntax for the above examples is:
```toml
# A path dependency
[[require]]
path = "path/to/local/package"
options = {}
name = "<pkg-name>"
path = "<path>"
options = {<options>}
# A Git dependency
[[require]]
git = "url.git"
rev = "rev"
subDir = "optional/path-to/dir-with-pkg"
name = "<pkg-name>"
git = "<url>"
rev = "<rev>"
subDir = "<subDir>"
options = {<options>}
```
## GitHub Release Builds