Drastically speeds up `isTracingEnabledFor` in the common case, which has evolved from "no options set" to "`Elab.async` and probably some linter options set but no `trace`". ## Breaking changes `Lean.Options` is now an opaque type. The basic but not all of the `KVMap` API has been redefined on top of it.
559 lines
21 KiB
Text
559 lines
21 KiB
Text
/-
|
||
Copyright (c) 2025 Lean FRO. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Leonardo de Moura, Mac Malone
|
||
-/
|
||
module
|
||
|
||
prelude
|
||
import Lean.Elab.Frontend
|
||
import Lean.Elab.ParseImportsFast
|
||
import Lean.Server.Watchdog
|
||
import Lean.Server.FileWorker
|
||
import Lean.Compiler.IR.EmitC
|
||
|
||
/- Lean companion to `shell.cpp` -/
|
||
|
||
open System
|
||
|
||
namespace Lean
|
||
|
||
/--
|
||
Decodes an array of bytes that encode a string as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) into
|
||
the corresponding string. Invalid UTF-8 characters in the byte array are replaced with `U+FFFD`
|
||
(the unicode replacement character) in the resulting string.
|
||
|
||
This is used instead of the standard `String` functions because Lean is expected to not immediately
|
||
abort on files with invalid UTF-8.
|
||
-/
|
||
@[extern "lean_decode_lossy_utf8"]
|
||
opaque decodeLossyUTF8 (a : @& ByteArray) : String
|
||
|
||
/- Runs the `main` function of the module with `args` using the Lean interpreter. -/
|
||
@[extern "lean_run_main"]
|
||
opaque runMain (env : @& Environment) (opts : @& Options) (args : @& List String) : BaseIO UInt32
|
||
|
||
/--
|
||
Initializes the LLVM subsystem.
|
||
If Lean lacks LLVM support, this function will fail with an assertion violation.
|
||
-/
|
||
@[extern "lean_init_llvm"]
|
||
opaque initLLVM : IO Unit
|
||
|
||
/--
|
||
Emits LLVM bitcode for the module.
|
||
Before calling this function, the LLVM subsystem must first be successfully initialized.
|
||
-/
|
||
@[extern "lean_emit_llvm"]
|
||
opaque emitLLVM (env : Environment) (modName : Name) (filepath : FilePath) : IO Unit
|
||
|
||
/-- Print all profiling times (if any) to standard error. -/
|
||
@[extern "lean_display_cumulative_profiling_times"]
|
||
opaque displayCumulativeProfilingTimes : BaseIO Unit
|
||
|
||
/-- Whether Lean was built with an address sanitizer enabled. -/
|
||
@[extern "lean_internal_has_address_sanitizer"]
|
||
opaque Internal.hasAddressSanitizer (_ : Unit) : Bool
|
||
|
||
/-- Whether Lean was built with multithread support (i.e., `LEAN_MULTI_THREAD`). -/
|
||
@[extern "lean_internal_is_multi_thread"]
|
||
opaque Internal.isMultiThread (_ : Unit) : Bool
|
||
|
||
/-- Whether Lean was built in debug mode (i.e., `LEAN_DEBUG`). -/
|
||
@[extern "lean_internal_is_debug"]
|
||
opaque Internal.isDebug (_ : Unit) : Bool
|
||
|
||
/-- Returns the mode Lean was built in (i.e., `LEAN_BUILD_TYPE`). -/
|
||
@[extern "lean_internal_get_build_type"]
|
||
opaque Internal.getBuildType (_ : Unit) : String
|
||
|
||
/--
|
||
Returns the default max memory (in megabytes) Lean was built with
|
||
(i.e., `LEAN_DEFAULT_MAX_MEMORY`).
|
||
-/
|
||
@[extern "lean_internal_get_default_max_memory"]
|
||
opaque Internal.getDefaultMaxMemory (_ : Unit) : Nat
|
||
|
||
/-- Sets Lean's internal maximum memory (in bytes) for the C runtime. -/
|
||
@[extern "lean_internal_set_max_memory"]
|
||
opaque Internal.setMaxMemory (max : USize) : BaseIO Unit
|
||
|
||
/--
|
||
Returns the default max heartbeats (in thousands) Lean was built with
|
||
(i.e., `LEAN_DEFAULT_MAX_HEARTBEAT`).
|
||
-/
|
||
@[extern "lean_internal_get_default_max_heartbeat"]
|
||
opaque Internal.getDefaultMaxHeartbeat (_ : Unit) : Nat
|
||
|
||
/-- Sets Lean's internal maximum heartbeats for the C runtime. -/
|
||
@[extern "lean_internal_set_max_heartbeat"]
|
||
opaque Internal.setMaxHeartbeat (max : USize) : BaseIO Unit
|
||
|
||
/-- Returns whether Lean was built as verbose by default (i.e., `LEAN_DEFAULT_VERBOSE`). -/
|
||
@[extern "lean_internal_get_default_verbose"]
|
||
opaque Internal.getDefaultVerbose (_ : Unit) : Bool
|
||
|
||
/-- Sets whether Lean aborts when a panic occurs. -/
|
||
@[extern "lean_internal_set_exit_on_panic"]
|
||
opaque Internal.setExitOnPanic (exit : Bool) : BaseIO Unit
|
||
|
||
/-- Sets the stack size (in bytes) of a Lean thread. -/
|
||
@[extern "lean_internal_set_thread_stack_size"]
|
||
opaque Internal.setThreadStackSize (sz : USize) : BaseIO Unit
|
||
|
||
/-- Enables debug assertions with the given tag. -/
|
||
@[extern "lean_internal_enable_debug"]
|
||
opaque Internal.enableDebug (tag : @& String) : BaseIO Unit
|
||
|
||
/--
|
||
Lean's short version string (i.e., what is printed by `lean --short-version`).
|
||
|
||
This is the Lean equivalent of of the C++ `LEAN_VERSION_STRING` / `g_short_version_string`.
|
||
-/
|
||
def shortVersionString : String :=
|
||
if version.specialDesc ≠ "" then
|
||
s!"{versionStringCore}-{version.specialDesc}"
|
||
else if !version.isRelease then
|
||
s!"{versionStringCore}-pre"
|
||
else
|
||
versionStringCore
|
||
|
||
/-- The full Lean version header (i.e, what is printed by `lean --version`). -/
|
||
def versionHeader : String := Id.run do
|
||
let mut ver := shortVersionString
|
||
if Platform.target ≠ "" then
|
||
ver := s!"{ver}, {Platform.target}"
|
||
if githash ≠ "" then
|
||
ver := s!"{ver}, commit {githash}"
|
||
s!"Lean (version {ver}, {Internal.getBuildType ()})"
|
||
|
||
/--
|
||
A string containing the list of additional features Lean was built with.
|
||
|
||
This is printed by `lean --features`.
|
||
-/
|
||
def featuresString :=
|
||
if Internal.hasLLVMBackend () then
|
||
"[LLVM]"
|
||
else "[]"
|
||
|
||
/-- Print the Lean CLI help message. -/
|
||
def displayHelp (useStderr : Bool) : IO Unit := do
|
||
let out ← if useStderr then IO.getStderr else IO.getStdout
|
||
out.putStrLn versionHeader
|
||
out.putStrLn "Miscellaneous"
|
||
out.putStrLn " -h, --help display this message"
|
||
out.putStrLn " --features display features compiler provides (eg. LLVM support)"
|
||
out.putStrLn " -v, --version display version information"
|
||
out.putStrLn " -V, --short-version display short version number"
|
||
out.putStrLn " -g, --githash display the git commit hash number used to build this binary"
|
||
out.putStrLn " --run <file> call the 'main' definition in the given file with the remaining arguments"
|
||
out.putStrLn " -o, --o=oname create olean file"
|
||
out.putStrLn " -i, --i=iname create ilean file"
|
||
out.putStrLn " -c, --c=fname name of the C output file"
|
||
out.putStrLn " -b, --bc=fname name of the LLVM bitcode file"
|
||
out.putStrLn " --stdin take input from stdin"
|
||
out.putStrLn " -R, --root=dir set package root directory from which the module name\n"
|
||
out.putStrLn " of the input file is calculated\n"
|
||
out.putStrLn " (default: current working directory)\n";
|
||
out.putStrLn " -t, --trust=num trust level (default: max) 0 means do not trust any macro,\n"
|
||
out.putStrLn " and type check all imported modules\n";
|
||
out.putStrLn " -q, --quiet do not print verbose messages"
|
||
out.putStrLn " -M, --memory=num maximum amount of memory that should be used by Lean"
|
||
out.putStrLn " (in megabytes)"
|
||
out.putStrLn " -T, --timeout=num maximum number of memory allocations per task"
|
||
out.putStrLn " this is a deterministic way of interrupting long running tasks"
|
||
if Internal.isMultiThread () then
|
||
out.putStrLn " -j, --threads=num number of threads used to process lean files"
|
||
out.putStrLn " -s, --tstack=num thread stack size in Kb"
|
||
out.putStrLn " --server start lean in server mode"
|
||
out.putStrLn " --worker start lean in server-worker mode"
|
||
out.putStrLn " --plugin=file load and initialize Lean shared library for registering linters etc."
|
||
out.putStrLn " --load-dynlib=file load shared library to make its symbols available to the interpreter"
|
||
out.putStrLn " --setup=file JSON file with module setup data (supersedes the file's header)"
|
||
out.putStrLn " --json report Lean output (e.g., messages) as JSON (one per line)"
|
||
out.putStrLn " -E, --error=kind report Lean messages of kind as errors"
|
||
out.putStrLn " --deps just print dependencies of a Lean input"
|
||
out.putStrLn " --src-deps just print dependency sources of a Lean input"
|
||
out.putStrLn " --print-prefix print the installation prefix for Lean and exit"
|
||
out.putStrLn " --print-libdir print the installation directory for Lean's built-in libraries and exit"
|
||
out.putStrLn " --profile display elaboration/type checking time for each definition/theorem"
|
||
out.putStrLn " --stats display environment statistics"
|
||
if Internal.isDebug () then
|
||
out.putStrLn " --debug=tag enable assertions with the given tag"
|
||
out.putStrLn " -D name=value set a configuration option (see set_option command)"
|
||
|
||
inductive ShellComponent
|
||
| frontend
|
||
| watchdog
|
||
| worker
|
||
|
||
private builtin_initialize maxMemory : Lean.Option Nat ←
|
||
Lean.Option.register `max_memory {defValue := Internal.getDefaultMaxMemory ()}
|
||
|
||
private builtin_initialize timeout : Lean.Option Nat ←
|
||
Lean.Option.register `timeout {defValue := Internal.getDefaultMaxHeartbeat ()}
|
||
|
||
private builtin_initialize verbose : Lean.Option Bool ←
|
||
Lean.Option.register `verbose {defValue := Internal.getDefaultVerbose ()}
|
||
|
||
/--
|
||
Returns the default options Lean was built with
|
||
(i.e., those set in `stdlib_flags.h`).
|
||
-/
|
||
@[extern "lean_internal_get_default_options"]
|
||
opaque Internal.getDefaultOptions (_ : Unit) : Options
|
||
|
||
/--
|
||
Returns the believer trust level of the Lean environment (i.e., `LEAN_BELIEVER_TRUST_LEVEL`).
|
||
|
||
If an environment is created with a trust level greater than this, then we can add declarations
|
||
to it without type checking them.
|
||
-/
|
||
@[extern "lean_internal_get_believer_trust_level"]
|
||
opaque Internal.getBelieverTrustLevel (_ : Unit) : UInt32
|
||
|
||
/-- Returns the shell's default trust level. -/
|
||
def defaultTrustLevel : UInt32 :=
|
||
Internal.getBelieverTrustLevel () + 1
|
||
|
||
/-- Returns the platform's native concurrency limit. -/
|
||
@[extern "lean_internal_get_hardware_concurrency"]
|
||
opaque Internal.getHardwareCurrency (_ : Unit) : UInt32
|
||
|
||
/-- Returns the default number of threads for the shell's task manager. -/
|
||
def defaultNumThreads : UInt32 :=
|
||
if Internal.isMultiThread () then
|
||
Internal.getHardwareCurrency ()
|
||
else 0
|
||
|
||
structure ShellOptions where
|
||
leanOpts : Options := Internal.getDefaultOptions ()
|
||
forwardedArgs : Array String := #[]
|
||
component : ShellComponent := .frontend
|
||
printPrefix : Bool := false
|
||
printLibDir : Bool := false
|
||
useStdin : Bool := false
|
||
onlyDeps : Bool := false
|
||
onlySrcDeps : Bool := false
|
||
depsJson : Bool := false
|
||
opts : Options := {}
|
||
trustLevel : UInt32 := defaultTrustLevel
|
||
numThreads : UInt32 := defaultNumThreads
|
||
rootDir? : Option System.FilePath := none
|
||
setupFileName? : Option System.FilePath := none
|
||
oleanFileName? : Option System.FilePath := none
|
||
ileanFileName? : Option System.FilePath := none
|
||
cFileName? : Option System.FilePath := none
|
||
bcFileName? : Option System.FilePath := none
|
||
jsonOutput : Bool := false
|
||
errorOnKinds : Array Name := #[]
|
||
printStats : Bool := false
|
||
run : Bool := false
|
||
|
||
@[export lean_shell_options_mk]
|
||
def mkShellOptions (_ : Unit) : ShellOptions := {}
|
||
|
||
@[export lean_shell_options_get_run]
|
||
def ShellOptions.getRun (opts : ShellOptions) : Bool :=
|
||
opts.run
|
||
|
||
@[export lean_shell_options_get_profiler]
|
||
def ShellOptions.getProfiler (opts : ShellOptions) : Bool :=
|
||
profiler.get opts.leanOpts
|
||
|
||
@[export lean_shell_options_get_num_threads]
|
||
def ShellOptions.getNumThreads (opts : ShellOptions) : UInt32 :=
|
||
opts.numThreads
|
||
|
||
def checkOptArg (optName : String) (optArg? : Option String) : IO String := do
|
||
if let some arg := optArg? then
|
||
return arg
|
||
else
|
||
throw <| .userError s!"argument missing for option '-{optName}'"
|
||
|
||
def setConfigOption (opts : Options) (arg : String) : IO Options := do
|
||
let pos := arg.find '='
|
||
if h : pos.IsAtEnd then
|
||
throw <| .userError "invalid -D parameter, argument must contain '='"
|
||
else
|
||
let name := arg.sliceTo pos |>.toName
|
||
let val := arg.sliceFrom (pos.next h) |>.copy
|
||
if let some decl := (← getOptionDecls).find? name then
|
||
Language.Lean.setOption opts decl name val
|
||
else
|
||
-- More options may be registered by imports, so we leave validating them to the elaborator.
|
||
-- This (minor) duplication may be resolved later.
|
||
return opts.set name val
|
||
|
||
/--
|
||
Process a command-line option parsed by the C++ shell.
|
||
|
||
Runs under `IO.initializing` and without a task manager.
|
||
-/
|
||
@[export lean_shell_options_process]
|
||
def ShellOptions.process (opts : ShellOptions)
|
||
(opt : Char) (optArg? : Option String) : EIO UInt32 ShellOptions := do
|
||
have : MonadLift IO (EIO UInt32) := ⟨liftIO⟩
|
||
match opt with
|
||
| 'e' => -- undocumented
|
||
Internal.setExitOnPanic true
|
||
return opts
|
||
| 'j' => -- `-j, --threads=num`
|
||
let arg ← checkOptArg "j" optArg?
|
||
let some numThreads := arg.toNat?
|
||
| throwExpectedNumeric "j"
|
||
if h : numThreads < UInt32.size then
|
||
let numThreads := UInt32.ofNatLT numThreads h
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-j{arg}";
|
||
return {opts with forwardedArgs, numThreads}
|
||
else
|
||
throwTooLarge "j"
|
||
| 'v' => --- `-v, --version`
|
||
IO.println Lean.versionHeader
|
||
throw 0
|
||
| 'V' => --- `-V, --short-version`
|
||
IO.println Lean.shortVersionString
|
||
throw 0
|
||
| 'g' => -- `-g, --githash`
|
||
IO.println Lean.githash
|
||
throw 0
|
||
| 'h' => -- `-h, --help`
|
||
displayHelp (useStderr := false)
|
||
throw 0
|
||
| 'f' => -- `--features`
|
||
IO.println Lean.featuresString
|
||
throw 0
|
||
| 'c' => -- `-c, --c=fname`
|
||
return {opts with cFileName? := ← checkOptArg "c" optArg?}
|
||
| 'b' => -- `-b, --bc=fname`
|
||
return {opts with bcFileName? := ← checkOptArg "b" optArg?}
|
||
| 's' => -- `-s, --tstack=num`
|
||
let arg ← checkOptArg "s" optArg?
|
||
let some stackSize := arg.toNat?
|
||
| throwExpectedNumeric "s"
|
||
let stackSize := stackSize / 4 * 4 * 1024
|
||
if h : stackSize < USize.size then
|
||
let stackSize := USize.ofNatLT stackSize h
|
||
Internal.setThreadStackSize stackSize
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-s{arg}"
|
||
return {opts with forwardedArgs}
|
||
else
|
||
throwTooLarge "s"
|
||
| 'I' => -- `-I, --stdin`
|
||
return {opts with useStdin := true}
|
||
| 'r' => -- `--run`
|
||
return {opts with run := true}
|
||
| 'o' => -- `--o, olean=fname`
|
||
return {opts with oleanFileName? := ← checkOptArg "o" optArg?}
|
||
| 'i' => -- `--i, ilean=fname`
|
||
return {opts with ileanFileName? := ← checkOptArg "i" optArg?}
|
||
| 'R' => -- `-R, --root=dir`
|
||
let arg ← checkOptArg "R" optArg?
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-R{arg}"
|
||
return {opts with forwardedArgs, rootDir? := arg}
|
||
| 'M' => -- `-M, --memory=num`
|
||
let arg ← checkOptArg "M" optArg?
|
||
let some val := arg.toNat?
|
||
| throwExpectedNumeric "M"
|
||
let leanOpts := maxMemory.set opts.leanOpts val
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-M{arg}"
|
||
return {opts with forwardedArgs, leanOpts}
|
||
| 'T' => -- `-T, --timeout=num`
|
||
let arg ← checkOptArg "T" optArg?
|
||
let some val := arg.toNat?
|
||
| throwExpectedNumeric "T"
|
||
let leanOpts := timeout.set opts.leanOpts val
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-T{arg}"
|
||
return {opts with forwardedArgs, leanOpts}
|
||
| 't' => -- `--trust=num`
|
||
let arg ← checkOptArg "t" optArg?
|
||
let some trustLevel := arg.toNat?
|
||
| throwExpectedNumeric "t"
|
||
if h : trustLevel < UInt32.size then
|
||
let trustLevel := UInt32.ofNatLT trustLevel h
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-t{arg}";
|
||
return {opts with forwardedArgs, trustLevel}
|
||
else
|
||
throwTooLarge "t"
|
||
| 'q' =>
|
||
return {opts with leanOpts := verbose.set opts.leanOpts false}
|
||
| 'd' => -- `--deps`
|
||
return {opts with onlyDeps := true}
|
||
| 'O' => -- `--src-deps`
|
||
return {opts with onlySrcDeps := true}
|
||
| 'N' => -- `--deps-json`
|
||
return {opts with onlyDeps := true, depsJson := true}
|
||
| 'J' => -- `--json`
|
||
return {opts with jsonOutput := true}
|
||
| 'a' => -- `--stats`
|
||
return {opts with printStats := true}
|
||
| 'x' => -- `--print-prefix`
|
||
return {opts with printPrefix := true}
|
||
| 'L' => -- `--print-libdir`
|
||
return {opts with printLibDir := true}
|
||
| 'D' => -- `-D name=value`
|
||
let arg ← checkOptArg "D" optArg?
|
||
let leanOpts ← setConfigOption opts.leanOpts arg
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-D{arg}"
|
||
return {opts with forwardedArgs, leanOpts}
|
||
| 'S' => -- `--server`
|
||
return {opts with component := .watchdog}
|
||
| 'W' => -- `--worker`
|
||
return {opts with component := .worker}
|
||
| 'P' => -- `--profile`
|
||
return {opts with leanOpts := profiler.set opts.leanOpts true}
|
||
| 'B' => -- `--debug=tag`
|
||
if Internal.isDebug () then
|
||
let arg ← checkOptArg "B" optArg?
|
||
Internal.enableDebug arg
|
||
return opts
|
||
-- if not `LEAN_DEBUG`, fall through to unknown option
|
||
| 'p' => -- `--plugin=file`
|
||
let arg ← checkOptArg "p" optArg?
|
||
Lean.loadPlugin arg
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-p{arg}"
|
||
return {opts with forwardedArgs}
|
||
| 'l' => -- `--load-dynlib=file`
|
||
let arg ← checkOptArg "l" optArg?
|
||
Lean.loadDynlib arg
|
||
let forwardedArgs := opts.forwardedArgs.push s!"-l{arg}"
|
||
return {opts with forwardedArgs}
|
||
| 'u' => -- `--setup=file`
|
||
return {opts with setupFileName? := ← checkOptArg "u" optArg?}
|
||
| 'E' => -- `-E, --error=kind`
|
||
let arg ← checkOptArg "E" optArg?
|
||
let errorOnKinds := opts.errorOnKinds.push arg.toName
|
||
return {opts with errorOnKinds}
|
||
| _ =>
|
||
pure ()
|
||
eprint "Unknown command line option\n"
|
||
displayHelp (useStderr := true)
|
||
throw 1
|
||
where
|
||
@[inline] eprint msg := do
|
||
IO.eprint msg |>.catchExceptions fun _ => pure ()
|
||
@[inline] liftIO {α} (x : IO α) := do
|
||
match (← x.toBaseIO) with
|
||
| .ok a =>
|
||
return a
|
||
| .error e =>
|
||
eprint s!"error: "
|
||
eprint (toString e)
|
||
eprint "\n"
|
||
throw 1
|
||
@[inline] throwExpectedNumeric opt := do
|
||
eprint s!"error: expected numeric argument for option '-{opt}'\n"
|
||
throw 1
|
||
@[inline] throwTooLarge opt := do
|
||
eprint s!"error: argument value for '-{opt}' is too large\n"
|
||
throw 1
|
||
|
||
@[export lean_shell_main]
|
||
def shellMain (args : List String) (opts : ShellOptions) : IO UInt32 := do
|
||
if opts.printPrefix then
|
||
IO.println (← getBuildDir)
|
||
return 0
|
||
if opts.printLibDir then
|
||
IO.println (← getLibDir (← getBuildDir))
|
||
return 0
|
||
let maxMemory := maxMemory.get opts.leanOpts
|
||
if maxMemory != 0 then
|
||
Internal.setMaxMemory (maxMemory.toUSize * 1024 * 1024)
|
||
let timeout := timeout.get opts.leanOpts
|
||
if timeout != 0 then
|
||
Internal.setMaxHeartbeat (timeout.toUSize * 1000)
|
||
match opts.component with
|
||
| .frontend =>
|
||
pure ()
|
||
| .watchdog =>
|
||
return ← Server.Watchdog.watchdogMain opts.forwardedArgs.toList
|
||
| .worker =>
|
||
return ← Server.FileWorker.workerMain opts.leanOpts
|
||
if opts.onlyDeps && opts.depsJson then
|
||
let fns ←
|
||
if opts.useStdin then
|
||
(← IO.getStdin).lines
|
||
else
|
||
pure args.toArray
|
||
printImportsJson fns
|
||
return 0
|
||
let (fileName?, args) :=
|
||
match args with
|
||
| fileName :: args => (some fileName, args)
|
||
| [] => (none, args)
|
||
if !opts.run && !args.isEmpty then
|
||
IO.eprintln "Expected exactly one file name"
|
||
displayHelp (useStderr := true)
|
||
return 1
|
||
let fileName ←
|
||
if let some fileName := fileName? then
|
||
pure fileName
|
||
else if opts.useStdin then
|
||
pure "<stdin>"
|
||
else
|
||
IO.eprintln "Expected exactly one file name"
|
||
displayHelp (useStderr := true)
|
||
return 1
|
||
let contents ← decodeLossyUTF8 <$> do
|
||
if opts.useStdin then
|
||
(← IO.getStdin).readBinToEnd
|
||
else
|
||
IO.FS.readBinFile fileName
|
||
if opts.onlyDeps then
|
||
Elab.printImports contents fileName
|
||
return 0
|
||
if opts.onlySrcDeps then
|
||
Elab.printImportSrcs contents fileName
|
||
return 0
|
||
-- Quick and dirty `#lang` support
|
||
---TODO: make it extensible, and add `lean4md`
|
||
let contents ←
|
||
if let some contents := contents.dropPrefix? "#lang" then
|
||
let endLinePos := contents.find '\n'
|
||
let langId := contents.sliceTo endLinePos |>.trimAscii
|
||
if langId == "lean4" then
|
||
pure () -- do nothing for now
|
||
else
|
||
IO.eprintln s!"unknown language '{langId}'\n";
|
||
return 1
|
||
-- Remove up to `\n`
|
||
pure (contents.sliceFrom endLinePos).copy
|
||
else
|
||
pure contents
|
||
let setup? ← opts.setupFileName?.mapM ModuleSetup.load
|
||
let mainModuleName ←
|
||
if let some setup := setup? then
|
||
pure setup.name
|
||
else if let some fileName := fileName? then
|
||
try moduleNameOfFileName fileName opts.rootDir? catch e =>
|
||
if opts.oleanFileName?.isNone && opts.cFileName?.isNone then
|
||
pure `_stdin
|
||
else
|
||
throw e
|
||
else
|
||
pure `_stdin
|
||
let env? ← Elab.runFrontend contents opts.leanOpts fileName mainModuleName
|
||
opts.trustLevel opts.oleanFileName? opts.ileanFileName? opts.jsonOutput opts.errorOnKinds
|
||
#[] opts.printStats setup?
|
||
if let some env := env? then
|
||
if opts.run then
|
||
return ← runMain env opts.leanOpts args
|
||
if let some c := opts.cFileName? then
|
||
let .ok out ← IO.FS.Handle.mk c .write |>.toBaseIO
|
||
| IO.eprintln s!"failed to create '{c}'"
|
||
return 1
|
||
profileitIO "C code generation" opts.leanOpts do
|
||
let data ← IO.ofExcept <| IR.emitC env mainModuleName
|
||
out.write data.toUTF8
|
||
if let some bc := opts.bcFileName? then
|
||
initLLVM
|
||
profileitIO "LLVM code generation" opts.leanOpts do
|
||
emitLLVM env mainModuleName bc
|
||
displayCumulativeProfilingTimes
|
||
if Internal.hasAddressSanitizer () then
|
||
return if env?.isSome then 0 else 1
|
||
else
|
||
-- When not using the address/leak sanitizer, we interrupt execution without garbage collecting.
|
||
-- This is useful when profiling improvements to Lean startup time.
|
||
IO.Process.exit <| if env?.isSome then 0 else 1
|