Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - feat: lake exe mk_all as a Lean executable #11853

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c2616fe
feat: `mk_all` script implemented in Lean
adomani Apr 2, 2024
2e159d0
move mkAll to Mathlib.Tactic
adomani Apr 2, 2024
6c7c746
Flag to use `walkDir` or `git ls-files`.
adomani Apr 2, 2024
6b80b6e
Rename file mkAll --> MKAll + update imports with itself!
adomani Apr 2, 2024
a2198f6
shake
adomani Apr 2, 2024
74966c0
reimplement as a Lean executable
YaelDillies Apr 2, 2024
44a4ba2
docs
adomani Apr 3, 2024
2cf953d
inline `git ls-files`
adomani Apr 3, 2024
66c5eab
golf
adomani Apr 3, 2024
d581e26
exit code
adomani Apr 3, 2024
a9362e7
Sort only once
adomani Apr 3, 2024
e214aae
Fix uncaught exception?
adomani Apr 3, 2024
67d9aae
Add Mario's getLeanLibs and use it!
adomani Apr 3, 2024
d8635b9
Special case package `mathlib`, as opposed to "containing `mathlib`.
adomani Apr 3, 2024
6485b73
docs and trim
adomani Apr 3, 2024
f9105d8
Only import existing files
adomani Apr 3, 2024
49729f9
Repetition and reverse
adomani Apr 3, 2024
6674129
Merge remote-tracking branch 'origin/master' into adomani/mkAll_in_lean
adomani May 9, 2024
1ea08aa
shake
adomani May 9, 2024
11e062a
fix mkAll with Kim's code
adomani May 10, 2024
2498357
rename to `mk_all`
YaelDillies May 10, 2024
7fb765a
docs
adomani May 10, 2024
842b6af
Merge branch 'master' into adomani/mkAll_in_lean
adomani May 24, 2024
f6ef6c8
synch with branch with CI
adomani May 24, 2024
baa366c
Merge remote-tracking branch 'origin/master' into adomani/mkAll_in_lean
adomani May 27, 2024
cc7b70a
getAll returns an array of strings. Add todo about dependencies
adomani May 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lakefile.lean
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ lean_exe checkYaml where
srcDir := "scripts"
supportInterpreter := true

/-- `lake exe mk_all` constructs the files containing all imports for a project. -/
lean_exe mk_all where
srcDir := "scripts"
supportInterpreter := true

/-- `lake exe shake` checks files for unnecessary imports. -/
lean_exe shake where
root := `Shake.Main
Expand Down
113 changes: 113 additions & 0 deletions scripts/mk_all.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/-
Copyright (c) 2024 Yaël Dillies, Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Damiano Testa
-/
import Cli.Basic
import Lean.Util.Path
import Lake.CLI.Main

/-!
# Script to create a file importing all files from a folder

This file declares a command to gather all Lean files from a folder into a single Lean file.

TODO:
`getLeanLibs` contains a hard-coded choice of which dependencies should be built and which ones
should not. Could this be made more structural and robust, possibly with extra `Lake` support?
-/

open Lean System.FilePath

/-- `getAll git ml` takes all `.lean` files in the dir `ml` (recursing into sub-dirs) and
returns the `Array` of `String`s
```
#[file₁, ..., fileₙ]
```
where each `fileᵢ` is of the form `"Mathlib.Algebra.Algebra.Basic"`.

The input `git` is a `Bool`ean flag:
* `true` means that the command uses `git ls-files` to find the relevant files;
* `false` means that the command recursively scans all dirs searching for `.lean` files.
-/
def getAll (git : Bool) (ml : String) : IO (Array String) := do
let ml.lean := addExtension ⟨ml⟩ "lean" -- for example, `Mathlib.lean`
let allModules : Array System.FilePath ← (do
if git then
let mlDir := ml.push pathSeparator -- for example, `Mathlib/`
let allLean ← IO.Process.run { cmd := "git", args := #["ls-files", mlDir ++ "*.lean"] }
return (((allLean.dropRightWhile (· == '\n')).splitOn "\n").map (⟨·⟩)).toArray
else do
let all ← walkDir ml
return all.filter (·.extension == some "lean"))
let files := (allModules.erase ml.lean).qsort (·.toString < ·.toString)
let withImport ← files.mapM fun f => do
-- this check is helpful in case the `git` option is on and a local file has been removed
if ← pathExists f then
return (← moduleNameOfFileName f none).toString
else return ""
return withImport.filter (· != "")

open Lake in
/-- `getLeanLibs` returns the names (as an `Array` of `String`s) of all the libraries
on which the current project depends.
If the current project is `mathlib`, then it excludes the libraries `Cache` and `LongestPole` and
it includes `Mathlib/Tactic`. -/
def getLeanLibs : IO (Array String) := do
let (elanInstall?, leanInstall?, lakeInstall?) ← findInstall?
let config ← MonadError.runEIO <| mkLoadConfig { elanInstall?, leanInstall?, lakeInstall? }
let ws ← MonadError.runEIO (MainM.runLogIO (loadWorkspace config)).toEIO
let package := ws.root
let libs := (package.leanLibs.map (·.name)).map (·.toString)
return if package.name == `mathlib then
libs.erase "Cache" |>.erase "LongestPole" |>.push ("Mathlib".push pathSeparator ++ "Tactic")
adomani marked this conversation as resolved.
Show resolved Hide resolved
else
libs

open IO.FS IO.Process Name Cli in
/-- Implementation of the `mk_all` command line program.
The exit code is the number of files that the command updates/creates. -/
def mkAllCLI (args : Parsed) : IO UInt32 := do
-- Check whether the `--git` flag was set
let git := (args.flag? "git").isSome
-- Check whether the `--lib` flag was set. If so, build the file corresponding to the library
-- passed to `--lib`. Else build all the libraries of the package.
-- If the package is `mathlib`, then it removes the libraries `Cache` and `LongestPole` and it
-- adds `Mathlib/Tactic`.
let libs := ← match args.flag? "lib" with
| some lib => return #[lib.as! String]
| none => getLeanLibs
let mut updates := 0
for d in libs.reverse do -- reverse to create `Mathlib/Tactic.lean` before `Mathlib.lean`
let fileName := addExtension d "lean"
let allFiles ← getAll git d
let fileContent := ("\n".intercalate (allFiles.map ("import " ++ ·)).toList).push '\n'
if !(← pathExists fileName) then
IO.println s!"Creating '{fileName}'"
updates := updates + 1
IO.FS.writeFile fileName fileContent
else if (← IO.FS.readFile fileName) != fileContent then
IO.println s!"Updating '{fileName}'"
updates := updates + 1
IO.FS.writeFile fileName fileContent
if updates == 0 then
IO.println "No update necessary"
return updates

open Cli in
/-- Setting up command line options and help text for `lake exe mk_all`. -/
def mkAll : Cmd := `[Cli|
grunweg marked this conversation as resolved.
Show resolved Hide resolved
mk_all VIA mkAllCLI; ["0.0.1"]
"Generate a file importing all the files of a Lean folder. \
By default, it generates the files for the Lean libraries of the package.\
In the case of `Mathlib`, it removes the libraries `Cache` and `LongestPole`\
and it adds `Mathlib/Tactic`. \
If you are working in a project downstream of mathlib, use `lake exe mk_all --lib MyProject`."

FLAGS:
lib : String; "Create a folder importing all Lean files from the specified library/subfolder."
git; "Use the folder content information from git."
]

/-- The entrypoint to the `lake exe mk_all` command. -/
def main (args : List String) : IO UInt32 := mkAll.validate args
Loading