Skip to content

Commit 2117d14

Browse files
committed
feat(Cache): Allow arguments of the form Mathlib.Data.+ which correspond to a folder but not a file (#35414)
This PR continues the work from #21838. Original PR: #21838
1 parent 33cae85 commit 2117d14

2 files changed

Lines changed: 74 additions & 31 deletions

File tree

Cache/IO.lean

Lines changed: 70 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ Authors: Arthur Paulino, Jon Eugster
55
-/
66
import Std.Data.TreeSet
77
import Cache.Lean
8+
import Lake.Load.Toml
9+
import Batteries.Tactic.OpenPrivate
810

911
variable {α : Type}
1012

1113
open Lean
12-
1314
namespace Cache.IO
1415

1516
open System (FilePath)
@@ -483,22 +484,27 @@ def lookup (hashMap : ModuleHashMap) (modules : List Name) : IO Unit := do
483484
println! " comment: {line}"
484485
if err then IO.Process.exit 1
485486

487+
open private Lake.Glob.ofString? from Lake.Load.Toml in
488+
486489
/--
487490
Parse a string as either a path or a Lean module name.
488-
TODO: If the argument describes a folder, use `walkDir` to find all `.lean` files within.
491+
If the argument describes a folder, use `walkDir` to find all `.lean` files within.
489492
490493
Return tuples of the form ("module name", "path to .lean file").
491494
492495
The input string `arg` takes one of the following forms:
493496
494497
1. `Mathlib.Algebra.Field.Basic`: there exists such a Lean file
495-
2. `Mathlib.Algebra.Field`: no Lean file exists but a folder (TODO)
496-
3. `Mathlib/Algebra/Field/Basic.lean`: the file exists (note potentially `\` on Windows)
497-
4. `Mathlib/Algebra/Field/`: the folder exists (TODO)
498+
2. `Mathlib.Algebra.Field.+`: no Lean file exists but a folder `Field`
499+
3. `Mathlib.Algebra.Field.*`: either a file or a folder
500+
(note in some shells escaping as `.\*` might be necessary)
501+
4. `Mathlib/Algebra/Field/Basic.lean`: the file exists
502+
(note potentially `\` on Windows)
503+
5. `Mathlib/Algebra/Field/`: the folder exists
498504
499505
Not supported yet:
500506
501-
5. `Aesop/Builder.lean`: the file does not exist, it's actually somewhere in `.lake`.
507+
6. `Aesop/Builder.lean`: the file does not exist, it's actually somewhere in `.lake`.
502508
503509
Note: An argument like `Archive` is treated as module, not a path.
504510
-/
@@ -514,39 +520,73 @@ def leanModulesFromSpec (sp : SearchPath) (argₛ : String) :
514520
-- provided file name of a Lean file
515521
let mod : Name := arg.withExtension "" |>.components.foldl .str .anonymous
516522
if !(← arg.pathExists) then
517-
-- TODO: (5.) We could use `getSrcDir` to allow arguments like `Aesop/Builder.lean` which
523+
-- TODO: (6.) We could use `getSrcDir` to allow arguments like `Aesop/Builder.lean` which
518524
-- refer to a file located under `.lake/packages/...`
519525
return .error s!"Invalid argument: non-existing path {arg}"
520526
if arg.extension == "lean" then
521-
-- (3.) provided existing `.lean` file
527+
-- (4.) provided existing `.lean` file
522528
return .ok #[(mod, arg)]
523529
else
524-
-- (4.) provided existing directory: walk it
525-
return .error "Searching lean files in a folder is not supported yet!"
530+
-- (5.) provided existing directory: walk it
531+
IO.println s!"Searching directory {arg} for .lean files"
532+
let leanModulesInFolder ← walkDir sp arg mod
533+
return .ok leanModulesInFolder
526534
else
527535
-- provided a module
528-
let mod := argₛ.toName
529-
if mod.isAnonymous then
530-
-- provided a module name which is not a valid Lean identifier
536+
-- user might provide `.*` or `.+` to include folders
537+
match Lake.Glob.ofString? argₛ with
538+
| none =>
531539
return .error s!"Invalid argument: expected path or module name, not {argₛ}"
532-
let sourceFile ← Lean.findLean sp mod
533-
if ← sourceFile.pathExists then
534-
-- (1.) provided valid module
535-
return .ok #[(mod, sourceFile)]
536-
else
537-
-- provided "pseudo-module" (like `Mathlib.Data`) which
538-
-- does not correspond to a Lean file, but to an existing folder
539-
-- `Mathlib/Data/`
540-
let folder := sourceFile.withExtension ""
541-
IO.println s!"Searching directory {folder} for .lean files"
542-
if ← folder.pathExists then
543-
-- (2.) provided "module name" of an existing folder: walk dir
544-
-- TODO: will be implemented in https://github.com/leanprover-community/mathlib4/issues/21838
545-
return .error "Entering a part of a module name \
546-
(i.e. `Mathlib.Data` when only the folder `Mathlib/Data/` but no \
547-
file `Mathlib/Data.lean` exists) is not supported yet!"
540+
| some glob =>
541+
let modules ← match glob with
542+
| .one mod =>
543+
let sourceFile ← Lean.findLean sp mod
544+
pure #[(mod, sourceFile)]
545+
| .submodules mod =>
546+
let sourceFile ← Lean.findLean sp mod
547+
let folder := sourceFile.withExtension ""
548+
if ← folder.pathExists then
549+
IO.println s!"Searching directory {folder} for .lean files"
550+
let leanModulesInFolder ← walkDir sp folder mod
551+
pure leanModulesInFolder
552+
else
553+
pure #[]
554+
| .andSubmodules mod =>
555+
let sourceFile ← Lean.findLean sp mod
556+
let folder := sourceFile.withExtension ""
557+
if ← folder.pathExists then
558+
IO.println s!"Searching directory {folder} for .lean files"
559+
let leanModulesInFolder ← walkDir sp folder mod
560+
pure <| #[(mod, sourceFile)] ++ leanModulesInFolder
561+
else
562+
pure #[]
563+
if !modules.isEmpty then
564+
return .ok modules
548565
else
549-
return .error s!"Invalid argument: non-existing module {mod}"
566+
return .error s!"Invalid argument: {argₛ}"
567+
568+
where
569+
/--
570+
Search all `.lean` files inside `folder`.
571+
572+
In order to figure out the module name corresponding
573+
to the found files, we use `mod` and the search path `sp` to figure out how much of
574+
the relative path needs to be trimmed.
575+
576+
This assumes the `folder` exists.
577+
-/
578+
walkDir (sp : SearchPath) (folder : FilePath) (mod : Name) : IO <| Array (Name × FilePath) := do
579+
-- The source direcory where `mod` is located
580+
let srcDir ← getSrcDir sp mod
581+
-- find all Lean files in the folder only skipping special entries such as `.` and `..`
582+
let files ← folder.walkDir (pure ·.fileName.isSome)
583+
let leanFiles := files.filter (·.extension == some "lean")
584+
let mut leanModulesInFolder : Array (Name × FilePath) := #[]
585+
for file in leanFiles do
586+
let path := file.withoutParent srcDir
587+
let mod : Name := path.withExtension "" |>.components.foldl .str .anonymous
588+
leanModulesInFolder := leanModulesInFolder.push (mod, file)
589+
pure leanModulesInFolder
550590

551591
/--
552592
Parse command line arguments.

Cache/Main.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,13 @@ Options:
5252
Valid arguments are:
5353
5454
* Module names like 'Mathlib.Init'
55+
* Module globs like 'Mathlib.Data.+' (find all Lean files inside `Mathlib/Data/`)
56+
* Module globs like 'Mathlib.Data.*' (both of the above)
5557
* File names like 'Mathlib/Init.lean'
5658
* Folder names like 'Mathlib/Data/' (find all Lean files inside `Mathlib/Data/`)
5759
* With bash's automatic glob expansion one can also write things like
58-
'Mathlib/**/Order/*.lean'.
60+
'Mathlib/**/Order/*.lean'. However, one would need to write `Mathlib.Data.\\*`
61+
to prevent glob expansion.
5962
6063
# Environment variables
6164

0 commit comments

Comments
 (0)