diff --git a/src/Lean/Elab/Frontend.lean b/src/Lean/Elab/Frontend.lean index 671da51011..8f29ee4d5c 100644 --- a/src/Lean/Elab/Frontend.lean +++ b/src/Lean/Elab/Frontend.lean @@ -132,6 +132,47 @@ def process (input : String) (env : Environment) (opts : Options) (fileName : Op let s ← IO.processCommands inputCtx { : Parser.ModuleParserState } (Command.mkState env {} opts) pure (s.commandState.env, s.commandState.messages) +/-- +Parses values of options registered during import and left by the C++ frontend as strings, fails if +any option names remain unknown. +-/ +def reparseOptions (opts : Options) : IO Options := do + let mut opts := opts + let decls ← getOptionDecls + for (name, val) in opts do + let .ofString val := val + | continue -- Already parsed by C++ + -- Options can be prefixed with `weak` in order to turn off the error when the option is not + -- defined + let weak := name.getRoot == `weak + if weak then + opts := opts.erase name + let name := name.replacePrefix `weak Name.anonymous + let some decl := decls.find? name + | unless weak do + throw <| .userError s!"invalid -D parameter, unknown configuration option '{name}' + +If the option is defined in this library, use '-D{`weak ++ name}' to set it conditionally" + + 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 + @[export lean_run_frontend] def runFrontend (input : String) @@ -151,6 +192,8 @@ def runFrontend let (header, parserState, messages) ← Parser.parseHeader inputCtx -- allow `env` to be leaked, which would live until the end of the process anyway let (env, messages) ← processHeader (leakEnv := true) header opts messages inputCtx trustLevel + -- now that imports have been loaded, check options again + let opts ← reparseOptions opts let env := env.setMainModule mainModuleName let mut commandState := Command.mkState env messages opts let elabStartTime := (← IO.monoNanosNow).toFloat / 1000000000 diff --git a/src/util/shell.cpp b/src/util/shell.cpp index b5053f84ba..779c682b89 100644 --- a/src/util/shell.cpp +++ b/src/util/shell.cpp @@ -310,7 +310,9 @@ options set_config_option(options const & opts, char const * in) { << "' cannot be set in the command line, use set_option command"); } } else { - throw lean::exception(lean::sstream() << "invalid -D parameter, unknown configuration option '" << opt << "'"); + // 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()); } } diff --git a/tests/pkg/user_opt/UserOptCmdline.lean b/tests/pkg/user_opt/UserOptCmdline.lean new file mode 100644 index 0000000000..45097c7a84 --- /dev/null +++ b/tests/pkg/user_opt/UserOptCmdline.lean @@ -0,0 +1,12 @@ +import UserOpt.Opts + +/-! Test setting user options from lakefile. -/ + +open Lean + +def tst2 : MetaM Unit := do + assert! myBoolOpt.get (← getOptions) + assert! myNatOpt.get (← getOptions) == 4 + pure () + +#eval tst2 diff --git a/tests/pkg/user_opt/lakefile.lean b/tests/pkg/user_opt/lakefile.lean deleted file mode 100644 index 5657d60ed1..0000000000 --- a/tests/pkg/user_opt/lakefile.lean +++ /dev/null @@ -1,5 +0,0 @@ -import Lake -open System Lake DSL - -package user_opt -@[default_target] lean_lib UserOpt diff --git a/tests/pkg/user_opt/lakefile.toml b/tests/pkg/user_opt/lakefile.toml new file mode 100644 index 0000000000..4d7e2d045f --- /dev/null +++ b/tests/pkg/user_opt/lakefile.toml @@ -0,0 +1,9 @@ +name = "user_opt" +defaultTargets = ["UserOpt", "UserOptCmdline"] + +[[lean_lib]] +name = "UserOpt" + +[[lean_lib]] +name = "UserOptCmdline" +leanOptions = { myBoolOpt = true, weak.myNatOpt = 4, weak.notMyNatOpt = 5 }