Skip to content

Commit 343328b

Browse files
authored
feat: lake: rename dependencies (#10452)
This PR refactors Lake's package naming procedure to allow packages to be renamed by the consumer. With this, users can now require a package using a different name than the one it was defined with. This is support will be used in the future to enable seamlessly including the same package at multiple different versions within the same workspace. In a Lake package configuration file written in Lean, the current package's assigned name is now accessed through `__name__` instead of the previous `_package.name`. A deprecation warning has been added to `_package.name` to assist in migration.
1 parent 5b9befc commit 343328b

File tree

21 files changed

+175
-120
lines changed

21 files changed

+175
-120
lines changed

src/lake/Lake/CLI/Translate/Lean.lean

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,13 @@ def Dependency.mkRequire (cfg : Dependency) : RequireDecl := Unhygienic.run do
279279

280280
private meta def genMkDeclFields
281281
(cmds : Array Command)
282-
(tyName : Name) [info : ConfigInfo tyName] (takesName : Bool)
282+
(tyName : Name) [info : ConfigInfo tyName]
283283
(exclude : Array Name := #[])
284284
: MacroM (Array Command) := do
285285
let val ← `(fs)
286-
let ty := if takesName then Syntax.mkCApp tyName #[mkIdent `n] else mkCIdent tyName
286+
let tyArgs := info.arity.fold (init := Array.emptyWithCapacity info.arity) fun i _ as =>
287+
as.push (mkIdent <| .mkSimple s!"x_{i+1}")
288+
let ty := Syntax.mkCApp tyName tyArgs
287289
let val ← info.fields.foldlM (init := val) fun val {name, canonical, ..} => do
288290
if !canonical || exclude.contains name then
289291
return val
@@ -298,21 +300,21 @@ private meta def genMkDeclFields
298300
local macro "gen_lean_encoders%" : command => do
299301
let cmds := #[]
300302
-- Targets
301-
let cmds ← genMkDeclFields cmds ``LeanConfig false
302-
let cmds ← genMkDeclFields cmds ``LeanLibConfig true
303+
let cmds ← genMkDeclFields cmds ``LeanConfig
304+
let cmds ← genMkDeclFields cmds ``LeanLibConfig
303305
(exclude := #[`nativeFacets])
304-
let cmds ← genMkDeclFields cmds ``LeanExeConfig true
306+
let cmds ← genMkDeclFields cmds ``LeanExeConfig
305307
(exclude := #[`nativeFacets])
306-
let cmds ← genMkDeclFields cmds ``InputFileConfig true
307-
let cmds ← genMkDeclFields cmds ``InputDirConfig true
308+
let cmds ← genMkDeclFields cmds ``InputFileConfig
309+
let cmds ← genMkDeclFields cmds ``InputDirConfig
308310
-- Package
309-
let cmds ← genMkDeclFields cmds ``WorkspaceConfig false
310-
let cmds ← genMkDeclFields cmds ``PackageConfig true
311+
let cmds ← genMkDeclFields cmds ``WorkspaceConfig
312+
let cmds ← genMkDeclFields cmds ``PackageConfig
311313
return ⟨mkNullNode cmds⟩
312314

313315
gen_lean_encoders%
314316

315-
def PackageConfig.mkCommand (cfg : PackageConfig n) : PackageCommand := Unhygienic.run do
317+
def PackageConfig.mkCommand (cfg : PackageConfig p n) : PackageCommand := Unhygienic.run do
316318
let declVal? := mkDeclValWhere? (mkDeclFields cfg)
317319
`(packageCommand|package $(mkIdent n):ident $[$declVal?]?)
318320

@@ -355,7 +357,7 @@ protected def InputDirConfig.mkCommand
355357

356358
/-- Create a Lean module that encodes the declarative configuration of the package. -/
357359
public def Package.mkLeanConfig (pkg : Package) : TSyntax ``module := Unhygienic.run do
358-
let pkgConfig : PackageConfig pkg.name :=
360+
let pkgConfig : PackageConfig pkg.name pkg.origName :=
359361
{pkg.config with testDriver := pkg.testDriver, lintDriver := pkg.lintDriver}
360362
let defaultTargets := pkg.defaultTargets.foldl NameSet.insert NameSet.empty
361363
`(module|

src/lake/Lake/CLI/Translate/Toml.lean

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,14 @@ public instance : ToToml Dependency := ⟨(toToml ·.toToml)⟩
151151

152152
private meta def genToToml
153153
(cmds : Array Command)
154-
(tyName : Name) [info : ConfigInfo tyName] (takesName : Bool)
154+
(tyName : Name) [info : ConfigInfo tyName]
155155
(exclude : Array Name := #[])
156156
: MacroM (Array Command) := do
157-
let val ← if takesName then `(t.insert `name $(mkIdent `n)) else `(t)
158-
let ty := if takesName then Syntax.mkCApp tyName #[mkIdent `n] else mkCIdent tyName
157+
let val ← if info.arity == 0 then `(t) else
158+
`(t.insert `name $(mkIdent <| .mkSimple s!"x_{info.arity}"))
159+
let tyArgs := info.arity.fold (init := Array.emptyWithCapacity info.arity) fun i _ as =>
160+
as.push (mkIdent <| .mkSimple s!"x_{i+1}")
161+
let ty := Syntax.mkCApp tyName tyArgs
159162
let val ← info.fields.foldlM (init := val) fun val {name, canonical, ..} => do
160163
if !canonical || exclude.contains name then
161164
return val
@@ -170,16 +173,16 @@ private meta def genToToml
170173
local macro "gen_toml_encoders%" : command => do
171174
let cmds := #[]
172175
-- Targets
173-
let cmds ← genToToml cmds ``LeanConfig false
174-
let cmds ← genToToml cmds ``LeanLibConfig true
176+
let cmds ← genToToml cmds ``LeanConfig
177+
let cmds ← genToToml cmds ``LeanLibConfig
175178
(exclude := #[`nativeFacets])
176-
let cmds ← genToToml cmds ``LeanExeConfig true
179+
let cmds ← genToToml cmds ``LeanExeConfig
177180
(exclude := #[`nativeFacets])
178-
let cmds ← genToToml cmds ``InputFileConfig true
179-
let cmds ← genToToml cmds ``InputDirConfig true
181+
let cmds ← genToToml cmds ``InputFileConfig
182+
let cmds ← genToToml cmds ``InputDirConfig
180183
-- Package
181-
let cmds ← genToToml cmds ``WorkspaceConfig false
182-
let cmds ← genToToml cmds ``PackageConfig true
184+
let cmds ← genToToml cmds ``WorkspaceConfig
185+
let cmds ← genToToml cmds ``PackageConfig
183186
return ⟨mkNullNode cmds⟩
184187

185188
gen_toml_encoders%
@@ -194,7 +197,7 @@ gen_toml_encoders%
194197

195198
/-- Create a TOML table that encodes the declarative configuration of the package. -/
196199
public def Package.mkTomlConfig (pkg : Package) (t : Table := {}) : Table :=
197-
let cfg : PackageConfig pkg.name :=
200+
let cfg : PackageConfig pkg.name pkg.origName :=
198201
{pkg.config with testDriver := pkg.testDriver, lintDriver := pkg.lintDriver}
199202
cfg.toToml t
200203
|>.smartInsert `defaultTargets pkg.defaultTargets

src/lake/Lake/Config/Meta.lean

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ set_option internal.parseQuotWithCurrentStage false
5757

5858
private meta def mkConfigAuxDecls
5959
(vis? : Option (TSyntax ``visibility))
60-
(structId : Ident) (structTy : Term) (views : Array FieldView)
60+
(structId : Ident) (structArity : Nat) (structTy : Term) (views : Array FieldView)
6161
: MacroM (Array Command) := do
6262
let data : FieldMetadata := {}
6363
-- `..` is used to avoid missing pattern error from an incomplete match.
@@ -113,7 +113,8 @@ private meta def mkConfigAuxDecls
113113
let fieldsInst ← `( $[$vis?:visibility]? instance $instId:ident : ConfigFields $structTy := ⟨$fieldsId⟩)
114114
let instId := mkIdentFrom structId <| structId.getId.modifyBase (·.str "instConfigInfo")
115115
let structNameLit : Term := ⟨mkNode ``Term.doubleQuotedName #[mkAtom "`", mkAtom "`", structId]⟩
116-
let infoInst ← `( $[$vis?:visibility]? instance $instId:ident : ConfigInfo $structNameLit := {fields := $fieldsId})
116+
let info ← `({fields := $fieldsId, arity := $(quote structArity)})
117+
let infoInst ← `( $[$vis?:visibility]? instance $instId:ident : ConfigInfo $structNameLit := $info)
117118
let instId := mkIdentFrom structId <| structId.getId.modifyBase (·.str "instEmptyCollection")
118119
let emptyInst ← `( $[$vis?:visibility]? instance $instId:ident : EmptyCollection $structTy := ⟨{}⟩)
119120
return data.cmds.push fieldsDef |>.push fieldsInst |>.push infoInst |>.push emptyInst
@@ -166,6 +167,6 @@ public meta def expandConfigDecl : Macro := fun stx => do
166167
extends $ps,* $(xty?.join)? where $(ctor?.join)? $fields* $drv:optDeriving
167168
)
168169
let vis? := mods.raw[2].getOptional?.map (⟨·⟩)
169-
let auxDecls ← mkConfigAuxDecls vis? structId structTy views
170+
let auxDecls ← mkConfigAuxDecls vis? structId bs.size structTy views
170171
let cmds := #[struct] ++ auxDecls
171172
return mkNullNode cmds

src/lake/Lake/Config/MetaClasses.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class ConfigInfo (name : Name) where
4242
fields : Array ConfigFieldInfo
4343
fieldMap : NameMap ConfigFieldInfo :=
4444
fields.foldl (init := ∅) fun m i => m.insert i.name i
45+
arity : Nat
4546

4647
public instance [parent : ConfigParent σ ρ] [field : ConfigField ρ name α] : ConfigField σ name α where
4748
mkDefault s := field.mkDefault (parent.get s)

src/lake/Lake/Config/Package.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ public nonempty_type OpaquePostUpdateHook (pkg : Name)
2626
public structure Package where
2727
/-- The name of the package. -/
2828
name : Name
29+
/-- The name specified by the package. -/
30+
origName : Name
2931
/-- The absolute path to the package's directory. -/
3032
dir : FilePath
3133
/-- The path to the package's directory relative to the workspace. -/
3234
relDir : FilePath
3335
/-- The package's user-defined configuration. -/
34-
config : PackageConfig name
36+
config : PackageConfig name origName
3537
/-- The absolute path to the package's configuration file. -/
3638
configFile : FilePath
3739
/-- The path to the package's configuration file (relative to `dir`). -/

src/lake/Lake/Config/PackageConfig.lean

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace Lake
2424

2525
set_option linter.unusedVariables false in
2626
/-- A `Package`'s declarative configuration. -/
27-
public configuration PackageConfig (name : Name) extends WorkspaceConfig, LeanConfig where
27+
public configuration PackageConfig (p : Name) (n : Name) extends WorkspaceConfig, LeanConfig where
2828
/-- **For internal use.** Whether this package is Lean itself. -/
2929
bootstrap : Bool := false
3030

@@ -306,11 +306,12 @@ public configuration PackageConfig (name : Name) extends WorkspaceConfig, LeanCo
306306
libPrefixOnWindows : Bool := false
307307
deriving Inhabited
308308

309-
/-- The package's name. -/
310-
public abbrev PackageConfig.name (_ : PackageConfig n) := n
309+
/-- The package's name as specified by the author. -/
310+
public abbrev PackageConfig.origName (_ : PackageConfig p n) := n
311311

312312
/-- A package declaration from a configuration written in Lean. -/
313313
public structure PackageDecl where
314314
name : Name
315-
config : PackageConfig name
315+
origName : Name
316+
config : PackageConfig name origName
316317
deriving TypeName

src/lake/Lake/DSL/Config.lean

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,28 @@ prelude
99
public import Lean.Elab.Term
1010
import Lake.DSL.Extensions
1111
import Lake.DSL.Syntax
12+
import Lake.Util.Name
1213

1314
namespace Lake.DSL
1415
open Lean Elab Term
1516

17+
@[builtin_term_elab nameConst]
18+
def elabNameConst : TermElab := fun stx expectedType? => do
19+
let env ← getEnv
20+
unless env.contains packageDeclName do
21+
throwError "`__name__` can only be used after the `package` declaration"
22+
let exp :=
23+
match nameExt.getState env with
24+
| .anonymous => mkIdent <| packageDeclName.str "origName"
25+
| name => Name.quoteFrom stx name
26+
withMacroExpansion stx exp <| elabTerm exp expectedType?
27+
1628
/--
1729
A dummy default constant for `__dir__` to make it type check
1830
outside Lakefile elaboration (e.g., when editing).
1931
-/
2032
public opaque dummyDir : System.FilePath
2133

22-
/--
23-
A dummy default constant for `get_config` to make it type check
24-
outside Lakefile elaboration (e.g., when editing).
25-
-/
26-
public opaque dummyGetConfig? : Name → Option String
27-
2834
@[builtin_term_elab dirConst]
2935
def elabDirConst : TermElab := fun stx expectedType? => do
3036
let exp :=
@@ -36,6 +42,12 @@ def elabDirConst : TermElab := fun stx expectedType? => do
3642
Syntax.mkApp (mkCIdentFrom stx ``id) #[mkCIdentFrom stx ``dummyDir]
3743
withMacroExpansion stx exp <| elabTerm exp expectedType?
3844

45+
/--
46+
A dummy default constant for `get_config` to make it type check
47+
outside Lakefile elaboration (e.g., when editing).
48+
-/
49+
public opaque dummyGetConfig? : Name → Option String
50+
3951
@[builtin_term_elab getConfig]
4052
def elabGetConfig : TermElab := fun stx expectedType? => do
4153
tryPostponeIfNoneOrMVar expectedType?

src/lake/Lake/DSL/Extensions.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ open Lean
1212

1313
namespace Lake
1414

15+
public builtin_initialize nameExt : EnvExtension Name ←
16+
registerEnvExtension (pure .anonymous)
17+
1518
public builtin_initialize dirExt : EnvExtension (Option System.FilePath) ←
1619
registerEnvExtension (pure none)
1720

src/lake/Lake/DSL/Package.lean

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ prelude
99
public import Lake.DSL.Syntax
1010
import Lake.Config.Package
1111
import Lake.DSL.Attributes
12+
import Lake.DSL.Extensions
1213
import Lake.DSL.DeclUtil
1314

1415
open Lean Parser Elab Command
@@ -26,15 +27,24 @@ def elabPackageCommand : CommandElab := fun stx => do
2627
withRef kw do
2728
let configId : Ident ← `(pkgConfig)
2829
let id ← mkConfigDeclIdent nameStx?
29-
let name := Name.quoteFrom id id.getId
30-
let ty := Syntax.mkCApp ``PackageConfig #[name]
30+
let origName := Name.quoteFrom id id.getId
31+
let name :=
32+
match nameExt.getState (← getEnv) with
33+
| .anonymous => origName
34+
| name => Name.quoteFrom id name
35+
let ty := Syntax.mkCApp ``PackageConfig #[name, origName]
3136
elabConfig ``PackageConfig configId ty cfg
3237
let attr ← `(Term.attrInstance| «package»)
3338
let attrs := #[attr] ++ expandAttrs attrs?
3439
let id := mkIdentFrom id packageDeclName
35-
let decl ← `({name := $name, config := $configId})
40+
let decl ← `({name := $name, origName := $origName, config := $configId})
3641
let cmd ← `($[$doc?]? @[$attrs,*] abbrev $id : PackageDecl := $decl)
3742
withMacroExpansion stx cmd <| elabCommand cmd
43+
let nameId := mkIdentFrom id <| packageDeclName.str "name"
44+
elabCommand <| ← `(
45+
@[deprecated "Use `__name__` instead." (since := "2025-09-18")]
46+
abbrev $nameId := __name__
47+
)
3848

3949
@[builtin_macro postUpdateDecl]
4050
def expandPostUpdateDecl : Macro := fun stx => do
@@ -43,9 +53,8 @@ def expandPostUpdateDecl : Macro := fun stx => do
4353
`($[$doc?]? $[$attrs?]? post_update%$kw $[$pkg?]? := do $seq $[$wds?:whereDecls]?)
4454
| `($[$doc?]? $[$attrs?]? post_update%$kw $[$pkg?]? := $defn $[$wds?:whereDecls]?) => withRef kw do
4555
let pkg ← expandOptSimpleBinder pkg?
46-
let pkgName := mkIdentFrom pkg `_package.name
4756
let attr ← `(Term.attrInstance| «post_update»)
4857
let attrs := #[attr] ++ expandAttrs attrs?
4958
`($[$doc?]? @[$attrs,*] def postUpdateHook : PostUpdateHookDecl :=
50-
{pkg := $pkgName, fn := fun $pkg => $defn} $[$wds?:whereDecls]?)
59+
{pkg := __name__, fn := fun $pkg => $defn} $[$wds?:whereDecls]?)
5160
| stx => Macro.throwErrorAt stx "ill-formed post_update declaration"

src/lake/Lake/DSL/Syntax.lean

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ allows the reference manual to document the DSL syntax.
1818

1919
namespace Lake.DSL
2020

21+
/--
22+
A macro that expands to the assigned name of package
23+
during the Lakefile's elaboration.
24+
-/
25+
scoped syntax (name := nameConst) "__name__" : term
26+
2127
/--
2228
A macro that expands to the path of package's directory
2329
during the Lakefile's elaboration.

0 commit comments

Comments
 (0)