chore: revert "refactor: port shell option processing to Lean" (#11378)

Needs a fix to unbreak the Windows build first.

Reverts leanprover/lean4#11345
This commit is contained in:
Sebastian Ullrich 2025-11-26 10:28:48 +01:00 committed by GitHub
parent 5a5f8c4c2e
commit 3772bb8685
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 387 additions and 442 deletions

View file

@ -300,28 +300,6 @@ structure SetupImportsResult where
/-- Lean plugins to load as part of the environment setup. -/
plugins : Array System.FilePath := #[]
/--
Parses an option value from a string and inserts it into `opts`.
The type of the option is determined from `decl`.
-/
def setOption (opts : Options) (decl : OptionDecl) (name : Name) (val : String) : IO Options := do
match decl.defValue with
| .ofBool _ =>
match val with
| "true" => return opts.insert name true
| "false" => return opts.insert name false
| _ =>
throw <| .userError s!"invalid -D parameter, invalid configuration option '{val}' value, \
it must be true/false"
| .ofNat _ =>
let some val := val.toNat?
| throw <| .userError s!"invalid -D parameter, invalid configuration option '{val}' value, \
it must be a natural number"
return opts.insert name val
| .ofString _ => return opts.insert name val
| _ => throw <| .userError s!"invalid -D parameter, configuration option '{name}' \
cannot be set in the command line, use set_option command"
/--
Parses values of options registered during import and left by the C++ frontend as strings.
Removes `weak` prefixes from both parsed and unparsed options and fails if any option names remain
@ -344,7 +322,22 @@ If the option is defined in a library, use '-D{`weak ++ name}' to set it conditi
let .ofString val := val
| opts' := opts'.insert name val -- Already parsed
opts' ← setOption opts' decl name val
match decl.defValue with
| .ofBool _ =>
match val with
| "true" => opts' := opts'.insert name true
| "false" => opts' := opts'.insert name false
| _ =>
throw <| .userError s!"invalid -D parameter, invalid configuration option '{val}' value, \
it must be true/false"
| .ofNat _ =>
let some val := val.toNat?
| throw <| .userError s!"invalid -D parameter, invalid configuration option '{val}' value, \
it must be a natural number"
opts' := opts'.insert name val
| .ofString _ => opts' := opts'.insert name val
| _ => throw <| .userError s!"invalid -D parameter, configuration option '{name}' \
cannot be set in the command line, use set_option command"
return opts'

View file

@ -55,15 +55,15 @@ opaque displayCumulativeProfilingTimes : BaseIO Unit
@[extern "lean_internal_has_address_sanitizer"]
opaque Internal.hasAddressSanitizer (_ : Unit) : Bool
/-- Whether Lean was built with multithread support (i.e., `LEAN_MULTI_THREAd`). -/
/-- Whether Lean was built with multithread support. -/
@[extern "lean_internal_is_multi_thread"]
opaque Internal.isMultiThread (_ : Unit) : Bool
/-- Whether Lean was built in debug mode (i.e., `LEAN_DEBUG`). -/
/-- Whether Lean was built in debug mode. -/
@[extern "lean_internal_is_debug"]
opaque Internal.isDebug (_ : Unit) : Bool
/-- Returns the mode Lean was built in (i.e., `LEAN_BUILD_TYPE`). -/
/-- Returns the mode Lean was built in. -/
@[extern "lean_internal_get_build_type"]
opaque Internal.getBuildType (_ : Unit) : String
@ -89,27 +89,7 @@ opaque Internal.getDefaultMaxHeartbeat (_ : Unit) : Nat
@[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`.
-/
/-- Lean equivalent of `LEAN_VERSION_STRING` / `g_short_version_string` -/
def shortVersionString : String :=
if version.specialDesc ≠ "" then
s!"{versionStringCore}-{version.specialDesc}"
@ -127,17 +107,13 @@ def versionHeader : String := Id.run do
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 version header. -/
@[export lean_display_header]
def displayHeader : IO Unit := do
IO.println versionHeader
/-- Print the Lean CLI help message. -/
@[export lean_display_help]
def displayHelp (useStderr : Bool) : IO Unit := do
let out ← if useStderr then IO.getStderr else IO.getStdout
out.putStrLn versionHeader
@ -153,7 +129,7 @@ def displayHelp (useStderr : Bool) : IO Unit := do
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 " --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"
@ -172,7 +148,7 @@ def displayHelp (useStderr : Bool) : IO Unit := do
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 " -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"
@ -194,285 +170,52 @@ private builtin_initialize maxMemory : Lean.Option Nat ←
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.insert 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
def shellMain
(args : List String)
(forwardedArgs : List 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 := 0)
(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)
: IO UInt32 := do
if printPrefix then
IO.println (← getBuildDir)
return 0
if opts.printLibDir then
if printLibDir then
IO.println (← getLibDir (← getBuildDir))
return 0
let maxMemory := maxMemory.get opts.leanOpts
let maxMemory := maxMemory.get opts
if maxMemory != 0 then
Internal.setMaxMemory (maxMemory.toUSize * 1024 * 1024)
let timeout := timeout.get opts.leanOpts
let timeout := timeout.get opts
if timeout != 0 then
Internal.setMaxHeartbeat (timeout.toUSize * 1000)
match opts.component with
match component with
| .frontend =>
pure ()
| .watchdog =>
return ← Server.Watchdog.watchdogMain opts.forwardedArgs.toList
return ← Server.Watchdog.watchdogMain forwardedArgs
| .worker =>
return ← Server.FileWorker.workerMain opts.leanOpts
if opts.onlyDeps && opts.depsJson then
return ← Server.FileWorker.workerMain opts
if onlyDeps && depsJson then
let fns ←
if opts.useStdin then
if useStdin then
(← IO.getStdin).lines
else
pure args.toArray
@ -482,73 +225,72 @@ def shellMain (args : List String) (opts : ShellOptions) : IO UInt32 := do
match args with
| fileName :: args => (some fileName, args)
| [] => (none, args)
if !opts.run && !args.isEmpty then
if !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
else if 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
if useStdin then
(← IO.getStdin).readBinToEnd
else
IO.FS.readBinFile fileName
if opts.onlyDeps then
if onlyDeps then
Elab.printImports contents fileName
return 0
if opts.onlySrcDeps then
if 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
if contents.startsWith "#lang" then
let endLinePos := contents.find '\n'
let langId := contents.sliceTo endLinePos |>.trimAscii
let langId := String.Pos.Raw.extract contents ⟨6⟩ endLinePos.offset |>.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
pure <| String.Pos.Raw.extract contents endLinePos.offset contents.rawEndPos
else
pure contents
let setup? ← opts.setupFileName?.mapM ModuleSetup.load
let setup? ← 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
try moduleNameOfFileName fileName rootDir? catch e =>
if oleanFileName?.isNone && 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?
let env? ← Elab.runFrontend contents opts fileName mainModuleName trustLevel
oleanFileName? ileanFileName? jsonOutput errorOnKinds #[] 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
if run then
return ← runMain env opts args
if let some c := 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
profileitIO "C code generation" opts do
let data ← IO.ofExcept <| IR.emitC env mainModuleName
out.write data.toUTF8
if let some bc := opts.bcFileName? then
if let some bc := bcFileName? then
initLLVM
profileitIO "LLVM code generation" opts.leanOpts do
profileitIO "LLVM code generation" opts do
emitLLVM env mainModuleName bc
displayCumulativeProfilingTimes
if Internal.hasAddressSanitizer () then

View file

@ -64,9 +64,4 @@ extern "C" LEAN_EXPORT lean_object * lean_kernel_check(lean_object * obj_env, le
return type_checker(env.to_kernel_env(), local_ctx(lctx)).check(expr(a)).steal();
});
}
/* getBelieverTrustLevel (_ : Unit) : UInt32 */
extern "C" LEAN_EXPORT uint32 lean_internal_get_believer_trust_level(obj_arg) {
return LEAN_BELIEVER_TRUST_LEVEL;
}
}

View file

@ -59,12 +59,6 @@ void enable_debug(char const * tag) {
g_enabled_debug_tags->insert(tag);
}
/* enableDebug (tag : @& String) : BaseIO Unit */
extern "C" LEAN_EXPORT lean_obj_res lean_internal_enable_debug(b_lean_obj_arg tag) {
enable_debug(lean_string_cstr(tag));
return lean_box(0);
}
void disable_debug(char const * tag) {
if (g_enabled_debug_tags)
g_enabled_debug_tags->erase(tag);

View file

@ -106,12 +106,6 @@ extern "C" LEAN_EXPORT void lean_set_exit_on_panic(bool flag) {
g_exit_on_panic = flag;
}
/* setExitOnPanic (exit : Bool) : BaseIO Unit */
extern "C" LEAN_EXPORT obj_res lean_internal_set_exit_on_panic(uint8 exit) {
g_exit_on_panic = exit;
return box(0);
}
extern "C" LEAN_EXPORT void lean_set_panic_messages(bool flag) {
g_panic_messages = flag;
}
@ -1020,11 +1014,6 @@ static unsigned get_lean_num_threads() {
return hardware_concurrency();
}
/* getHardwareConcurrency (_ : Unit) : UInt32 */
extern "C" LEAN_EXPORT uint32 lean_internal_get_hardware_concurrency(obj_arg) {
return hardware_concurrency();
}
extern "C" LEAN_EXPORT void lean_init_task_manager() {
lean_init_task_manager_using(get_lean_num_threads());
}

View file

@ -157,12 +157,6 @@ lthread::~lthread() {}
void lthread::join() { m_imp->join(); }
#endif
/* setThreadStackSize (sz : USize) : BaseIO Unit */
extern "C" LEAN_EXPORT lean_obj_res lean_internal_set_thread_stack_size(size_t sz) {
lthread::set_thread_stack_size(sz);
return lean_box(0);
}
LEAN_THREAD_VALUE(bool, g_finalizing, false);
bool in_thread_finalization() {

View file

@ -9,7 +9,6 @@ Author: Leonardo de Moura
#include "runtime/sstream.h"
#include "util/options.h"
#include "util/option_declarations.h"
#include "stdlib_flags.h"
#ifndef LEAN_DEFAULT_VERBOSE
#define LEAN_DEFAULT_VERBOSE true
@ -51,16 +50,6 @@ bool get_verbose(options const & opts) {
return opts.get_bool(*g_verbose, LEAN_DEFAULT_VERBOSE);
}
/* getDefaultVerbose (_ : Unit) : Bool */
extern "C" uint8 lean_internal_get_default_verbose(obj_arg) {
return LEAN_DEFAULT_VERBOSE;
}
/* getDefaultOptions (_ : Unit) : Options */
extern "C" obj_res lean_internal_get_default_options(obj_arg) {
return get_default_options().steal();
}
options join(options const & opts1, options const & opts2) {
kvmap r = opts2.m_value;
for (kvmap_entry const & e : opts1.m_value) {

View file

@ -68,7 +68,6 @@ public:
*/
friend options join(options const & opts1, options const & opts2);
object * steal() { return m_value.steal(); }
object * to_obj_arg() const { return m_value.to_obj_arg(); }
};

View file

@ -40,6 +40,7 @@ Author: Leonardo de Moura
#include "initialize/init.h"
#include "library/ir_interpreter.h"
#include "util/path.h"
#include "stdlib_flags.h"
#ifdef _MSC_VER
#include <io.h>
#define STDOUT_FILENO 1
@ -156,6 +157,33 @@ int getopt_long(int argc, char *in_argv[], const char *optstring, const option *
using namespace lean; // NOLINT
extern "C" obj_res lean_display_header();
static void display_header() {
consume_io_result(lean_display_header());
}
static void display_version(std::ostream & out) {
out << get_short_version_string() << "\n";
}
static void display_features(std::ostream & out) {
out << "[";
#if defined(LEAN_LLVM)
out << "LLVM";
#endif
out << "]\n";
}
extern "C" obj_res lean_display_help(uint8 use_stderr);
static void display_help(bool use_stderr) {
consume_io_result(lean_display_help(use_stderr));
}
static int only_src_deps = 0;
static int print_prefix = 0;
static int print_libdir = 0;
static int json_output = 0;
static struct option g_long_options[] = {
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
@ -172,12 +200,12 @@ static struct option g_long_options[] = {
{"stats", no_argument, 0, 'a'},
{"quiet", no_argument, 0, 'q'},
{"deps", no_argument, 0, 'd'},
{"src-deps", no_argument, 0, 'O'},
{"deps-json", no_argument, 0, 'N'},
{"src-deps", no_argument, &only_src_deps, 1},
{"deps-json", no_argument, 0, 'J'},
{"timeout", optional_argument, 0, 'T'},
{"c", optional_argument, 0, 'c'},
{"bc", optional_argument, 0, 'b'},
{"features", no_argument, 0, 'f'},
{"features", optional_argument, 0, 'f'},
{"exitOnPanic", no_argument, 0, 'e'},
#if defined(LEAN_MULTI_THREAD)
{"threads", required_argument, 0, 'j'},
@ -189,9 +217,9 @@ static struct option g_long_options[] = {
{"load-dynlib", required_argument, 0, 'l'},
{"setup", required_argument, 0, 'u'},
{"error", required_argument, 0, 'E'},
{"json", no_argument, 0, 'J'},
{"print-prefix", no_argument, 0, 'x'},
{"print-libdir", no_argument, 0, 'L'},
{"json", no_argument, &json_output, 1},
{"print-prefix", no_argument, &print_prefix, 1},
{"print-libdir", no_argument, &print_libdir, 1},
#ifdef LEAN_DEBUG
{"debug", required_argument, 0, 'B'},
#endif
@ -205,9 +233,90 @@ static char const * g_opt_str =
#endif
; // NOLINT
options set_config_option(options const & opts, char const * in) {
if (!in) return opts;
while (*in && std::isspace(*in))
++in;
std::string in_str(in);
auto pos = in_str.find('=');
if (pos == std::string::npos)
throw lean::exception("invalid -D parameter, argument must contain '='");
lean::name opt = lean::string_to_name(in_str.substr(0, pos));
std::string val = in_str.substr(pos+1);
auto decls = lean::get_option_declarations();
auto it = decls.find(opt);
if (it) {
switch (it->kind()) {
case lean::data_value_kind::Bool:
if (val == "true")
return opts.update(opt, true);
else if (val == "false")
return opts.update(opt, false);
else
throw lean::exception(lean::sstream() << "invalid -D parameter, invalid configuration option '" << opt
<< "' value, it must be true/false");
case lean::data_value_kind::Nat:
return opts.update(opt, static_cast<unsigned>(atoi(val.c_str())));
case lean::data_value_kind::String:
return opts.update(opt, val.c_str());
default:
throw lean::exception(lean::sstream() << "invalid -D parameter, configuration option '" << opt
<< "' cannot be set in the command line, use set_option command");
}
} else {
// More options may be registered by imports, so we leave validating them to the Lean side.
// This (minor) duplication will be resolved when this file is rewritten in Lean.
return opts.update(opt, val.c_str());
}
}
namespace lean {
extern "C" obj_res lean_shell_main(obj_arg args, obj_arg shell_opts);
int run_shell_main(int argc, char* argv[], object_ref const & shell_opts) {
extern "C" obj_res lean_shell_main(
obj_arg args,
obj_arg forwarded_args,
uint8 component,
uint8 print_prefix,
uint8 print_libdir,
uint8 use_stdin,
uint8 only_deps,
uint8 only_src_deps,
uint8 deps_json,
obj_arg opts,
uint32_t trust_level,
obj_arg root_dir,
obj_arg setup_file_name,
obj_arg olean_filename,
obj_arg ilean_filename,
obj_arg c_filename,
obj_arg bc_filename,
uint8 json_output,
obj_arg error_kinds,
uint8 print_stats,
uint8 run
);
uint32 run_shell_main(
int argc, char* argv[],
buffer<string_ref> forwarded_args,
int run_server,
bool print_prefix,
bool print_libdir,
bool use_stdin,
bool only_deps,
bool only_src_deps,
bool deps_json,
options const & opts,
uint32_t trust_level,
optional<std::string> const & root_dir,
optional<std::string> const & setup_file_name,
optional<std::string> const & olean_file_name,
optional<std::string> const & ilean_file_name,
optional<std::string> const & c_file_name,
optional<std::string> const & bc_file_name,
bool json_output,
array_ref<name> const & error_kinds,
bool print_stats,
bool run
) {
list_ref<string_ref> args;
while (argc > 0) {
argc--;
@ -215,51 +324,45 @@ int run_shell_main(int argc, char* argv[], object_ref const & shell_opts) {
}
return get_io_scalar_result<uint32>(lean_shell_main(
args.steal(),
shell_opts.to_obj_arg()
to_list_ref(forwarded_args).to_obj_arg(),
run_server,
print_prefix,
print_libdir,
use_stdin,
only_deps, only_src_deps, deps_json,
opts.to_obj_arg(),
trust_level,
root_dir ? mk_option_some(mk_string(*root_dir)) : mk_option_none(),
setup_file_name ? mk_option_some(mk_string(*setup_file_name)) : mk_option_none(),
olean_file_name ? mk_option_some(mk_string(*olean_file_name)) : mk_option_none(),
ilean_file_name ? mk_option_some(mk_string(*ilean_file_name)) : mk_option_none(),
c_file_name ? mk_option_some(mk_string(*c_file_name)) : mk_option_none(),
bc_file_name ? mk_option_some(mk_string(*bc_file_name)) : mk_option_none(),
json_output,
error_kinds.to_obj_arg(),
print_stats,
run
));
}
extern "C" object* lean_init_search_path();
void init_search_path() {
consume_io_result(lean_init_search_path());
get_io_scalar_result<unsigned>(lean_init_search_path());
}
extern "C" obj_res lean_shell_options_mk(obj_arg);
object_ref mk_shell_options() {
return object_ref(lean_shell_options_mk(box(0)));
extern "C" object* lean_environment_free_regions(object * env);
void environment_free_regions(elab_environment && env) {
consume_io_result(lean_environment_free_regions(env.steal()));
}
}
extern "C" obj_res lean_shell_options_process(obj_arg shell_opts, uint32 opt, obj_arg opt_arg);
bool process_shell_option(object_ref & shell_opts, int opt, char const * optarg, int & rc) {
auto optarg_ref = optarg ? mk_option_some(mk_string(optarg)) : mk_option_none();
auto r = lean_shell_options_process(shell_opts.steal(), opt, optarg_ref);
if (io_result_is_ok(r)) {
shell_opts = object_ref(io_result_get_value(r), true);
dec_ref(r);
return false;
} else {
rc = unbox(io_result_get_error(r));
dec_ref(r);
return true;
void check_optarg(char const * option_name) {
if (!optarg) {
std::cerr << "error: argument missing for option '-" << option_name << "'" << std::endl;
std::exit(1);
}
}
extern "C" uint8 lean_shell_options_get_run(obj_arg shell_opts);
bool get_shell_run(object_ref const & shell_opts) {
return lean_shell_options_get_run(shell_opts.to_obj_arg());
}
extern "C" uint8 lean_shell_options_get_profiler(obj_arg shell_opts);
bool get_shell_profiler(object_ref const & shell_opts) {
return lean_shell_options_get_profiler(shell_opts.to_obj_arg());
}
extern "C" uint32 lean_shell_options_get_num_threads(obj_arg shell_opts);
unsigned get_shell_num_threads(object_ref const & shell_opts) {
return lean_shell_options_get_num_threads(shell_opts.to_obj_arg());
}
}
extern "C" object * lean_enable_initializer_execution();
namespace lean {
@ -309,6 +412,21 @@ extern "C" LEAN_EXPORT int lean_main(int argc, char ** argv) {
auto init_start = std::chrono::steady_clock::now();
lean::initializer init;
second_duration init_time = std::chrono::steady_clock::now() - init_start;
bool run = false;
optional<std::string> olean_fn;
optional<std::string> ilean_fn;
optional<std::string> setup_fn;
bool use_stdin = false;
unsigned trust_lvl = LEAN_BELIEVER_TRUST_LEVEL + 1;
bool only_deps = false;
bool deps_json = false;
bool stats = false;
// 0 = don't run server, 1 = watchdog, 2 = worker
int run_server = 0;
unsigned num_threads = 0;
#if defined(LEAN_MULTI_THREAD)
num_threads = hardware_concurrency();
#endif
try {
// Remark: This currently runs under `IO.initializing = true`.
@ -319,35 +437,167 @@ extern "C" LEAN_EXPORT int lean_main(int argc, char ** argv) {
}
consume_io_result(lean_enable_initializer_execution());
int rc;
object_ref shell_opts;
try {
shell_opts = mk_shell_options();
for (;;) {
int c = getopt_long(argc, argv, g_opt_str, g_long_options, NULL);
if (c == -1)
break; // end of command line
if (process_shell_option(shell_opts, c, optarg, rc))
return rc; // option processing triggered an early exit
if (get_shell_run(shell_opts))
break; // stop consuming arguments after `--run`
options opts = get_default_options();
optional<std::string> c_output;
optional<std::string> llvm_output;
optional<std::string> root_dir;
buffer<string_ref> forwarded_args;
buffer<name> error_kinds;
while (!run) { // stop consuming arguments after `--run`
int c = getopt_long(argc, argv, g_opt_str, g_long_options, NULL);
if (c == -1)
break; // end of command line
if (c == 0)
continue; // long-only option
switch (c) {
case 'e':
lean_set_exit_on_panic(true);
break;
case 'j':
num_threads = static_cast<unsigned>(atoi(optarg));
forwarded_args.push_back(string_ref("-j" + std::string(optarg)));
break;
case 'v':
display_header();
return 0;
case 'V':
display_version(std::cout);
return 0;
case 'g':
std::cout << LEAN_GITHASH << "\n";
return 0;
case 'h':
display_help(/* useStderr */ false);
return 0;
case 'f':
display_features(std::cout);
return 0;
case 'c':
check_optarg("c");
c_output = optarg;
break;
case 'b':
check_optarg("bc");
llvm_output = optarg;
break;
case 's':
lean::lthread::set_thread_stack_size(
static_cast<size_t>((atoi(optarg) / 4) * 4) * static_cast<size_t>(1024));
forwarded_args.push_back(string_ref("-s" + std::string(optarg)));
break;
case 'I':
use_stdin = true;
break;
case 'r':
run = true;
break;
case 'o':
olean_fn = optarg;
break;
case 'i':
ilean_fn = optarg;
break;
case 'R':
root_dir = optarg;
forwarded_args.push_back(string_ref("-R" + std::string(optarg)));
break;
case 'M':
check_optarg("M");
opts = opts.update(get_max_memory_opt_name(), static_cast<unsigned>(atoi(optarg)));
forwarded_args.push_back(string_ref("-M" + std::string(optarg)));
break;
case 'T':
check_optarg("T");
opts = opts.update(get_timeout_opt_name(), static_cast<unsigned>(atoi(optarg)));
forwarded_args.push_back(string_ref("-T" + std::string(optarg)));
break;
case 't':
check_optarg("t");
trust_lvl = atoi(optarg);
forwarded_args.push_back(string_ref("-t" + std::string(optarg)));
break;
case 'q':
opts = opts.update(lean::get_verbose_opt_name(), false);
break;
case 'd':
only_deps = true;
break;
case 'J':
only_deps = true;
deps_json = true;
break;
case 'a':
stats = true;
break;
case 'D':
try {
check_optarg("D");
opts = set_config_option(opts, optarg);
forwarded_args.push_back(string_ref("-D" + std::string(optarg)));
} catch (lean::exception & ex) {
std::cerr << ex.what() << std::endl;
return 1;
}
break;
case 'S':
run_server = 1;
break;
case 'W':
run_server = 2;
break;
case 'P':
opts = opts.update("profiler", true);
break;
#if defined(LEAN_DEBUG)
case 'B':
check_optarg("B");
lean::enable_debug(optarg);
break;
#endif
case 'p':
check_optarg("p");
lean::load_plugin(optarg);
forwarded_args.push_back(string_ref("--plugin=" + std::string(optarg)));
break;
case 'l':
check_optarg("l");
lean::load_dynlib(optarg);
forwarded_args.push_back(string_ref("--load-dynlib=" + std::string(optarg)));
break;
case 'u':
check_optarg("u");
setup_fn = optarg;
break;
case 'E':
check_optarg("E");
error_kinds.push_back(string_to_name(std::string(optarg)));
break;
default:
std::cerr << "Unknown command line option\n";
display_help(/* useStderr */ true);
return 1;
}
} catch (lean::throwable & ex) {
std::cerr << "error: " << ex.what() << std::endl;
return 1;
}
lean::io_mark_end_initialization();
if (get_shell_profiler(shell_opts)) {
if (get_profiler(opts)) {
g_lean_report_task_get_blocked_time = report_task_get_blocked_time;
report_profiling_time("initialization", init_time);
}
scoped_task_manager scope_task_man(get_shell_num_threads(shell_opts));
scoped_task_manager scope_task_man(num_threads);
try {
return run_shell_main(argc - optind, argv + optind, shell_opts);
return run_shell_main(
argc - optind, argv + optind,
forwarded_args, run_server, print_prefix, print_libdir,
use_stdin, only_deps, only_src_deps, deps_json,
opts, trust_lvl, root_dir, setup_fn,
olean_fn, ilean_fn, c_output, llvm_output,
json_output, error_kinds, stats, run
);
} catch (lean::throwable & ex) {
std::cerr << ex.what() << "\n";
} catch (std::bad_alloc & ex) {