diff --git a/changes/unreleased/grouped-help.changed.md b/changes/unreleased/grouped-help.changed.md new file mode 100644 index 0000000000..bee3523475 --- /dev/null +++ b/changes/unreleased/grouped-help.changed.md @@ -0,0 +1,3 @@ +- **`sysml -help` and the manual page list the options by task.** The 84 flags come under headings — General, Evaluating, Checking a model, Running behaviors, Analysis engines, Checking every schedule, Converting and migrating, Compiling natively, Rendering views, Rendering documents, Styling HTML documents, Syncing against a repository, Diagnostics and profiling, Deprecated — instead of in one alphabetical list. Each is spelled once with its shorthand folded in (`-e, -eval `, `-o, -output `) and its argument named (``, ``, ``) rather than typed (`string`, `value`); a description is a wrapped line or two, with the detail it carried moved into the section on that mode (`Checking a model`, `Running actions and state machines`, `Checking every schedule`, ...), and the three `-pdf-*` spellings sit under *Deprecated* pointing at their `-doc-*` names. The examples come first. A flag added without a heading fails a test. +- **A mistyped flag prints the error, the synopsis and `Run 'sysml -help' for the options.`** — three lines, rather than the whole help after the error. +- **`%help` groups every REPL command under a heading and wraps its description.** The commands that had none come under *Session*, *Settings*, *Analysis engines* and *Checking every schedule*; a description wraps into the column after its command, and a signature too wide for that column takes a line of its own. `%exit` is noted beside `%quit` rather than listed as a command. diff --git a/cmd/sysml/main.go b/cmd/sysml/main.go index 6d0f89e6e8..5740662402 100644 --- a/cmd/sysml/main.go +++ b/cmd/sysml/main.go @@ -274,10 +274,10 @@ func flagGiven(name string) bool { return given } -// printUsage writes the help to w: the caller chooses the stream, since help -// asked for is a result and help shown over a misuse belongs with the error. -func printUsage(w io.Writer) { - doc().WriteText(w, flag.CommandLine) +// printUsage writes the help for the flags of fs to w: help asked for is a +// result, so it goes on stdout. +func printUsage(w io.Writer, fs *flag.FlagSet) { + doc().WriteText(w, fs) } // printMan writes the command's manual page, rendered from the same description @@ -289,8 +289,9 @@ func printMan(w io.Writer) { // runCLI carries out what the command line asked for and returns the exit // status, so a profile started for the run is written before the process exits. func runCLI() int { - // Usage shown over a misuse goes on the stream the error naming it goes on. - flag.Usage = func() { printUsage(flag.CommandLine.Output()) } + // A misuse is answered on the error's stream with the synopsis and where the + // help is, not the help itself, which would bury the error. + flag.Usage = func() { doc().WriteHint(flag.CommandLine.Output()) } // The tool manifest is read before the flags, since -engine is checked against its engines. if err := resolveEngines(); err != nil { @@ -306,7 +307,7 @@ func runCLI() int { // Help that was asked for is the result of the run: it belongs on stdout, where // it can be piped, and the run did what was asked. if showHelp { - printUsage(os.Stdout) + printUsage(os.Stdout, flag.CommandLine) return exitHolds } diff --git a/cmd/sysml/streams_test.go b/cmd/sysml/streams_test.go index f74ed09636..410c34c0db 100644 --- a/cmd/sysml/streams_test.go +++ b/cmd/sysml/streams_test.go @@ -156,14 +156,14 @@ func TestStreamsAndStatus(t *testing.T) { model: checkModel, args: []string{"-h"}, status: exitHolds, - stdout: []string{"Usage: sysml [options] [file...]", "-convert string"}, + stdout: []string{"Usage: sysml [options] [file...]", "-convert "}, emptyErrs: true, }, { - name: "a flag that is not defined is reported with the usage on stderr", + name: "a flag that is not defined is reported with a pointer to the help on stderr", model: checkModel, args: []string{"--nosuchflag"}, status: exitUnevaluable, - stderr: []string{"flag provided but not defined: -nosuchflag", "Usage: sysml [options] [file...]"}, + stderr: []string{"flag provided but not defined: -nosuchflag", "Usage: sysml [options] [file...]", "Run 'sysml -help' for the options."}, emptyOut: true, }} diff --git a/cmd/sysml/usage.go b/cmd/sysml/usage.go index 9c88963e9e..a95ae3ff16 100644 --- a/cmd/sysml/usage.go +++ b/cmd/sysml/usage.go @@ -24,8 +24,10 @@ func doc() usage.Doc { "otherwise it does what was asked and exits on the verdict, which " + "is what lets a run gate a build.", }, + Options: optionGroups(), Sections: []usage.Section{{ - Title: "Examples", + Title: "Examples", + BeforeOptions: true, Examples: []usage.Example{ usage.Ex("sysml", "Start interactive REPL"), usage.Ex(`sysml -e "5 + 3"`, "Evaluate and exit"), @@ -56,7 +58,42 @@ func doc() usage.Doc { usage.Ex("sysml -schedule explore -action Drive m.sysml", "Run every linearization; table the outcomes"), usage.Ex("sysml -satisfy -json model.sysml", "Report the verdicts as JSON"), }, - Paragraphs: []string{"Each check flag may be repeated."}, + Paragraphs: []string{ + "Each check flag may be repeated. -validate= checks every " + + "assertion about an object -instantiate created and the objects it " + + "holds: an object named, #, or a path such as car.engine. " + + "-requirement and -satisfy report beside each verdict the verdict of " + + "every verification case verifying the requirement. -analysis takes " + + "arguments for the case's inputs and an object as its subject, as " + + "-analysis \"Pkg::Case(3.0) Pkg::part\"; a verification case also " + + "reports the verdict its body produced.", + }, + }, { + Title: "Running actions and state machines", + Paragraphs: []string{ + "-action runs an action to completion and -state a state machine, both " + + "on one shared clock that -advance runs for the given simulated time " + + "units; without -advance a state machine takes only its initial " + + "transition. Either names a definition or usage, or one followed by " + + "the object to perform it on, as -action \"Drive rover1\".", + "-schedule is the policy every run resolves its choice points under — " + + "concurrent tokens, overlapping guards, competing transitions: " + + "declared, reverse (the default), seed: for a reproducible " + + "pseudo-random order, explore[:runs=N,depth=D] to run every " + + "linearization within the budget and table the distinct outcomes, " + + "or replay: to follow a witness file's choice lines move for " + + "move, then reverse.", + "Under -schedule explore, -engine check, smt or all, each run creates " + + "objects of its own before the behaviors start: one per -instantiate, " + + "which a -state or -action named alone attaches to, and one per " + + "-action or -state naming a definition or usage to create or a path " + + "into one such as mission.rover. The declaration is created once per " + + "run, so machines on sibling parts share it and its connectors.", + "-jobs is how many runs of one check may go concurrently — the " + + "linearizations of an exploration, the engines -engine all consults " + + "— each on a worker of its own over the shared model; the result is " + + "the same at any count.", + }, }, { Title: "Analysis engines", Examples: []usage.Example{ @@ -85,6 +122,56 @@ func doc() usage.Doc { "engine reached lowers the strength and is named. Under -json each " + "check carries the plan and one results[] entry per engine that " + "answered, with its engine, claim, strength, bounds and witness.", + "-engines lists each engine's name, kind (built-in, tool, engine, policy, " + + "sampler), protocol, authority, the questions it answers and whether " + + "its process is found, with the manifest entry and resolved command " + + "of each external one; -probe also starts each external engine once " + + "and checks its describe against its manifest entry field by field. " + + "An engine is named run, explore, check, smt, sweep, solve, " + + "tool: from OPENSYSML_TOOLS, or as an external engine from " + + "OPENSYSML_ENGINES. -engine explore is -schedule explore; -engine " + + "check searches every schedule of each -action for a violation, " + + "deadlock, failure or divergence; -engine smt decides each " + + "-check-property over every schedule and every value of the action's " + + "free inputs through an SMT solver.", + }, + }, { + Title: "Checking every schedule", + Examples: []usage.Example{ + usage.Ex("sysml -engine check -action Go -check-property Safe m.sysml", "Search every schedule for Safe"), + usage.Ex("sysml -engine check -action Go -check-diverge speed m.sysml", "Is speed schedule-dependent?"), + usage.Ex("sysml -engine smt -action Go -check-input t -check-property Safe m.sysml", ""), + usage.Ex("sysml -engine check -action Go -check-witness out m.sysml", "A witness file per finding"), + }, + Paragraphs: []string{ + "-check-property evaluates a constraint or requirement at every stable " + + "state of the action, on the performing object when there is one, " + + "and reports a schedule at which it is false. -check-diverge reports " + + "a feature sensitive when schedules leave it with different final " + + "values — check searching the schedules, smt asking the solver for " + + "two that end it apart — named as x, step.out for a performed node's " + + "output or this.level for the performing object's (not covered under " + + "smt); a name nothing holds is refused, and without it every " + + "attribute of the action and of its performing object is checked, " + + "the action's own when it has none.", + "Under smt every input the model leaves unbound is free and every bound " + + "one is pinned; -check-input leaves a bound feature free in its " + + "declared domain, and a name that is not a feature the action reads " + + "is refused. -check-assume assumes a constraint or requirement over " + + "the initial state; a set no initial state satisfies is reported not " + + "covered, never proved.", + "-check-witness writes a file for each violation and each divergent " + + "value — the inputs the solver chose, the schedule's choices, a blank " + + "line, then the run's trace — which -schedule replay: and " + + "%replay follow. A bound is named when it is hit: -check-depth is the " + + "most moves one schedule may make before the search backtracks, or " + + "the moves smt unrolls the action to; -check-states the most distinct " + + "states the search may visit, the same figure -engine all gives an " + + "exploration as its runs; -check-unroll the most iterations of one " + + "loop smt unrolls; -check-timeout the time the plan may run for and " + + "the time each solver query may take in place of " + + "OPENSYSML_SMT_TIMEOUT — a search it stops is reported incomplete " + + "with the states and depth reached, not as a verdict.", }, }, { Title: "Sweeping and sampling a parameter", @@ -162,7 +249,10 @@ func doc() usage.Doc { }, Paragraphs: []string{ "The input format is taken from the file extension (.sysml, .kerml, " + - ".ttl) unless -from names it. Converting to the format it is " + + ".ttl) unless -from names it: sysml, kerml, ttl, turtle, rdf, or " + + "xmi, uml or mdzip for a SysML v1 model to migrate, whose " + + "element-by-element report -migration-report writes out. " + + "Converting to the format it is " + "already in rewrites the input: notation is reformatted, Turtle " + "is normalized.", // Printed rather than restated, so the help cannot drift from what a @@ -229,6 +319,9 @@ func doc() usage.Doc { "it. Notices — an empty view, an element the rendering cannot " + "represent — go on stderr. Every file named is loaded as one " + "model, so a view may expose elements a sibling file declares. " + + "-render-form names the form written — text, mermaid, markdown, dot " + + "or plantuml; by default -render takes it from the destination and " + + "-render-all writes each kind's machine form. " + "A graph-shaped rendering is written as a Mermaid diagram by " + "default, as Graphviz DOT with -render-form dot and as PlantUML with " + "-render-form plantuml, which also writes a sequence rendering; neither " + @@ -364,94 +457,249 @@ func solverEnvironment() []usage.Item { } } -// registerFlags declares the command's flags on fs, so the help, the man page -// and a run all read one declaration of each. +// registerFlags declares the command's flags on fs; each flag's group and +// placeholder live in optionGroups, and a flag missing there fails the tests. func registerFlags(fs *flag.FlagSet) { fs.BoolVar(&showHelp, "help", false, "Show this help and exit") - fs.BoolVar(&showHelp, "h", false, "Show this help (shorthand)") + fs.BoolVar(&showHelp, "h", false, "Show this help and exit") + fs.BoolVar(&showVersion, "version", false, "Show the version and exit") + fs.BoolVar(&showVersion, "v", false, "Show the version and exit") fs.BoolVar(&showMan, "man", false, "Write this command's manual page, in roff, to stdout and exit") - fs.Var(&evalExprs, "eval", "Evaluate expression and exit (can be specified multiple times)") - fs.Var(&evalExprs, "e", "Evaluate expression and exit (shorthand)") - fs.BoolVar(&showVersion, "version", false, "Show version information") - fs.BoolVar(&showVersion, "v", false, "Show version (shorthand)") - fs.BoolVar(&debugMode, "debug", false, "Report every diagnostic over the whole session buffer, with the pass that produced it") - fs.BoolVar(&quietMode, "quiet", false, "Report errors only, suppressing warnings") + + fs.Var(&evalExprs, "eval", "Evaluate this expression, against the model when one is loaded, and exit (repeatable)") + fs.Var(&evalExprs, "e", "Evaluate this expression, against the model when one is loaded, and exit (repeatable)") + fs.StringVar(&queryText, "query", "", "Evaluate this OSLC Query text against the model and exit") + + fs.Var(&modelChecks.validate, "validate", "Report the model's diagnostics and exit, nonzero on an error; -validate= checks instead every assertion about that object (repeatable)") fs.BoolVar(&strictMode, "strict", false, "Judge the model as conforming SysML v2: notation no pinned production admits is an error, not a warning") - fs.BoolVar(&traceMode, "trace", false, "Report each execution step: expression evaluation, calc invocation, action tokens, state transitions") - fs.Var(&schedule, "schedule", "Scheduling policy every run resolves its choice points under (concurrent tokens, overlapping guards, competing transitions): declared, reverse (default), seed: for a reproducible pseudo-random order, explore[:runs=N,depth=D] to run every linearization within the budget and table the distinct outcomes, or replay: to follow a witness file's choice lines move for move, then reverse") - fs.BoolVar(&listEngines, "engines", false, "List the analysis engines this build knows — name, kind (built-in, tool, engine, policy, sampler), protocol, authority, the questions each answers and whether its process is found — with the manifest entry and resolved command of each external one, spawning nothing, and exit") - fs.BoolVar(&probeEngines, "probe", false, "With -engines, also start each external engine once, check its describe against its manifest entry field by field and report the outcome as its status") - fs.Var(&engine, "engine", "Analysis engine every check is put to: auto (default) picks the strongest engine covering the question, all puts it to every covering engine in name order and composes their answers, or an engine by name (run, explore, check, smt, sweep, solve, tool: from OPENSYSML_TOOLS, or an external engine from OPENSYSML_ENGINES), whose refusal is then the answer; -engine explore is -schedule explore, -engine check searches every schedule of each -action for a violation, deadlock, failure or divergence, and -engine smt decides each -check-property over every schedule and every value of the action's free inputs through an SMT solver") - fs.Var(&jobsFlag, "jobs", "Runs of one check that may go concurrently — the linearizations of an exploration, the engines -engine all consults — each on a worker of its own over the shared model; the result is the same at any count. Default OPENSYSML_JOBS, else the number of CPUs") + fs.Var(&modelChecks.constraints, "constraint", "Evaluate this constraint and exit (repeatable)") + fs.Var(&modelChecks.requirements, "requirement", "Evaluate this requirement, and every verification case verifying it, and exit (repeatable)") + fs.Var(&modelChecks.satisfy, "satisfy", "Evaluate every satisfaction assertion, or with -satisfy= those the named element states, and exit (repeatable)") + fs.Var(&modelChecks.calcs, "calc", "Invoke this calculation and report its result, as -calc \"Fall(3, 4)\" (repeatable)") + fs.Var(&modelChecks.analyses, "analysis", "Run this analysis or verification case and report its outputs and verdict, as -analysis \"Pkg::Case(3.0) Pkg::part\" (repeatable)") + fs.Var(&modelChecks.queries, "run-query", "Execute this document query and report its rows, as -run-query \"Heavy root=telescope\" (repeatable)") + fs.Var(&modelChecks.instantiate, "instantiate", "Create an object of this definition or usage before the checks, so a verdict is about it (repeatable)") + fs.BoolVar(&modelChecks.jsonOut, "json", false, "Report checks as one JSON document rather than as lines") + + fs.Var(&modelChecks.actions, "action", "Run this action to completion, as -action \"Drive rover1\" to run it on an object (repeatable)") + fs.Var(&modelChecks.states, "state", "Run this state machine, as -state \"Mission rover1\" to run it on an object (repeatable)") + fs.Var(&modelChecks.advance, "advance", "Simulated time units to run the -action and -state behaviors for, on one shared clock; without it a state machine takes only its initial transition") + fs.Var(&schedule, "schedule", "Policy every run resolves its choice points under: declared, reverse (default), seed:, explore[:runs=N,depth=D] or replay:") + fs.Var(&modelChecks.seed, "seed", "Seed the model's own draws — weighted decisions, random functions — and those of -samples and -runs; the same seed draws the same run or table") + fs.Var(&modelChecks.runs, "runs", "Run the -action this many times, each run seeded from -seed, and table the -observe features; needs -seed") + fs.Var(&modelChecks.observe, "observe", "Report this feature of the -runs action, or clock for the time it completed at; default every feature it holds and the clock (repeatable)") + fs.Var(&modelChecks.sweeps, "sweep", "Run the -analysis or -calc once per value of this range, as -sweep \"n=1..8:2\"; several ranges run their cartesian product (repeatable)") + fs.Var(&modelChecks.samples, "samples", "Draw this many values uniformly from each -sweep range instead of stepping through it; needs -seed") + fs.Var(&jobsFlag, "jobs", "Runs of one check that may go concurrently, each on a worker of its own; default OPENSYSML_JOBS, else the number of CPUs") + + fs.BoolVar(&listEngines, "engines", false, "List the analysis engines this build knows and their status, spawning nothing, and exit") + fs.BoolVar(&probeEngines, "probe", false, "With -engines, also start each external engine once and report the outcome as its status") + fs.Var(&engine, "engine", "Analysis engine every check is put to: auto (default), all, or one by name — run, explore, check, smt, sweep, solve, tool: or an external engine") + + fs.Var(&modelChecks.checker.properties, "check-property", "Evaluate this constraint or requirement at every stable state of the action and report a schedule at which it is false (repeatable)") + fs.Var(&modelChecks.checker.diverge, "check-diverge", "Report this feature sensitive when schedules leave it with different final values, as x, step.out or this.level; default every attribute (repeatable)") + fs.Var(&modelChecks.checker.inputs, "check-input", "Under smt, leave this feature of the action free in its declared domain although the model binds it (repeatable)") + fs.Var(&modelChecks.checker.assume, "check-assume", "Under smt, assume this constraint or requirement over the initial state of the action (repeatable)") + fs.StringVar(&modelChecks.checker.witness, "check-witness", "", "Write a witness file for each violation and each divergent value to this directory, for -schedule replay: and %replay to follow") + modelChecks.checker.depth.flag, modelChecks.checker.states.flag, modelChecks.checker.unroll.flag = "check-depth", "check-states", "check-unroll" + fs.Var(&modelChecks.checker.depth, "check-depth", "The most moves one schedule may make before the search backtracks, or the moves smt unrolls the action to (default 10000 for check, 40 for smt)") + fs.Var(&modelChecks.checker.states, "check-states", "Under check, the most distinct states the search may visit (default 1000000)") + fs.Var(&modelChecks.checker.unroll, "check-unroll", "Under smt, the most iterations of one loop the solver unrolls before it stops (default 4)") + fs.Var(&modelChecks.checker.timeout, "check-timeout", "The time the check's plan may run for, as 30s or 2m, and the time each smt solver query may take in place of OPENSYSML_SMT_TIMEOUT") + fs.StringVar(&convertFormat, "convert", "", "Convert the model to this format instead of running it: sysml, kerml, ttl, turtle or rdf (RDF is experimental)") - fs.StringVar(&queryText, "query", "", "Evaluate OSLC Query text against the model instead of running the REPL") - fs.StringVar(&outputPath, "output", "", "Write conversion output to this file (default: stdout)") - fs.StringVar(&outputPath, "o", "", "Write conversion output to this file (shorthand)") - fs.StringVar(&fromFormat, "from", "", "Input format for -convert: sysml, kerml, ttl, turtle, rdf, or xmi/uml/mdzip for a SysML v1 model to migrate (experimental; default: from the input's extension)") - fs.StringVar(&migrationReport, "migration-report", "", "With -convert from xmi: write the element-by-element migration report to this file (JSON when it ends in .json, text otherwise)") - fs.StringVar(&renderView, "render", "", "Render this view of the model (every file named, loaded as one) instead of running it, in the form its render member states") + fs.StringVar(&fromFormat, "from", "", "Input format for -convert: sysml, kerml, ttl, turtle, rdf, or xmi, uml or mdzip for a SysML v1 model to migrate (experimental); default the input's extension") + fs.StringVar(&outputPath, "output", "", "Write what -convert, -compile, -render or -render-document produces to this file instead of stdout") + fs.StringVar(&outputPath, "o", "", "Write what -convert, -compile, -render or -render-document produces to this file instead of stdout") + fs.StringVar(&migrationReport, "migration-report", "", "With -convert from xmi, write the element-by-element migration report to this file: JSON when it ends in .json, text otherwise") + + fs.StringVar(&compileCalc, "compile", "", "Compile this calc def to a native executable named by -o, as -compile Pkg::Fib") + fs.StringVar(&compileTarget, "target", "c", "Backend -compile generates code for: c or go") + fs.BoolVar(&compileSource, "source", false, "With -compile, write the generated source to -o instead of building it") + + fs.StringVar(&renderView, "render", "", "Render this view of the model instead of running it, in the form its render member states") fs.StringVar(&renderAllDir, "render-all", "", "Render every declared view into this directory") - fs.StringVar(&renderDoc, "render-document", "", "Compile this document definition, run its queries and write the rendered Markdown") - fs.StringVar(&renderDocsDir, "render-documents", "", "Render every document definition as linked Markdown into this directory") - fs.StringVar(&renderForm, "render-form", "", "Form -render or -render-all writes: text, mermaid, markdown, dot or plantuml (default: destination-dependent for -render, each kind's machine form for -render-all)") - fs.StringVar(&renderPalette, "render-palette", "", "Palette the DOT or PlantUML form of -render or -render-all fills nodes from, by keyword family: okabe-ito, tol-bright, tol-muted, tol-light, brewer-set2, brewer-dark2, viridis or cividis (default: black and white)") - fs.StringVar(&docForm, "doc-form", "", "Form -render-document and -render-documents write: markdown (default), html or pdf, which drives an external converter") - fs.StringVar(&diagramForm, "diagram-form", "", "Form the graph-shaped diagrams of -render-document and -render-documents are written in: mermaid (default), dot or plantuml; a table-kind view is a table either way") + fs.StringVar(&renderForm, "render-form", "", "Form -render or -render-all writes: text, mermaid, markdown, dot or plantuml; default from the destination for -render, each kind's machine form for -render-all") + fs.StringVar(&renderPalette, "render-palette", "", "Palette the dot or plantuml form fills nodes from, by keyword family: okabe-ito, tol-bright, tol-muted, tol-light, brewer-set2, brewer-dark2, viridis or cividis; default black and white") + + fs.StringVar(&renderDoc, "render-document", "", "Compile this document definition, run its queries and write the rendered document") + fs.StringVar(&renderDocsDir, "render-documents", "", "Render every document definition, linked to one another, into this directory") + fs.StringVar(&docForm, "doc-form", "", "Form the documents are written in: markdown (default), html or pdf, which drives an external converter") + fs.StringVar(&diagramForm, "diagram-form", "", "Form the documents' graph-shaped diagrams are written in: mermaid (default), dot or plantuml; a table-kind view is a table either way") + fs.BoolVar(&pdfTitlePage, "doc-title-page", false, "Put the document title on a page of its own (html or pdf)") + fs.BoolVar(&pdfTOC, "doc-toc", false, "Write a table of contents ahead of the content (html or pdf)") + fs.BoolVar(&pdfNumbering, "doc-number-sections", false, "Number the section headings hierarchically (html or pdf)") fs.StringVar(&pdfEngine, "pdf-engine", "", "Converter -doc-form pdf drives: weasyprint (default), pandoc or prince") - fs.BoolVar(&pdfTitlePage, "pdf-title-page", false, "Put the document title on a page of its own (-doc-form pdf)") - fs.BoolVar(&pdfTOC, "pdf-toc", false, "Write a table of contents ahead of the content (-doc-form pdf)") - fs.BoolVar(&pdfNumbering, "pdf-number-sections", false, "Number the section headings hierarchically (-doc-form pdf)") - fs.BoolVar(&pdfTitlePage, "doc-title-page", false, "Put the document title on a page of its own (-doc-form html or pdf)") - fs.BoolVar(&pdfTOC, "doc-toc", false, "Write a table of contents ahead of the content (-doc-form html or pdf)") - fs.BoolVar(&pdfNumbering, "doc-number-sections", false, "Number the section headings hierarchically (-doc-form html or pdf)") - fs.Var(&htmlCSS, "html-css", "Style the HTML with this stylesheet: a file is inlined, a URL is linked (repeatable, applied in order after the default sheet)") - fs.StringVar(&htmlTheme, "html-theme", "", "Style the HTML page with a bundled theme layered over the default stylesheet: default, modern, print or report (default: the default stylesheet alone)") + + fs.StringVar(&htmlTheme, "html-theme", "", "Style the page with a bundled theme layered over the default stylesheet: default, modern, print or report") + fs.Var(&htmlCSS, "html-css", "Style the page with this stylesheet too: a file is inlined, a URL is linked (repeatable, applied in order after the default sheet)") fs.BoolVar(&htmlNoCSS, "html-no-default-css", false, "Leave the default stylesheet out, so only -html-css sheets style the document") - fs.BoolVar(&htmlShowCSS, "html-default-css", false, "Write the default document stylesheet and exit, as a starting point for your own") fs.BoolVar(&htmlFragment, "html-fragment", false, "Write the document element alone, without the page shell or a stylesheet, to embed in a page of your own") - fs.StringVar(&htmlMermaid, "html-mermaid", "", "Have the HTML page load Mermaid to draw its diagrams: cdn loads a pinned release from jsDelivr, a URL loads the script it names (default: diagrams stay Mermaid source)") - fs.StringVar(&htmlMath, "html-math", "", "Have the HTML page load MathJax to typeset its formulas: cdn loads a pinned release from jsDelivr, a URL loads the script it names (default: formulas stay LaTeX source)") - fs.StringVar(&syncDiffWith, "sync-diff", "", "Show the change set between the model and this repository — a graph file (.ttl) or a SysML v2 API endpoint URL — keyed by effective element id, instead of running it; never writes") - fs.StringVar(&syncApplyTo, "sync-apply", "", "Apply the change set to the model's project branch at this SysML v2 API endpoint URL, then record the commit in the sync state (token from "+flexo.EnvToken+")") + fs.BoolVar(&htmlShowCSS, "html-default-css", false, "Write the default document stylesheet, or with -html-theme that theme's whole sheet, and exit") + fs.StringVar(&htmlMermaid, "html-mermaid", "", "Have the page load Mermaid to draw its diagrams: cdn loads a pinned release from jsDelivr, a URL the script it names") + fs.StringVar(&htmlMath, "html-math", "", "Have the page load MathJax to typeset its formulas: cdn loads a pinned release from jsDelivr, a URL the script it names") + + fs.StringVar(&syncDiffWith, "sync-diff", "", "Show the change set between the model and this repository — a graph file (.ttl) or a SysML v2 API endpoint URL — and exit; never writes") + fs.StringVar(&syncApplyTo, "sync-apply", "", "Apply the change set to the model's project branch at this SysML v2 API endpoint URL, then record the commit in the sync state") fs.StringVar(&syncBase, "sync-base", "", "Repository graph at the last-seen commit; with it, repository changes since then surface as conflicts") - fs.StringVar(&syncState, "sync-state", "", "Sync state file recording project, branch and last-seen commit (default: .sync.json beside the model)") + fs.StringVar(&syncState, "sync-state", "", "Sync state file recording project, branch and last-seen commit; default .sync.json beside the model") fs.BoolVar(&syncConfirmDeletes, "sync-confirm-deletes", false, "Confirm repository-side deletes; without it the diff reports them but applying is refused") fs.BoolVar(&syncMintIDs, "sync-mint-ids", false, "Mint a UUID for each unannotated element being created, so the repository can address it stably") fs.StringVar(&syncAnnotate, "sync-annotate", "", "Write the model to this file with each minted id declared as an @ElementId annotation (needs -sync-mint-ids)") - fs.Var(&deprecatedFlag{instead: "-to has been replaced by -convert, as `sysml model.sysml -convert ttl`"}, "to", "Replaced by -convert, which names the output format") - fs.Var(&modelChecks.instantiate, "instantiate", "Create an object of this definition or usage before the checks, so a verdict is about it; under -schedule explore, -engine check, smt or all, each run creates an object of it of its own before the behaviors start, one per -instantiate, which a -state or -action named alone attaches to and a path such as mission.vehicle walks into (repeatable)") - fs.Var(&modelChecks.constraints, "constraint", "Evaluate this constraint and exit (repeatable)") - fs.Var(&modelChecks.requirements, "requirement", "Evaluate this requirement and exit, reporting beside its verdict the verdict of every verification case verifying it (repeatable)") - fs.Var(&modelChecks.satisfy, "satisfy", "Evaluate every satisfaction assertion, or with -satisfy= those the named element states, reporting beside each verdict the verdict of every verification case verifying the requirement (repeatable)") - fs.Var(&modelChecks.validate, "validate", "Analyse the model and report its diagnostics, exiting nonzero on an error; with -validate= check instead every assertion about an object -instantiate created and the objects it holds, an object named, #, or a path such as car.engine (repeatable)") - fs.Var(&modelChecks.calcs, "calc", "Invoke this calculation and report what it computed, as -calc \"Fall(3, 4)\" (repeatable)") - fs.Var(&modelChecks.analyses, "analysis", "Run this analysis or verification case and report its outputs and the verdict of its objective, as -analysis \"Pkg::Case(3.0) Pkg::part\" with arguments for its inputs and an object as its subject; a verification case also reports the verdict its body produced (repeatable)") - fs.Var(&modelChecks.sweeps, "sweep", "Run the named -analysis or -calc once per value of this range, as -sweep \"speed=0.0 [SI::'m/s']..10.0 [SI::'m/s']:2.0 [SI::'m/s']\"; the values are produced in the parameter's declared type; several ranges run their cartesian product (repeatable)") - fs.Var(&modelChecks.samples, "samples", "Draw this many values uniformly from each -sweep range instead of stepping through them, Integers or reals as the parameter is typed; needs -seed") - fs.Var(&modelChecks.seed, "seed", "Seed the model's own draws — Probability-weighted decisions, RandomFunctions — come from in every run made, whatever -schedule, and the seed -samples or -runs draws from; the same seed draws the same run or table") - fs.Var(&modelChecks.runs, "runs", "Run the -action this many times, each run's modeled randomness (weighted decisions, random functions) seeded from -seed, and report the table of the -observe features with each one's distribution; needs -seed") - fs.Var(&modelChecks.observe, "observe", "Report this feature of the -runs action, or `clock` for the time it completed at; default every feature it holds and the clock (repeatable)") - fs.Var(&modelChecks.queries, "run-query", "Execute this document query and report its rows, as -run-query \"HeavySubsystems root=telescope\" (repeatable)") - fs.Var(&modelChecks.actions, "action", "Run this action to completion, as -action \"Drive rover1\" to run it on an object; under -schedule explore each run performs it on an object of its own, named as a definition or usage to create, a path into one such as mission.rover, or, named alone, the run's one -instantiate object performing it (repeatable)") - fs.Var(&modelChecks.states, "state", "Run this state machine, as -state \"Mission rover1\" to run it on an object; under -schedule explore each run creates the object of its own, named as a definition or usage to create or a path such as mission.rover into one — the declaration is created once per run, so machines on sibling parts share it and its connectors — or, named alone, the run's one -instantiate object exhibiting it (repeatable)") - fs.Var(&modelChecks.advance, "advance", "Simulated time units to run the -action and -state behaviors for, on one shared clock (default: a state machine takes only its initial transition; an action runs to completion)") - fs.Var(&modelChecks.checker.diverge, "check-diverge", "With -engine check, -engine smt or -engine all: report this feature sensitive when schedules leave it with different final values — check searching the schedules, smt asking the solver for two that end it apart — as -check-diverge x, -check-diverge step.out for a performed node's output or -check-diverge this.level for the performing object's (not covered under smt), a name nothing holds refused; default every attribute of the action and of its performing object, the action's own when it has none (repeatable)") - fs.Var(&modelChecks.checker.properties, "check-property", "With -engine check or -engine all: evaluate this constraint or requirement at every stable state of the action, on the performing object when there is one, and report a schedule at which it is false (repeatable)") - fs.Var(&modelChecks.checker.inputs, "check-input", "With -engine smt or -engine all: leave this feature of the action free in its declared domain although the model binds it, as -check-input inletTemp; a name that is not a feature the action reads is refused. Without it, every input the model leaves unbound is free and every bound one is pinned (repeatable)") - fs.Var(&modelChecks.checker.assume, "check-assume", "With -engine smt or -engine all: assume this constraint or requirement over the initial state of the action, as -check-assume Plant::EnvelopeLimits; a set no initial state satisfies is reported not covered, never proved (repeatable)") - fs.StringVar(&modelChecks.checker.witness, "check-witness", "", "With -engine check, -engine smt or -engine all: write a witness file to this directory for each violation and each divergent value — the inputs the solver chose, the schedule's choices, a blank line, then the run's trace — which -schedule replay: and %replay follow") - modelChecks.checker.depth.flag, modelChecks.checker.states.flag, modelChecks.checker.unroll.flag = "check-depth", "check-states", "check-unroll" - fs.Var(&modelChecks.checker.depth, "check-depth", "With -engine check, -engine smt or -engine all: the most moves one schedule may make before the search backtracks, or the moves the smt engine unrolls the action to, named as the depth bound hit (default 10000 for check, 40 for smt)") - fs.Var(&modelChecks.checker.states, "check-states", "With -engine check or -engine all: the most distinct states the search may visit, named as the states bound hit; the same figure -engine all gives an exploration as its runs (default 1000000)") - fs.Var(&modelChecks.checker.unroll, "check-unroll", "With -engine smt or -engine all: the most iterations of one loop the smt engine unrolls before it stops, named as the unroll bound hit (default 4)") - fs.Var(&modelChecks.checker.timeout, "check-timeout", "With -engine check, -engine smt or -engine all: the time the check's plan may run for, as 30s or 2m, and the time each of the smt engine's solver queries may take in place of OPENSYSML_SMT_TIMEOUT; a search it stops is reported incomplete with the states and depth reached, not as a verdict") - fs.BoolVar(&modelChecks.jsonOut, "json", false, "Report checks as one JSON document rather than as lines") - fs.StringVar(&compileCalc, "compile", "", "Compile this calc def to a native executable named by -o, as -compile Pkg::Fib") - fs.StringVar(&compileTarget, "target", "c", "Backend -compile generates code for: c (default) or go") - fs.BoolVar(&compileSource, "source", false, "With -compile, write the generated source to -o instead of building it") + + fs.BoolVar(&debugMode, "debug", false, "Report every diagnostic over the whole session buffer, with the pass that produced it") + fs.BoolVar(&quietMode, "quiet", false, "Report errors only, suppressing warnings") + fs.BoolVar(&traceMode, "trace", false, "Report each execution step: expression evaluation, calc invocation, action tokens, state transitions") fs.StringVar(&cpuProfilePath, "cpuprofile", "", "Write a CPU profile of the run to this file, for go tool pprof") fs.StringVar(&memProfilePath, "memprofile", "", "Write a heap profile of the run to this file, for go tool pprof") fs.BoolVar(&memStats, "memstats", false, "Report on stderr what the run cost: wall time, memory allocated, memory taken from the OS") + + fs.Var(&deprecatedFlag{instead: "-to has been replaced by -convert, as `sysml model.sysml -convert ttl`"}, "to", "Replaced by -convert, which names the output format") + fs.BoolVar(&pdfTitlePage, "pdf-title-page", false, "Former name of -doc-title-page, which also shapes HTML") + fs.BoolVar(&pdfTOC, "pdf-toc", false, "Former name of -doc-toc, which also shapes HTML") + fs.BoolVar(&pdfNumbering, "pdf-number-sections", false, "Former name of -doc-number-sections, which also shapes HTML") +} + +// optionGroups lays the flags out by task, each with the placeholder its +// argument is shown as, in the order the help and the man page list them. +func optionGroups() []usage.OptionGroup { + return []usage.OptionGroup{{ + Title: "General", + Options: []usage.Option{ + usage.Opt("help", "", "h"), + usage.Opt("version", "", "v"), + usage.Opt("man", ""), + }, + }, { + Title: "Evaluating", + Options: []usage.Option{ + usage.Opt("eval", "", "e"), + usage.Opt("query", ""), + }, + }, { + Title: "Checking a model", + Options: []usage.Option{ + usage.Opt("validate", "[=]"), + usage.Opt("strict", ""), + usage.Opt("constraint", ""), + usage.Opt("requirement", ""), + usage.Opt("satisfy", "[=]"), + usage.Opt("calc", ""), + usage.Opt("analysis", ""), + usage.Opt("run-query", ""), + usage.Opt("instantiate", ""), + usage.Opt("json", ""), + }, + }, { + Title: "Running behaviors", + Options: []usage.Option{ + usage.Opt("action", ""), + usage.Opt("state", ""), + usage.Opt("advance", "