diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2023-05-02 11:10:58 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-02 11:10:58 -0400 |
| commit | 29cb65585782f71a9c6fa1062eaa0b8de8359604 (patch) | |
| tree | cdd820f2c4a8b26933ba4f5057f92e84b8dc01eb | |
| parent | 19c0866b050a022406867aa650302f4efbf8e010 (diff) | |
Markdown CommandOptions (#2860)
* WIP CommandOptions
* Fix some output issues.
* Simplify word wrapping.
* Add file extensions.
* Change how lookup takes place.
Add appendSplit functions to StringUtil.
Make Categories hold the index range of their options.
* Small improvement.
* Lookup with partial option names.
* Associate user values.
* Encoding flags in the name.
* Refactor setting up of command options.
* Use CommandOptions in slang-options.
* Remove old help text.
* Cache the CommandOptions on the Session.
* Range checking.
Fix bug in the Options handling.
* Extra checks for validity.
* Get categories directly.
* Slight improvements over output.
* Added NameValue types.
* Fix typo.
Remove some now unused diagnostics.
Fix diagnostic in testing, as output has changed.
* Add minimal usage message.
* Remove platform executable extension from diagnostics output.
* Some improvements around getting names from NameValue types.
* Improve some option descriptions.
* Small fixes.
* WIP improvements around CommandOptions.
* Split out CommandOptionsWriter.
* Add links to options.
* Add command line options reference.
* Link to the reference command line information.
* Add quick links.
* Improvements around lookup.
Add categories to linking.
* Small additional fixes.
* Add LinkFlags control.
* Small text fixes.
* Fix typo.
* Fix typo.
* Fix typo.
* Add support for -g and -O using CommandOptions.
* Improve generated doc output/descriptions.
Remove options listed directly in documentation.
| -rw-r--r-- | build/visual-studio/core/core.vcxproj | 2 | ||||
| -rw-r--r-- | build/visual-studio/core/core.vcxproj.filters | 6 | ||||
| -rw-r--r-- | build/visual-studio/slang-rt/slang-rt.vcxproj | 2 | ||||
| -rw-r--r-- | build/visual-studio/slang-rt/slang-rt.vcxproj.filters | 6 | ||||
| -rw-r--r-- | docs/command-line-slangc-reference.md | 788 | ||||
| -rw-r--r-- | docs/command-line-slangc.md | 101 | ||||
| -rw-r--r-- | source/core/slang-command-options-writer.cpp | 712 | ||||
| -rw-r--r-- | source/core/slang-command-options-writer.h | 69 | ||||
| -rw-r--r-- | source/core/slang-command-options.cpp | 394 | ||||
| -rw-r--r-- | source/core/slang-command-options.h | 82 | ||||
| -rw-r--r-- | source/core/slang-string-util.cpp | 34 | ||||
| -rw-r--r-- | source/core/slang-string-util.h | 5 | ||||
| -rw-r--r-- | source/core/slang-type-text-util.cpp | 71 | ||||
| -rw-r--r-- | source/core/slang-type-text-util.h | 21 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 256 |
16 files changed, 2016 insertions, 535 deletions
diff --git a/build/visual-studio/core/core.vcxproj b/build/visual-studio/core/core.vcxproj index 90fcfe8ef..559dde3b4 100644 --- a/build/visual-studio/core/core.vcxproj +++ b/build/visual-studio/core/core.vcxproj @@ -278,6 +278,7 @@ <ClInclude Include="..\..\..\source\core\slang-chunked-list.h" />
<ClInclude Include="..\..\..\source\core\slang-com-object.h" />
<ClInclude Include="..\..\..\source\core\slang-command-line.h" />
+ <ClInclude Include="..\..\..\source\core\slang-command-options-writer.h" />
<ClInclude Include="..\..\..\source\core\slang-command-options.h" />
<ClInclude Include="..\..\..\source\core\slang-common.h" />
<ClInclude Include="..\..\..\source\core\slang-compression-system.h" />
@@ -344,6 +345,7 @@ <ClCompile Include="..\..\..\source\core\slang-char-encode.cpp" />
<ClCompile Include="..\..\..\source\core\slang-char-util.cpp" />
<ClCompile Include="..\..\..\source\core\slang-command-line.cpp" />
+ <ClCompile Include="..\..\..\source\core\slang-command-options-writer.cpp" />
<ClCompile Include="..\..\..\source\core\slang-command-options.cpp" />
<ClCompile Include="..\..\..\source\core\slang-crypto.cpp" />
<ClCompile Include="..\..\..\source\core\slang-deflate-compression-system.cpp" />
diff --git a/build/visual-studio/core/core.vcxproj.filters b/build/visual-studio/core/core.vcxproj.filters index 41d0fafe1..11c689121 100644 --- a/build/visual-studio/core/core.vcxproj.filters +++ b/build/visual-studio/core/core.vcxproj.filters @@ -48,6 +48,9 @@ <ClInclude Include="..\..\..\source\core\slang-command-line.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\core\slang-command-options-writer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\core\slang-command-options.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -242,6 +245,9 @@ <ClCompile Include="..\..\..\source\core\slang-command-line.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\core\slang-command-options-writer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\core\slang-command-options.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/build/visual-studio/slang-rt/slang-rt.vcxproj b/build/visual-studio/slang-rt/slang-rt.vcxproj index 41c44a051..ad3136e66 100644 --- a/build/visual-studio/slang-rt/slang-rt.vcxproj +++ b/build/visual-studio/slang-rt/slang-rt.vcxproj @@ -290,6 +290,7 @@ <ClInclude Include="..\..\..\source\core\slang-chunked-list.h" />
<ClInclude Include="..\..\..\source\core\slang-com-object.h" />
<ClInclude Include="..\..\..\source\core\slang-command-line.h" />
+ <ClInclude Include="..\..\..\source\core\slang-command-options-writer.h" />
<ClInclude Include="..\..\..\source\core\slang-command-options.h" />
<ClInclude Include="..\..\..\source\core\slang-common.h" />
<ClInclude Include="..\..\..\source\core\slang-compression-system.h" />
@@ -357,6 +358,7 @@ <ClCompile Include="..\..\..\source\core\slang-char-encode.cpp" />
<ClCompile Include="..\..\..\source\core\slang-char-util.cpp" />
<ClCompile Include="..\..\..\source\core\slang-command-line.cpp" />
+ <ClCompile Include="..\..\..\source\core\slang-command-options-writer.cpp" />
<ClCompile Include="..\..\..\source\core\slang-command-options.cpp" />
<ClCompile Include="..\..\..\source\core\slang-crypto.cpp" />
<ClCompile Include="..\..\..\source\core\slang-deflate-compression-system.cpp" />
diff --git a/build/visual-studio/slang-rt/slang-rt.vcxproj.filters b/build/visual-studio/slang-rt/slang-rt.vcxproj.filters index 4fa4373ff..a33803e0e 100644 --- a/build/visual-studio/slang-rt/slang-rt.vcxproj.filters +++ b/build/visual-studio/slang-rt/slang-rt.vcxproj.filters @@ -48,6 +48,9 @@ <ClInclude Include="..\..\..\source\core\slang-command-line.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\core\slang-command-options-writer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\core\slang-command-options.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -245,6 +248,9 @@ <ClCompile Include="..\..\..\source\core\slang-command-line.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\core\slang-command-options-writer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\core\slang-command-options.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md new file mode 100644 index 000000000..e61c4245b --- /dev/null +++ b/docs/command-line-slangc-reference.md @@ -0,0 +1,788 @@ +# Slang Command Line Options + +*Usage:* +``` +slangc [options...] [--] <input files> + +# For help +slangc -h + +# To generate this file +slangc -help-style markdown -h +``` +## Quick Links + +* [General](#General) +* [Target](#Target) +* [Downstream](#Downstream) +* [Debugging](#Debugging) +* [Experimental](#Experimental) +* [Internal](#Internal) +* [Depreciated](#Depreciated) +* [compiler](#compiler) +* [language](#language) +* [archive-type](#archive-type) +* [line-directive-mode](#line-directive-mode) +* [debug-info-format](#debug-info-format) +* [fp-mode](#fp-mode) +* [help-style](#help-style) +* [optimization-level](#optimization-level) +* [debug-level](#debug-level) +* [file-system-type](#file-system-type) +* [target](#target) +* [stage](#stage) +* [capability](#capability) +* [file-extension](#file-extension) + +<a id="General"></a> +# General + +General options + +<a id="D"></a> +## -D + +**-D<name>\[=<value>\], -D <name>\[=<value>\]** + +Insert a preprocessor macro. + +The space between - D and <name> is optional. If no <value> is specified, Slang will define the macro with an empty value. + + +<a id="depfile"></a> +## -depfile + +**-depfile <path>** + +Save the source file dependency list in a file. + + +<a id="entry"></a> +## -entry + +**-entry <name>** + +Specify the name of an entry-point function. + +When compiling from a single file, this defaults to main if you specify a stage using [-stage](#stage-1). + +Multiple [-entry](#entry) options may be used in a single invocation. When they do, the file associated with the entry point will be the first one found when searching to the left in the command line. + +If no [-entry](#entry) options are given, compiler will use \[shader(...)\] attributes to detect entry points. + + +<a id="emit-ir"></a> +## -emit-ir +Emit IR typically as a '.slang-module' when outputting to a container. + + +<a id="h"></a> +## -h, -help, --help + +**-h or -h <help-category>** + +Print this message, or help in specified category. + + +<a id="help-style-1"></a> +## -help-style + +**-help-style <[help-style](#help-style)>** + +Help formatting style + + +<a id="I"></a> +## -I + +**-I<path>, -I <path>** + +Add a path to be used in resolving '#include' and 'import' operations. + + +<a id="lang"></a> +## -lang + +**-lang <[language](#language)>** + +Set the language for the following input files. + + +<a id="matrix-layout-column-major"></a> +## -matrix-layout-column-major +Set the default matrix layout to column-major. + + +<a id="matrix-layout-row-major"></a> +## -matrix-layout-row-major +Set the default matrix layout to row-major. + + +<a id="module-name"></a> +## -module-name + +**-module-name <name>** + +Set the module name to use when compiling multiple .slang source files into a single module. + + +<a id="o"></a> +## -o + +**-o <path>** + +Specify a path where generated output should be written. + +If no [-target](#target-1) or [-stage](#stage-1) is specified, one may be inferred from file extension (see [<file-extension>](#file-extension)). If multiple [-target](#target-1) options and a single [-entry](#entry) are present, each [-o](#o) associates with the first [-target](#target-1) to its left. Otherwise, if multiple [-entry](#entry) options are present, each [-o](#o) associates with the first [-entry](#entry) to its left, and with the [-target](#target-1) that matches the one inferred from <path>. + + +<a id="profile"></a> +## -profile + +**-profile <profile>\[+<[capability](#capability)>...\]** + +Specify the shader profile for code generation. + +Accepted profiles are: + +* sm_{4_0,4_1,5_0,5_1,6_0,6_1,6_2,6_3,6_4,6_5,6_6} + +* glsl_{110,120,130,140,150,330,400,410,420,430,440,450,460} + +Additional profiles that include [-stage](#stage-1) information: + +* {vs,hs,ds,gs,ps}_<version> + +See [-capability](#capability-1) for information on [<capability>](#capability) + +When multiple [-target](#target-1) options are present, each [-profile](#profile) associates with the first [-target](#target-1) to its left. + + +<a id="stage-1"></a> +## -stage + +**-stage <[stage](#stage)>** + +Specify the stage of an entry-point function. + +When multiple [-entry](#entry) options are present, each [-stage](#stage-1) associated with the first [-entry](#entry) to its left. + +May be omitted if entry-point function has a \[shader(...)\] attribute; otherwise required for each [-entry](#entry) option. + + +<a id="target-1"></a> +## -target + +**-target <[target](#target)>** + +Specifies the format in which code should be generated. + + +<a id="v"></a> +## -v, -version +Display the build version. This is the contents of git describe --tags. + +It is typically only set from automated builds(such as distros available on github).A user build will by default be 'unknown'. + + +<a id="warnings-as-errors"></a> +## -warnings-as-errors + +**-warnings-as-errors all or -warnings-as-errors <id>\[,<id>...\]** + +all - Treat all warnings as errors. + +<id>\[,<id>...\]: Treat specific warning ids as errors. + + + + +<a id="warnings-disable"></a> +## -warnings-disable + +**-warnings-disable <id>\[,<id>...\]** + +Disable specific warning ids. + + +<a id="W"></a> +## -W + +**-W<id>** + +Enable a warning with the specified id. + + +<a id="Wno"></a> +## -Wno- + +**-Wno-<id>** + +Disable warning with <id> + + +<a id="dump-warning-diagnostics"></a> +## -dump-warning-diagnostics +Dump to output list of warning diagnostic numeric and name ids. + + +<a id="id"></a> +## -- +Treat the rest of the command line as input files. + + + +<a id="Target"></a> +# Target + +Target code generation options + +<a id="capability-1"></a> +## -capability + +**-capability <[capability](#capability)>\[+<[capability](#capability)>...\]** + +Add optional capabilities to a code generation target. See Capabilities below. + + +<a id="default-image-format-unknown"></a> +## -default-image-format-unknown +Set the format of R/W images with unspecified format to 'unknown'. Otherwise try to guess the format. + + +<a id="disable-dynamic-dispatch"></a> +## -disable-dynamic-dispatch +Disables generating dynamic dispatch code. + + +<a id="disable-specialization"></a> +## -disable-specialization +Disables generics and specialization pass. + + +<a id="fp-mode-1"></a> +## -fp-mode, -floating-point-mode + +**-fp-mode <[fp-mode](#fp-mode)>, -floating-point-mode <[fp-mode](#fp-mode)>** + +Control floating point optimizations + + +<a id="g"></a> +## -g + +**-g, -g<[debug-info-format](#debug-info-format)>, -g<[debug-level](#debug-level)>** + +Include debug information in the generated code, where possible. + +[<debug-level>](#debug-level) is the amount of information, 0..3, unspecified means 2 + +[<debug-info-format>](#debug-info-format) specifies a debugging info format + +It is valid to have multiple [-g](#g) options, such as a [<debug-level>](#debug-level) and a [<debug-info-format>](#debug-info-format) + + +<a id="line-directive-mode-1"></a> +## -line-directive-mode + +**-line-directive-mode <[line-directive-mode](#line-directive-mode)>** + +Sets how the `#line` directives should be produced. Available options are: + +If not specified, default behavior is to use C-style `#line` directives for HLSL and C/C++ output, and traditional GLSL-style `#line` directives for GLSL output. + + +<a id="O"></a> +## -O + +**-O<[optimization-level](#optimization-level)>** + +Set the optimization level. + + +<a id="obfuscate"></a> +## -obfuscate +Remove all source file information from outputs. + + + +<a id="Downstream"></a> +# Downstream + +Downstream compiler options + +<a id="none-path"></a> +## -none-path, -fxc-path, -dxc-path, -glslang-path, -visualstudio-path, -clang-path, -gcc-path, -genericcpp-path, -nvrtc-path, -llvm-path + +**-<[compiler](#compiler)>-path <path>** + +Specify path to a downstream [<compiler>](#compiler) executable or library. + + + + +<a id="default-downstream-compiler"></a> +## -default-downstream-compiler + +**-default-downstream-compiler <[language](#language)> <[compiler](#compiler)>** + +Set a default compiler for the given language. See [-lang](#lang) for the list of languages. + + +<a id="X"></a> +## -X + +**-X<[compiler](#compiler)> <option> -X<[compiler](#compiler)>... <options> -X.** + +Pass arguments to downstream [<compiler>](#compiler). Just [-X<compiler>](#X) passes just the next argument to the downstream compiler. [-X<compiler>](#X)... options [-X](#X). will pass *all* of the options inbetween the opening [-X](#X) and [-X](#X). to the downstream compiler. + + +<a id="pass-through"></a> +## -pass-through + +**-pass-through <[compiler](#compiler)>** + +Pass the input through mostly unmodified to the existing compiler [<compiler>](#compiler). + +These are intended for debugging/testing purposes, when you want to be able to see what these existing compilers do with the "same" input and options + + + +<a id="Debugging"></a> +# Debugging + +Compiler debugging/instrumentation options + +<a id="dump-ast"></a> +## -dump-ast +Dump the AST to a .slang-ast file next to the input. + + +<a id="dump-intermediate-prefix"></a> +## -dump-intermediate-prefix + +**-dump-intermediate-prefix <prefix>** + +File name prefix for [-dump-intermediates](#dump-intermediates) outputs, default is 'slang-dump-' + + +<a id="dump-intermediates"></a> +## -dump-intermediates +Dump intermediate outputs for debugging. + + +<a id="dump-ir"></a> +## -dump-ir +Dump the IR for debugging. + + +<a id="dump-ir-ids"></a> +## -dump-ir-ids +Dump the IDs with [-dump-ir](#dump-ir) (debug builds only) + + +<a id="dump-repro"></a> +## -dump-repro +Dump a `.slang-repro` file that can be used to reproduce a compilation on another machine. + + + + +<a id="dump-repro-on-error"></a> +## -dump-repro-on-error +Dump `.slang-repro` file on any compilation error. + + +<a id="E"></a> +## -E, -output-preprocessor +Output the preprocessing result and exit. + + +<a id="extract-repro"></a> +## -extract-repro + +**-extract-repro <name>** + +Extract the repro files into a folder. + + +<a id="load-repro-directory"></a> +## -load-repro-directory + +**-load-repro-directory <path>** + +Use repro along specified path + + +<a id="load-repro"></a> +## -load-repro + +**-load-repro <name>** + +Load repro + + +<a id="no-codegen"></a> +## -no-codegen +Skip the code generation step, just check the code and generate layout. + + +<a id="output-includes"></a> +## -output-includes +Print the hierarchy of the processed source files. + + +<a id="repro-file-system"></a> +## -repro-file-system + +**-repro-file-system <name>** + +Use a repro as a file system + + +<a id="serial-ir"></a> +## -serial-ir +Serialize the IR between front-end and back-end. + + +<a id="skip-codegen"></a> +## -skip-codegen +Skip the code generation phase. + + +<a id="validate-ir"></a> +## -validate-ir +Validate the IR between the phases. + + +<a id="verbose-paths"></a> +## -verbose-paths +When displaying diagnostic output aim to display more detailed path information. In practice this is typically the complete 'canonical' path to the source file used. + + +<a id="verify-debug-serial-ir"></a> +## -verify-debug-serial-ir +Verify IR in the front-end. + + + +<a id="Experimental"></a> +# Experimental + +Experimental options (use at your own risk) + +<a id="emit-spirv-directly"></a> +## -emit-spirv-directly +Generate SPIR-V output directly (otherwise through GLSL and using the glslang compiler) + + +<a id="file-system"></a> +## -file-system + +**-file-system <[file-system-type](#file-system-type)>** + +Set the filesystem hook to use for a compile request. + + +<a id="heterogeneous"></a> +## -heterogeneous +Output heterogeneity-related code. + + +<a id="no-mangle"></a> +## -no-mangle +Do as little mangling of names as possible. + + + +<a id="Internal"></a> +# Internal + +Internal-use options (use at your own risk) + +<a id="archive-type-1"></a> +## -archive-type + +**-archive-type <[archive-type](#archive-type)>** + +Set the archive type for [-save-stdlib](#save-stdlib). Default is zip. + + +<a id="compile-stdlib"></a> +## -compile-stdlib +Compile the StdLib from embedded sources. Will return a failure if there is already a StdLib available. + + +<a id="doc"></a> +## -doc +Write documentation for [-compile-stdlib](#compile-stdlib) + + +<a id="ir-compression"></a> +## -ir-compression + +**-ir-compression <type>** + +Set compression for IR and AST outputs. + +Accepted compression types: none, lite + + +<a id="load-stdlib"></a> +## -load-stdlib + +**-load-stdlib <filename>** + +Load the StdLib from file. + + +<a id="r"></a> +## -r + +**-r <name>** + +reference module <name> + + +<a id="save-stdlib"></a> +## -save-stdlib + +**-save-stdlib <filename>** + +Save the StdLib modules to an archive file. + + +<a id="save-stdlib-bin-source"></a> +## -save-stdlib-bin-source + +**-save-stdlib-bin-source <filename>** + +Same as [-save-stdlib](#save-stdlib) but output the data as a C array. + + + + +<a id="track-liveness"></a> +## -track-liveness +Enable liveness tracking. Places SLANG_LIVE_START, and SLANG_LIVE_END in output source to indicate value liveness. + + + +<a id="Depreciated"></a> +# Depreciated + +Deprecated options (allowed but ignored; may be removed in future) + +<a id="parameter-blocks-use-register-spaces"></a> +## -parameter-blocks-use-register-spaces +Parameter blocks will use register spaces + + + +<a id="compiler"></a> +# compiler + +Downstream Compilers (aka Pass through) + +* `none` : Unknown +* `fxc` : FXC HLSL compiler +* `dxc` : DXC HLSL compiler +* `glslang` : GLSLANG GLSL compiler +* `visualstudio`, `vs` : Visual Studio C/C++ compiler +* `clang` : Clang C/C++ compiler +* `gcc` : GCC C/C++ compiler +* `genericcpp`, `c`, `cpp` : A generic C++ compiler (can be any one of visual studio, clang or gcc depending on system and availability) +* `nvrtc` : NVRTC CUDA compiler +* `llvm` : LLVM/Clang `slang-llvm` + +<a id="language"></a> +# language + +Language + +* `c`, `C` : C language +* `cpp`, `c++`, `C++`, `cxx` : C++ language +* `slang` : Slang language +* `glsl` : GLSL language +* `hlsl` : HLSL language +* `cu`, `cuda` : CUDA + +<a id="archive-type"></a> +# archive-type + +Archive Type + +* `riff-deflate` : Slang RIFF using deflate compression +* `riff-lz4` : Slang RIFF using LZ4 compression +* `zip` : Zip file +* `riff` : Slang RIFF without compression + +<a id="line-directive-mode"></a> +# line-directive-mode + +Line Directive Mode + +* `none` : Don't emit `#line` directives at all +* `source-map` : Use source map to track line associations (doen't emit #line) +* `default` : Default behavior +* `standard` : Emit standard C-style `#line` directives. +* `glsl` : Emit GLSL-style directives with file *number* instead of name. + +<a id="debug-info-format"></a> +# debug-info-format + +Debug Info Format + +* `default-format` : Use the default debugging format for the target +* `c7` : CodeView C7 format (typically means debugging infomation is embedded in the binary) +* `pdb` : Program database +* `stabs` : STABS debug format +* `coff` : COFF debug format +* `dwarf` : DWARF debug format + +<a id="fp-mode"></a> +# fp-mode + +Floating Point Mode + +* `precise` : Disable optimization that could change the output of floating-point computations, including around infinities, NaNs, denormalized values, and negative zero. Prefer the most precise versions of special functions supported by the target. +* `fast` : Allow optimizations that may change results of floating-point computations. Prefer the fastest version of special functions supported by the target. +* `default` : Default floating point mode + +<a id="help-style"></a> +# help-style + +Help Style + +* `text` : Text suitable for output to a terminal +* `markdown` : Markdown +* `no-link-markdown` : Markdown without links + +<a id="optimization-level"></a> +# optimization-level + +Optimization Level + +* `0`, `none` : Disable all optimizations +* `1`, `default` : Enable a default level of optimization.This is the default if no [-o](#o) options are used. +* `2`, `high` : Enable aggressive optimizations for speed. +* `3`, `maximal` : Enable further optimizations, which might have a significant impact on compile time, or involve unwanted tradeoffs in terms of code size. + +<a id="debug-level"></a> +# debug-level + +Debug Level + +* `0`, `none` : Don't emit debug information at all. +* `1`, `minimal` : Emit as little debug information as possible, while still supporting stack traces. +* `2`, `standard` : Emit whatever is the standard level of debug information for each target. +* `3`, `maximal` : Emit as much debug information as possible for each target. + +<a id="file-system-type"></a> +# file-system-type + +File System Type + +* `default` : Default fike system. +* `load-file` : Just implements loadFile interface, so will be wrapped with CacheFileSystem internally. +* `os` : Use the OS based file system directly (without file system caching) + +<a id="target"></a> +# target + +Target + +* `unknown` +* `none` +* `hlsl` : HLSL source code +* `dxbc` : DirectX shader bytecode binary +* `dxbc-asm`, `dxbc-assembly` : DirectX shader bytecode assembly +* `dxil` : DirectX Intermediate Language binary +* `dxil-asm`, `dxil-assembly` : DirectX Intermediate Language assembly +* `glsl` : GLSL source code +* `glsl-vulkan` : GLSL Vulkan source code +* `glsl-vulkan-one-desc` : GLSL Vulkan source code +* `spirv` : SPIR-V binary +* `spirv-asm`, `spirv-assembly` : SPIR-V assembly +* `c` : C source code +* `cpp`, `c++`, `cxx` : C++ source code +* `torch`, `torch-binding`, `torch-cpp`, `torch-cpp-binding` : C++ for pytorch binding +* `host-cpp`, `host-c++`, `host-cxx` : C++ source for host execution +* `exe`, `executable` : Executable binary +* `sharedlib`, `sharedlibrary`, `dll` : Shared library/Dll +* `cuda`, `cu` : CUDA source code +* `ptx` : PTX assembly +* `cuobj`, `cubin` : CUDA binary +* `host-callable`, `callable` : Host callable +* `object-code` : Object code +* `host-host-callable` : Host callable for host execution + +<a id="stage"></a> +# stage + +Stage + +* `vertex` +* `hull` +* `domain` +* `geometry` +* `pixel`, `fragment` +* `compute` +* `raygeneration` +* `intersection` +* `anyhit` +* `closesthit` +* `miss` +* `callable` +* `mesh` +* `amplification` + +<a id="capability"></a> +# capability + +A capability describes an optional feature that a target may or may not support. When a [-capability](#capability-1) is specified, the compiler may assume that the target supports that capability, and generate code accordingly. + +* `spirv_1_{ 0`, `1`, `2`, `3`, `4`, `5 }` : minimum supported SPIR - V version +* `invalid` +* `hlsl` +* `glsl` +* `c` +* `cpp` +* `cuda` +* `spirv_direct` +* `GL_NV_ray_tracing` : enables the GL_NV_ray_tracing extension +* `GL_EXT_ray_tracing` : enables the GL_EXT_ray_tracing extension +* `GL_NV_fragment_shader_barycentric` : enables the GL_NV_fragment_shader_barycentric extension +* `GL_EXT_fragment_shader_barycentric` : enables the GL_EXT_fragment_shader_barycentric extension + +<a id="file-extension"></a> +# file-extension + +A [<language>](#language), <format>, and/or [<stage>](#stage) may be inferred from the extension of an input or [-o](#o) path + +* `hlsl`, `fx` : hlsl +* `dxbc` +* `dxbc-asm` : dxbc-assembly +* `dxil` +* `dxil-asm` : dxil-assembly +* `glsl` +* `vert` : glsl (vertex) +* `frag` : glsl (fragment) +* `geom` : glsl (geoemtry) +* `tesc` : glsl (hull) +* `tese` : glsl (domain) +* `comp` : glsl (compute) +* `slang` +* `spv` : SPIR-V +* `spv-asm` : SPIR-V assembly +* `c` +* `cpp`, `c++`, `cxx` : C++ +* `exe` : executable +* `dll`, `so` : sharedlibrary/dll +* `cu` : CUDA +* `ptx` : PTX +* `obj`, `o` : object-code +* `zip` : container +* `slang-module`, `slang-library` : Slang Module/Library +* `dir` : Container as a directory + diff --git a/docs/command-line-slangc.md b/docs/command-line-slangc.md index 2947c8184..d95365083 100644 --- a/docs/command-line-slangc.md +++ b/docs/command-line-slangc.md @@ -5,9 +5,20 @@ The `slangc` command-line tool is used to compile or cross-compile shader source ``` slangc [<options>] <file1> [<file2>...] +``` + +## Options + +The available options are in [the command line option reference](command-line-slangc-reference.md). +This information is also available from `slangc` via + +``` +slangc -h ``` +The sections below describe usage in more detail. + Simple Examples --------------- @@ -49,8 +60,10 @@ To write DXBC, SPIR-V, or GLSL to files, use: slangc my-shader.slang -profile sm_6_0 -entry main -stage fragment -o my-shader.dxil slangc my-shader.slang -profile glsl_450 -entry main -stage fragment -o my-shader.spv -Multiple Entry Points ---------------------- +Usage +----- + +## Multiple Entry Points `slangc` can compile multiple entry points, which may span multiple files in a single invocation. This is useful when you are taking advantage of Slang's ability to automatically assign binding locations to shader parameters, because the compiler can take all of your entry points into account when assigning location (avoiding overlap between entry points that will be used together). @@ -75,84 +88,8 @@ These long command lines obviously aren't pleasant. We encourage applications that require complex shader compilation workflows to use the Slang API directly so that they can implement compilation that follows application conventions/policy. The ability to specify compilation actions like this on the command line is primarily intended a testing and debugging tool. -Options -------- - -For completeness, here are the options that `slangc` currently accepts: - -* `-v`: Displays the build version. This is the contents of `git describe --tags`. It is typically only set from automated builds (such as distros available on github). A user build will by default be 'unknown'. - -* `-D <name>[=<value>]`: Insert a preprocessor macro definition - * The space between `-D` and `<name>` is optional - * If no `<value>` is specified, Slang will define the macro with an empty value - -* `-I <path>`: Add a path to be used in resolving `#include` and `import` operations - * The space between `-I` and `<path>` is optional - -* `-entry <name>`: Specify the name of the entry-point function - * When compiling from a single file, this defaults to `main` *if* you specify a stage using `-stage` - * Multiple `-entry` options may appear on the command line. When they do, the file associated with the entry point will be the first one found when searching to the left in the command line. - -* `-stage <name>`: Specify the stage of an entry-point function - * When there are multiple entry points, a `-stage` option applies to the most recent `-entry` point specified - * When there is only a single entry point, the `-stage` option may appear anywhere on the command line - * The traditional stages are named as follows: - * `vertex` - * `hull`: D3D Hull Shader and GL/VK Tessellation Control Shader - * `domain`: D3D Domain Shader and GL/VK Tessellation Evaluation Shader - * `geometry` - * `fragment` / `pixel`: D3D Pixel Shader and GL/VK Fragment Shader - * `compute` - * The stages for ray tracing use the following names: - * `raygeneration` - * `intersection` - * `anyhit` - * `closesthit` - * `miss` - * `callable` - -* `-target <format>`: Specifies the format in which code should be generated. Values for `<target>` are: - * `glsl`: GLSL source code - * `hlsl`: HLSL source code - * `spirv`: SPIR-V intermediate language binary. - * `spirv-assembly` / `spirv-asm`: SPIR-V intermediate language assembly - * `dxbc`: DirectX shader bytecode binary - * `dxbc-assembly` / `dxbc-asm`: DirectX shader bytecode assembly - * `dxil`: DirectX Intermediate Language binary - * `dxil-assembly` / `dxil-asm`: DirectX Intermediate Language assembly - -* `-profile <profile>`: Specify the "profile" to use for the code generation target, which represents an abstact feature level as defined by a particular API standard. Available values include: - * The Direct3D "Shader Model" levels are available as `sm_{4_0,4_1,5_0,5_1,6_0,6_1,6_2,6_3,6_4,6_5,6_6}` - * Profiles corresponding to GLSL langauge versions are available as `glsl_{110,120,130,140,150,330,400,410,420,430,440,450,460}` - * As a convenience, names matching traditional HLSL shader profiles are provided such that, e.g., `-profile vs_5_0` is an abbreviation for `-profile sm_5_0 -stage vertex` - -* `-o <path>`: Specify a path where generated output should be written - * When multiple `-entry` options are present, each `-o` associates with the first `-entry` to its left. - -* `-pass-through <name>`: Don't actually perform Slang parsing/checking/etc. on the input and instead pass it through (more or less) unmodified to the existing compiler `<name>` - * `fxc`: Use the `D3DCompile` API as exposed by `d3dcompiler_47.dll` - * `glslang`: Use Slang's internal version of `glslang` as exposed by `slang-glslang.dll` - * `dxc`: Use DirectXShaderCompiler (https://github.com/Microsoft/DirectXShaderCompiler) - * These are intended for debugging/testing purposes, when you want to be able to see what these existing compilers do with the "same" input and options - -* `-verbose-paths`: When displaying diagnostic output aim to display more detailed path information. In practice this is typically the complete 'canonical' path to the source file used. - -* `-g`: Include debug information in the generated code, where possible. Currently only supported for DXBC and DXIL output (not SPIR-V). - -* `-O`: Control optimization levels. This currently only affects DXBC and DXIL generation. - * `-O0`: Disable all optimizations - * `-O1`, `-O`: Enable a default level of optimization. This is the default if no `-O` options are used. - * `-O2`: Enable aggressive optimizations for speed. - * `-O3`: Enable further optimizations, which might have a significant impact on compile time, or involve unwanted tradeoffs in terms of code size. - -* `--`: Stop parsing options, and treat the rest of the command line as input paths - -* `-output-includes`: After pre-processing has been performed will output to via the diagnostics the hierarchy of paths to source files reached - -* -Xname to specify arguments to downstream tool `name` (covered in more detail in "Downstream Arguments") - <a id="downstream-arguments"></a> -### Downstream Arguments +## Downstream Arguments During a Slang compilation work may be performed by multiple other stages including downstream compilers and linkers. It isn't possible in general or perhaps even desirable to provide Slang command line equivalents of every option available at every stage of compilation. It is useful to be able to set options specific to a particular compilation stage - to alter code generation, linkage and other options. @@ -245,16 +182,16 @@ For example to specify an include path "somePath" to DXC you can use... -Xdxc -IsomePath ``` -### Specifying where dlls/shared libraries are loaded from +## Specifying where dlls/shared libraries are loaded from On windows if you want a dll loaded from a specific path, the path must be specified absolutely. See the [LoadLibrary documentation](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya) for more details. A relative path will cause Windows to check all locations along it's search procedure. On linux it's similar, but any path (relative or not) will override the regular search mechanism. See [dlopen](https://man7.org/linux/man-pages/man3/dlopen.3.html) for more details. -* `-dxc-path`: Sets the path where dxc dll/shared libraries are loaded from (dxcompiler & dxil). +See [the reference for a complete list](#command-line-slangc-reference.md#none-path) +* `-dxc-path`: Sets the path where dxc dll/shared libraries are loaded from (dxcompiler & dxil). * `-fxc-path`: Sets the path where fxc dll is loaded from (d3dcompiler_47.dll). - * `-glslang-path`: Sets where the Slang specific 'slang-glslang' is loaded from Paths can specify a directory that holds the appropriate binaries. It can also be used to name a specific downstream binary - be it a shared library or an executable. Note that if it is a shared library, it is not necessary to provide the full filesystem name - just the path and/or name that will be used to load it. For example on windows `fxc` can be loaded from `D:/mydlls` with diff --git a/source/core/slang-command-options-writer.cpp b/source/core/slang-command-options-writer.cpp new file mode 100644 index 000000000..afa46db64 --- /dev/null +++ b/source/core/slang-command-options-writer.cpp @@ -0,0 +1,712 @@ +// slang-command-options-writer.cpp + +#include "slang-command-options-writer.h" + +#include "slang-string-util.h" +#include "slang-char-util.h" +#include "slang-byte-encode-util.h" + +namespace Slang { + +namespace { // anonymous +typedef CommandOptionsWriter::Style Style; +} // anonymous + +static bool _isMarkdown(Style style) { return style == Style::Markdown || style == Style::NoLinkMarkdown; } +static bool _hasLinks(Style style) { return style == Style::Markdown; } + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! MarkdownCommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +class MarkdownCommandOptionsWriter : public CommandOptionsWriter +{ +public: + typedef CommandOptionsWriter Super; + + typedef uint32_t LinkFlags; + struct LinkFlag + { + enum Enum + { + Category = 0x1, + Option = 0x2, + + All = Category | Option, + }; + }; + + MarkdownCommandOptionsWriter(const Options& options): + Super(options) + { + } + +protected: + // CommandOptionsWriter + virtual void appendDescriptionForCategoryImpl(Index categoryIndex) SLANG_OVERRIDE; + virtual void appendDescriptionImpl() SLANG_OVERRIDE; + + void _appendParagraph(const UnownedStringSlice& text, LinkFlags flags = LinkFlag::All); + void _appendParagraph(const ConstArrayView<UnownedStringSlice>& words, LinkFlags flags = LinkFlag::All); + + void _appendMaybeLink(const UnownedStringSlice& word, LinkFlags linkFlags); + + void _appendText(const UnownedStringSlice& text); + void _appendDescriptionForCategory(Index categoryIndex); + UnownedStringSlice _getLinkName(CommandOptions::LookupKind kind, Index index); + UnownedStringSlice _getLinkName(const NameKey& key, Index index); + + void _appendQuickLinks(); + + bool m_hasLinks = false; + Dictionary<NameKey, StringSlicePool::Handle> m_linkMap; +}; + +void MarkdownCommandOptionsWriter::appendDescriptionForCategoryImpl(Index categoryIndex) +{ + // No point doing links for a single category + m_hasLinks = false; + _appendDescriptionForCategory(categoryIndex); +} + + +void MarkdownCommandOptionsWriter::appendDescriptionImpl() +{ + m_hasLinks = _hasLinks(m_options.style); + + if (m_hasLinks) + { + _appendQuickLinks(); + } + + // Go through categories in order + const auto& categories = m_commandOptions->getCategories(); + for (Index categoryIndex = 0; categoryIndex < categories.getCount(); ++categoryIndex) + { + _appendDescriptionForCategory(categoryIndex); + } +} + +static bool _needsMarkdownEscape(const UnownedStringSlice& text) +{ + for (auto c : text) + { + switch (c) + { + case '<': + case '>': + case '&': + case '[': + case ']': + { + return true; + } + default: break; + } + } + + return false; +} + +void _appendEscapedMarkdown(const UnownedStringSlice& text, StringBuilder& ioBuf) +{ + if (_needsMarkdownEscape(text)) + { + // Replace any < > & + for (auto c : text) + { + switch (c) + { + case '<': ioBuf << "<"; break; + case '>': ioBuf << ">"; break; + case '&': ioBuf << "&"; break; + case '[': ioBuf << "\\["; break; + case ']': ioBuf << "\\]"; break; + default: ioBuf << c; + } + } + } + else + { + ioBuf << text; + } +} + +void MarkdownCommandOptionsWriter::_appendQuickLinks() +{ + const auto& categories = m_commandOptions->getCategories(); + const auto count = categories.getCount(); + + m_builder << "## Quick Links\n\n"; + + for (Index categoryIndex = 0; categoryIndex < count; ++categoryIndex) + { + const auto& cat = categories[categoryIndex]; + + m_builder << "* ["; + _appendEscapedMarkdown(cat.name, m_builder); + m_builder << "](#" << _getLinkName(LookupKind::Category, categoryIndex) << ")\n"; + } + + m_builder << "\n"; +} + +void MarkdownCommandOptionsWriter::_appendParagraph(const UnownedStringSlice& text, LinkFlags linkFlags) +{ + List<UnownedStringSlice> words; + StringUtil::splitOnWhitespace(text, words); + _appendParagraph(words.getArrayView(), linkFlags); +} + +static bool _isEndPunctionation(char c) +{ + return c == '.' || c == ')' || c == ','; +} + +static bool _isStartPunctionation(char c) +{ + return c == '(' || c == ','; +} + +static UnownedStringSlice _trimPunctuation(const UnownedStringSlice& word) +{ + const char* start = word.begin(); + const char* end = word.end(); + + while (start < end && _isStartPunctionation(*start)) start++; + while (end > start && _isEndPunctionation(end[-1])) --end; + return UnownedStringSlice(start, end); +} + +void MarkdownCommandOptionsWriter::_appendMaybeLink(const UnownedStringSlice& inWord, LinkFlags linkFlags) +{ + if (linkFlags) + { + auto trimmedWord = _trimPunctuation(inWord); + + if (trimmedWord.getLength()) + { + Index index = -1; + NameKey nameKey; + + // Look for options + if (trimmedWord[0] == '-' && (linkFlags & LinkFlag::Option)) + { + index = m_commandOptions->findTargetIndexByName(LookupKind::Option, trimmedWord, &nameKey); + } + else if (trimmedWord[0] == '<' && trimmedWord[trimmedWord.getLength() - 1] == '>' && (linkFlags & LinkFlag::Category)) + { + index = m_commandOptions->findTargetIndexByName(LookupKind::Category, trimmedWord.subString(1, trimmedWord.getLength() - 2), &nameKey); + } + + if (index > 0) + { + // Append before the link + _appendEscapedMarkdown(UnownedStringSlice(inWord.begin(), trimmedWord.begin()), m_builder); + + // Make into a link + m_builder << "["; + _appendEscapedMarkdown(trimmedWord, m_builder); + m_builder << "](#" << _getLinkName(nameKey, index) << ")"; + + // Append after the link + _appendEscapedMarkdown(UnownedStringSlice(trimmedWord.end(), inWord.end()), m_builder); + return; + } + } + } + + _appendEscapedMarkdown(inWord, m_builder); +} + +void MarkdownCommandOptionsWriter::_appendParagraph(const ConstArrayView<UnownedStringSlice>& words, LinkFlags linkFlags) +{ + if (m_hasLinks && linkFlags) + { + for (auto word : words) + { + _appendMaybeLink(word, linkFlags); + m_builder << " "; + } + } + else + { + for (auto word : words) + { + _appendEscapedMarkdown(word, m_builder); + m_builder << " "; + } + } +} + +void MarkdownCommandOptionsWriter::_appendText(const UnownedStringSlice& text) +{ + List<UnownedStringSlice> lines; + StringUtil::calcLines(text, lines); + for (auto line : lines) + { + if (line.startsWith(toSlice(" "))) + { + // If prefixed means we want to display as is + m_builder << "> " << line << "\n"; + } + else + { + _appendParagraph(line); + m_builder << "\n\n"; + } + } +} + + +void MarkdownCommandOptionsWriter::_appendDescriptionForCategory(Index categoryIndex) +{ + auto& options = *m_commandOptions; + + const auto& categories = options.getCategories(); + const auto& category = categories[categoryIndex]; + + const bool isValue = (category.kind == CommandOptions::CategoryKind::Value); + + // Header + { + if (m_hasLinks) + { + // Output anchor + m_builder << "<a id=\"" << _getLinkName(LookupKind::Category, categoryIndex) << "\"></a>\n"; + } + + m_builder << "# " << category.name << "\n\n"; + + // If there is a description output, making \n split paragraphs + if (category.description.getLength() > 0) + { + _appendText(category.description); + } + } + + for (Index optionIndex = category.optionStartIndex; optionIndex < category.optionEndIndex; ++optionIndex) + { + const auto& option = options.getOptionAt(optionIndex); + + { + List<UnownedStringSlice> names; + StringUtil::split(option.names, ',', names); + + if (isValue) + { + m_builder << "* "; + // Output all the names + m_builder << "`"; + StringUtil::join(names.getBuffer(), names.getCount(), toSlice("`, `"), m_builder); + m_builder << "` "; + } + else + { + if (m_hasLinks) + { + m_builder << "<a id=\"" << _getLinkName(LookupKind::Option, optionIndex) << "\"></a>\n"; + } + + m_builder << "## "; + StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), m_builder); + m_builder << "\n"; + + if (option.usage.getLength()) + { + m_builder << "\n**"; + + if (m_hasLinks) + { + List<UnownedStringSlice> usedCategories; + options.splitUsage(option.usage, usedCategories); + + const char* cur = option.usage.begin(); + for (auto usedCategory : usedCategories) + { + _appendEscapedMarkdown(UnownedStringSlice(cur, usedCategory.begin()), m_builder); + + // Now do the link + const Index usedCategoryIndex = options.findCategoryByName(usedCategory); + + m_builder << "[" << usedCategory << "](#" << _getLinkName(LookupKind::Category, usedCategoryIndex) << ")"; + + cur = usedCategory.end(); + } + + _appendEscapedMarkdown(UnownedStringSlice(cur, option.usage.end()), m_builder); + } + else + { + _appendEscapedMarkdown(option.usage, m_builder); + } + + m_builder << "**\n\n"; + } + } + } + + if (option.description.getLength() > 0) + { + if (isValue) + { + m_builder << ": "; + _appendParagraph(option.description); + } + else + { + _appendText(option.description); + } + } + + m_builder << "\n"; + } + + m_builder << "\n"; +} + +UnownedStringSlice MarkdownCommandOptionsWriter::_getLinkName(const NameKey& key, Index index) +{ + if (auto ptr = m_linkMap.tryGetValue(key)) + { + return m_pool.getSlice(*ptr); + } + + UnownedStringSlice prefix = (key.kind == CommandOptions::LookupKind::Category) ? + m_commandOptions->getFirstNameForCategory(index) : + m_commandOptions->getFirstNameForOption(index); + prefix = prefix.trim('-'); + + if (prefix.getLength() == 0) + { + prefix = toSlice("id"); + } + + StringBuilder buf; + buf << prefix; + + const auto bufLen = buf.getLength(); + + for (Index i = 0; i < 1000; ++i) + { + buf.reduceLength(bufLen); + + if (i > 0) + { + buf << "-" << i; + } + + if (!m_pool.has(buf.getUnownedSlice())) + { + break; + } + } + + const auto handle = m_pool.add(buf.getUnownedSlice()); + m_linkMap.add(key, handle); + + return m_pool.getSlice(handle); +} + +UnownedStringSlice MarkdownCommandOptionsWriter::_getLinkName(CommandOptions::LookupKind kind, Index index) +{ + auto& options = *m_commandOptions; + + // Set up the name key + const auto key = (kind == LookupKind::Category) ? + options.getNameKeyForCategory(index) : + options.getNameKeyForOption(index); + + return _getLinkName(key, index); + +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TextCommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +class TextCommandOptionsWriter : public CommandOptionsWriter +{ +public: + typedef CommandOptionsWriter Super; + + TextCommandOptionsWriter(const Options& options) : + Super(options) + { + } +protected: + // CommandOptionsWriter + virtual void appendDescriptionForCategoryImpl(Index categoryIndex) SLANG_OVERRIDE; + virtual void appendDescriptionImpl() SLANG_OVERRIDE; + + void _appendText(Count indentCount, const UnownedStringSlice& text); + void _appendDescriptionForCategory(Index categoryIndex); +}; + +void TextCommandOptionsWriter::appendDescriptionForCategoryImpl(Index categoryIndex) +{ + _appendDescriptionForCategory(categoryIndex); +} + +void TextCommandOptionsWriter::appendDescriptionImpl() +{ + const auto& categories = m_commandOptions->getCategories(); + for (Index categoryIndex = 0; categoryIndex < categories.getCount(); ++categoryIndex) + { + _appendDescriptionForCategory(categoryIndex); + } +} + +void TextCommandOptionsWriter::_appendDescriptionForCategory(Index categoryIndex) +{ + auto& options = *m_commandOptions; + + const auto& categories = options.getCategories(); + const auto& category = categories[categoryIndex]; + + // Header + { + const auto count = m_builder.getLength(); + if (category.kind == CategoryKind::Value) + { + m_builder << "<" << category.name << ">"; + } + else + { + m_builder << category.name; + } + + const auto length = m_builder.getLength() - count; + m_builder << "\n"; + + m_builder.appendRepeatedChar('=', length); + + m_builder << "\n\n"; + + // If there is a description output it + if (category.description.getLength() > 0) + { + _appendText(0, category.description); + m_builder << "\n"; + } + } + + for (auto& option : options.getOptionsForCategory(categoryIndex)) + { + m_builder << m_options.indent; + + if (option.usage.getLength()) + { + m_builder << option.usage; + } + else + { + List<UnownedStringSlice> names; + StringUtil::split(option.names, ',', names); + + _appendWrappedIndented(1, names, toSlice(", ")); + } + + if (option.description.getLength() == 0) + { + m_builder << "\n"; + continue; + } + + m_builder << ": "; + + _appendText(2, option.description); + + if (option.usage.getLength()) + { + List<Index> usageCategoryIndices; + options.findCategoryIndicesFromUsage(option.usage, usageCategoryIndices); + + for (auto usageCategoryIndex : usageCategoryIndices) + { + auto& usageCat = categories[usageCategoryIndex]; + + m_builder << m_options.indent << m_options.indent; + + m_builder << "<" << usageCat.name << "> can be: "; + + List<UnownedStringSlice> optionNames; + options.getCategoryOptionNames(usageCategoryIndex, optionNames); + + _appendWrappedIndented(2, optionNames, toSlice(", ")); + + m_builder << "\n"; + } + } + } + + m_builder << "\n"; +} + +void TextCommandOptionsWriter::_appendText(Count indentCount, const UnownedStringSlice& text) +{ + List<UnownedStringSlice> lines; + StringUtil::calcLines(text, lines); + + // Remove very last line if it's empty + if (lines.getCount() > 1 && lines.getLast().trim().getLength() == 0) + { + lines.removeLast(); + } + + List<UnownedStringSlice> words; + + for (auto line : lines) + { + if (line.startsWith(toSlice(" "))) + { + // Append the line as is after the indent + _requireIndent(indentCount); + m_builder << line; + } + else if (line.trim().getLength() == 0) + { + } + else + { + words.clear(); + StringUtil::split(line, ' ', words); + + _requireIndent(indentCount); + _appendWrappedIndented(indentCount, words, toSlice(" ")); + } + + m_builder << "\n"; + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +typedef CommandOptionsWriter::Style Style; + +static const NamesDescriptionValue s_styleInfos[] = +{ + { ValueInt(Style::Text), "text", "Text suitable for output to a terminal" }, + { ValueInt(Style::Markdown), "markdown", "Markdown" }, + { ValueInt(Style::NoLinkMarkdown), "no-link-markdown", "Markdown without links" }, +}; + +/* static */ConstArrayView<NamesDescriptionValue> CommandOptionsWriter::getStyleInfos() +{ + return makeConstArrayView(s_styleInfos); +} + +CommandOptionsWriter::CommandOptionsWriter(const Options& options) : + m_pool(StringSlicePool::Style::Default), + m_options(options) +{ + m_options.indent = m_pool.addAndGetSlice(options.indent); +} + +/* static */RefPtr<CommandOptionsWriter> CommandOptionsWriter::create(const Options& options) +{ + if (_isMarkdown(options.style)) + { + return new MarkdownCommandOptionsWriter(options); + } + else + { + return new TextCommandOptionsWriter(options); + } +} + +void CommandOptionsWriter::appendDescriptionForCategory(CommandOptions* options, Index categoryIndex) +{ + m_commandOptions = options; + appendDescriptionForCategoryImpl(categoryIndex); + m_commandOptions = nullptr; +} + +void CommandOptionsWriter::appendDescription(CommandOptions* options) +{ + m_commandOptions = options; + appendDescriptionImpl(); + m_commandOptions = nullptr; +} + +Count CommandOptionsWriter::_getCurrentLineLength() +{ + // Work out the current line length + const char* start = m_builder.begin(); + const char* cur = m_builder.end(); + + Count lineLength = 0; + + if (cur > start) + { + for (--cur; cur > start; --cur) + { + const auto c = *cur; + if (c == '\n' || c == '\r') + { + ++cur; + break; + } + } + + lineLength = Count(ptrdiff_t(m_builder.end() - cur)); + } + + return lineLength; +} + +void CommandOptionsWriter::_requireIndent(Count indentCount) +{ + const auto length = m_builder.getLength(); + if (length) + { + const auto c = m_builder[length - 1]; + if (c == '\n' || c == '\r') + { + for (Index j = 0; j < indentCount; j++) + { + m_builder.append(m_options.indent); + } + } + } +} + +void CommandOptionsWriter::_appendWrappedIndented(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit) +{ + Count lineLength = _getCurrentLineLength(); + + const auto count = slices.getCount(); + + for (Index i = 0; i < count; ++i) + { + auto slice = slices[i]; + + auto sliceLength = slice.getLength(); + + if (i < count - 1) + { + sliceLength += delimit.getLength(); + } + + // If out of space onto the next line + if (lineLength + sliceLength > m_options.lineLength) + { + m_builder.append("\n"); + + lineLength = indentCount * m_options.indent.getLength(); + + for (Index j = 0; j < indentCount; j++) + { + m_builder.append(m_options.indent); + } + } + + m_builder.append(slice); + if (i < count - 1) + { + m_builder.append(delimit); + } + + lineLength += sliceLength; + } +} + +} // namespace Slang + + diff --git a/source/core/slang-command-options-writer.h b/source/core/slang-command-options-writer.h new file mode 100644 index 000000000..eb9a2795f --- /dev/null +++ b/source/core/slang-command-options-writer.h @@ -0,0 +1,69 @@ +#ifndef SLANG_CORE_COMMAND_OPTIONS_WRITER_H +#define SLANG_CORE_COMMAND_OPTIONS_WRITER_H + +#include "slang-command-options.h" + +namespace Slang +{ + +class CommandOptionsWriter : public RefObject +{ +public: + typedef CommandOptions::CategoryKind CategoryKind; + typedef CommandOptions::NameKey NameKey; + typedef CommandOptions::LookupKind LookupKind; + + enum class Style + { + Text, ///< Suitable for output to a terminal + Markdown, ///< Markdown + NoLinkMarkdown, ///< Markdown without links + }; + + static ConstArrayView<NamesDescriptionValue> getStyleInfos(); + + struct Options + { + Style style = Style::Text; ///< The style + Index lineLength = 120; ///< The maximum amount of characters on a line + UnownedStringSlice indent = toSlice(" ");; + }; + + /// Append descirption for a category + void appendDescriptionForCategory(CommandOptions* options, Index categoryIndex); + /// Appends a description of all of the options + void appendDescription(CommandOptions* options); + + /// Get the builder that string is being written to + StringBuilder& getBuilder() { return m_builder; } + + static RefPtr<CommandOptionsWriter> create(const Options& options); + + +protected: + + /// Append descirption for a category + virtual void appendDescriptionForCategoryImpl(Index categoryIndex) = 0; + /// Appends a description of all of the options + virtual void appendDescriptionImpl() = 0; + + // Ctor, use create to create a writer + CommandOptionsWriter(const Options& options); + + /// Get the length of the current line in ascii chars/bytes + Count _getCurrentLineLength(); + + /// Indentation/wrapping + void _requireIndent(Count indentCount); + void _appendWrappedIndented(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit); + + CommandOptions* m_commandOptions = nullptr; + + StringSlicePool m_pool; + StringBuilder m_builder; + Options m_options; +}; + +} // namespace Slang + +#endif diff --git a/source/core/slang-command-options.cpp b/source/core/slang-command-options.cpp index 4577bcb8e..5bbe59a0d 100644 --- a/source/core/slang-command-options.cpp +++ b/source/core/slang-command-options.cpp @@ -7,7 +7,37 @@ #include "slang-byte-encode-util.h" namespace Slang { - + +UnownedStringSlice CommandOptions::getFirstNameForOption(Index optionIndex) +{ + const auto& opt = m_options[optionIndex]; + return StringUtil::getAtInSplit(opt.names, ',', 0); +} + +UnownedStringSlice CommandOptions::getFirstNameForCategory(Index categoryIndex) +{ + const auto& cat = m_categories[categoryIndex]; + return cat.name; +} + +CommandOptions::NameKey CommandOptions::getNameKeyForOption(Index optionIndex) +{ + const auto& opt = m_options[optionIndex]; + const auto& cat = m_categories[opt.categoryIndex]; + NameKey key; + key.nameIndex = m_pool.findIndex(getFirstNameForOption(optionIndex)); + key.kind = (cat.kind == CategoryKind::Option) ? LookupKind::Option : makeLookupKind(opt.categoryIndex); + return key; +} + +CommandOptions::NameKey CommandOptions::getNameKeyForCategory(Index categoryIndex) +{ + NameKey key; + key.nameIndex = m_pool.findIndex(getFirstNameForCategory(categoryIndex)); + key.kind = LookupKind::Category; + return key; +} + SlangResult CommandOptions::_addName(LookupKind kind, const UnownedStringSlice& name, Index targetIndex) { NameKey nameKey; @@ -80,7 +110,16 @@ UnownedStringSlice CommandOptions::_addString(const UnownedStringSlice& slice) Index CommandOptions::_addOption(const UnownedStringSlice& name, const Option& inOption) { - return _addOption(&name, 1, inOption); + if (name.indexOf(',') < 0) + { + return _addOption(&name, 1, inOption); + } + else + { + List<UnownedStringSlice> names; + StringUtil::split(name, ',', names); + return _addOption(names.getBuffer(), names.getCount(), inOption); + } } Index CommandOptions::_addOption(const UnownedStringSlice* names, Count namesCount, const Option& inOption) @@ -386,7 +425,52 @@ void CommandOptions::setCategory(const char* name) m_currentCategoryIndex = -1; } -Index CommandOptions::findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name) const +Index CommandOptions::findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name, NameKey* outNameKey) const +{ + // Look up directly + { + auto index = _findTargetIndexByName(kind, name, outNameKey); + if (index >= 0) + { + return index; + } + } + + // Special case options, which can have prefix styles + if (kind == LookupKind::Option) + { + auto prefixSizes = m_prefixSizes; + + while (prefixSizes) + { + auto prefixSize = ByteEncodeUtil::calcMsb32(prefixSizes); + + if (prefixSize < name.getLength()) + { + // Look it up + const auto index = _findTargetIndexByName(kind, name.head(prefixSize), outNameKey); + if (index >= 0) + { + auto& option = m_options[index]; + + // If the option accepts prefixes, we return the index + if (option.flags & (Flag::CanPrefix | Flag::IsPrefix)) + { + return index; + } + } + } + + // Remove the bit + prefixSizes &= ~(uint32_t(1) << prefixSize); + } + } + + // Was not found + return -1; +} + +Index CommandOptions::_findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name, NameKey* outNameKey) const { const auto nameIndex = m_pool.findIndex(name); // If the name isn't in the pool then there isn't a category with this name @@ -401,6 +485,10 @@ Index CommandOptions::findTargetIndexByName(LookupKind kind, const UnownedString if (auto ptr = m_nameMap.tryGetValue(key)) { + if (outNameKey) + { + *outNameKey = key; + } return *ptr; } @@ -447,66 +535,31 @@ Index CommandOptions::findOptionByCategoryUserValue(UserValue categoryUserValue, return findValueByName(categoryIndex, name); } -Index CommandOptions::findOptionByName(const UnownedStringSlice& name) const -{ - { - auto index = findTargetIndexByName(LookupKind::Option, name); - if (index >= 0) - { - return index; - } - } - - // We need to search for partials - auto prefixSizes = m_prefixSizes; - - while (prefixSizes) - { - auto prefixSize = ByteEncodeUtil::calcMsb32(prefixSizes); - - if (prefixSize < name.getLength()) - { - // Look it up - const auto index = findTargetIndexByName(LookupKind::Option, name.head(prefixSize)); - if (index >= 0) - { - auto& option = m_options[index]; - - // If the option accepts prefixes, we return the index - if (option.flags & (Flag::CanPrefix | Flag::IsPrefix)) - { - return index; - } - } - } - - // Remove the bit - prefixSizes &= ~(uint32_t(1) << prefixSize); - } - - // Was not found - return -1; -} - ConstArrayView<CommandOptions::Option> CommandOptions::getOptionsForCategory(Index categoryIndex) const { const auto& cat = m_categories[categoryIndex]; return makeConstArrayView(m_options.getBuffer() + cat.optionStartIndex, cat.optionEndIndex - cat.optionStartIndex); } -void CommandOptions::getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const + +void CommandOptions::appendCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const { - outNames.clear(); for (const auto& option : getOptionsForCategory(categoryIndex)) { StringUtil::appendSplit(option.names, ',', outNames); } } -void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slice, List<Index>& outCategories) const +void CommandOptions::getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const +{ + outNames.clear(); + appendCategoryOptionNames(categoryIndex, outNames); +} + +void CommandOptions::splitUsage(const UnownedStringSlice& usageSlice, List<UnownedStringSlice>& outSlices) const { - const auto* cur = slice.begin(); - const auto* end = slice.end(); + const auto* cur = usageSlice.begin(); + const auto* end = usageSlice.end(); while (cur < end) { @@ -522,16 +575,16 @@ void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slic { cur++; } - + // If we hit closing > we want to lookup if (cur < end && *cur == '>') { const UnownedStringSlice categoryName(start, cur); Index categoryIndex = findCategoryByName(categoryName); - if (categoryIndex >= 0 && outCategories.indexOf(categoryIndex) < 0) + if (categoryIndex >= 0) { - outCategories.add(categoryIndex); + outSlices.add(categoryName); } } @@ -540,6 +593,22 @@ void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slic } } + +void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slice, List<Index>& outCategories) const +{ + List<UnownedStringSlice> categoryNames; + splitUsage(slice, categoryNames); + + for (auto name : categoryNames) + { + Index categoryIndex = findCategoryByName(name); + if (categoryIndex >= 0 && outCategories.indexOf(categoryIndex) < 0) + { + outCategories.add(categoryIndex); + } + } +} + Count CommandOptions::getOptionCountInRange(Index categoryIndex, UserValue start, UserValue nonInclEnd) const { const UserIndex startIndex = UserIndex(start); @@ -616,227 +685,6 @@ bool CommandOptions::hasContiguousUserValueRange(LookupKind kind, UserValue star return rangeCount == count; } -/* !!!!!!!!!!!!!!!!!!!!!!!!!!! CommandOptionsWriter !!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -void CommandOptionsWriter::appendDescriptionForCategory(const CommandOptions& options, Index categoryIndex) -{ - const auto& categories = options.getCategories(); - - const auto& category = categories[categoryIndex]; - - // Header - { - const auto count = m_builder.getLength(); - if (category.kind == CategoryKind::Value) - { - m_builder << "<" << category.name << ">"; - } - else - { - m_builder << category.name; - } - - const auto length = m_builder.getLength() - count; - m_builder << "\n"; - - m_builder.appendRepeatedChar('=', length); - - m_builder << "\n\n"; - - // If there is a description output it - if (category.description.getLength() > 0) - { - _appendText(0, category.description); - m_builder << "\n"; - } - } - - for (auto& option : options.getOptionsForCategory(categoryIndex)) - { - m_builder << m_indentSlice; - - if (option.usage.getLength()) - { - m_builder << option.usage; - } - else - { - List<UnownedStringSlice> names; - StringUtil::split(option.names, ',', names); - - _appendWithWrap(1, names, toSlice(", ")); - } - - if (option.description.getLength() == 0) - { - m_builder << "\n"; - continue; - } - - m_builder << ": "; - - _appendText(2, option.description); - - if (option.usage.getLength()) - { - List<Index> usageCategoryIndices; - options.findCategoryIndicesFromUsage(option.usage, usageCategoryIndices); - - for (auto usageCategoryIndex : usageCategoryIndices) - { - auto& usageCat = categories[usageCategoryIndex]; - - m_builder << m_indentSlice << m_indentSlice; - m_builder << "<" << usageCat.name << "> can be: "; - - List<UnownedStringSlice> optionNames; - options.getCategoryOptionNames(usageCategoryIndex, optionNames); - - _appendWithWrap(2, optionNames, toSlice(", ")); - - m_builder << "\n"; - } - } - } - - m_builder << "\n"; -} - -void CommandOptionsWriter::appendDescription(const CommandOptions& options) -{ - // Go through categories in order - - const auto& categories = options.getCategories(); - - for (Index categoryIndex = 0; categoryIndex < categories.getCount(); ++categoryIndex) - { - appendDescriptionForCategory(options, categoryIndex); - } -} - -void CommandOptionsWriter::_appendText(Count indentCount, const UnownedStringSlice& text) -{ - List<UnownedStringSlice> lines; - StringUtil::calcLines(text, lines); - - // Remove very last line if it's empty - if (lines.getCount() > 1 && lines.getLast().trim().getLength() == 0) - { - lines.removeLast(); - } - - _appendWithWrap(indentCount, lines); -} - -Count CommandOptionsWriter::_getCurrentLineLength() -{ - // Work out the current line length - const char* start = m_builder.begin(); - const char* cur = m_builder.end(); - - Count lineLength = 0; - - if (cur > start) - { - for (--cur; cur > start; --cur) - { - const auto c = *cur; - if (c == '\n' || c == '\r') - { - ++cur; - break; - } - } - - lineLength = Count(ptrdiff_t(m_builder.end() - cur)); - } - - return lineLength; -} - -void CommandOptionsWriter::_requireIndent(Count indentCount) -{ - const auto length = m_builder.getLength(); - if (length) - { - const auto c = m_builder[length - 1]; - if (c == '\n' || c == '\r') - { - for (Index j = 0; j < indentCount; j++) - { - m_builder.append(m_indentSlice); - } - } - } -} - -void CommandOptionsWriter::_appendWithWrap(Count indentCount, List<UnownedStringSlice>& lines) -{ - List<UnownedStringSlice> words; - - for (auto line : lines) - { - if (line.trim().getLength() == 0 || line.startsWith(toSlice(" "))) - { - // Append the line as is after the indent - _requireIndent(indentCount); - m_builder << line << "\n"; - } - else - { - words.clear(); - StringUtil::split(line, ' ', words); - - _requireIndent(indentCount); - - _appendWithWrap(indentCount, words, toSlice(" ")); - m_builder << "\n"; - } - } -} - - - -void CommandOptionsWriter::_appendWithWrap(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit) -{ - Count lineLength = _getCurrentLineLength(); - - const auto count = slices.getCount(); - - for (Index i = 0; i < count; ++i) - { - auto slice = slices[i]; - - auto sliceLength = slice.getLength(); - - if (i < count - 1) - { - sliceLength += delimit.getLength(); - } - - // If out of space onto the next line - if (lineLength + sliceLength > m_lineLength) - { - m_builder.append("\n"); - - lineLength = indentCount * m_indentSlice.getLength(); - - for (Index j = 0; j < indentCount; j++) - { - m_builder.append(m_indentSlice); - } - } - - m_builder.append(slice); - if (i < count - 1) - { - m_builder.append(delimit); - } - - lineLength += sliceLength; - } -} - } // namespace Slang diff --git a/source/core/slang-command-options.h b/source/core/slang-command-options.h index a92d4d8f8..8b6d7b0ce 100644 --- a/source/core/slang-command-options.h +++ b/source/core/slang-command-options.h @@ -28,6 +28,20 @@ struct CommandOptions Base = 0, ///< Lookup via category index }; + /// A key type that uses the combination of the lookup kind and a name index. + /// Maps to a target index that could be a category or an option index. + struct NameKey + { + typedef NameKey ThisType; + + SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return kind == rhs.kind && nameIndex == rhs.nameIndex; } + SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + HashCode getHashCode() const { return combineHash(Slang::getHashCode(kind), Slang::getHashCode(nameIndex)); } + + LookupKind kind; ///< The kind of lookup + Index nameIndex; ///< The name index in the pool + }; + enum class CategoryKind { Option, ///< Command line option (like "-D") @@ -74,6 +88,16 @@ struct CommandOptions Flags flags = 0; ///< Flags about this option }; + /// Get the first name + UnownedStringSlice getFirstNameForOption(Index optionIndex); + /// Get the first name for the category + UnownedStringSlice getFirstNameForCategory(Index categoryIndex); + + /// Get a name key for an opton + NameKey getNameKeyForOption(Index optionIndex); + /// Get a name key for a category + NameKey getNameKeyForCategory(Index optionIndex); + /// Add a category Index addCategory(CategoryKind kind, const char* name, const char* description, UserValue userValue = kInvalidUserValue); /// Use an already known category. It's an error if the category isn't found @@ -100,14 +124,14 @@ struct CommandOptions void addValuesWithAliases(const ConstArrayView<NameValue>& values); /// Get the target index based off the name and the kind - Index findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name) const; + Index findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name, NameKey* outNameKey = nullptr) const; /// Given a kind and a user value lookup the target index Index findTargetIndexByUserValue(LookupKind kind, UserValue userValue) const; /// Finds the category by name or -1 if not found Index findCategoryByName(const UnownedStringSlice& name) const { return findTargetIndexByName(LookupKind::Category, name); } /// Finds the option index by name or -1 if not found - Index findOptionByName(const UnownedStringSlice& name) const; + Index findOptionByName(const UnownedStringSlice& name) const { return findTargetIndexByName(LookupKind::Option, name); } /// Find the option index of a value, using it's category index and the name Index findValueByName(Index categoryIndex, const UnownedStringSlice& name) const { return findTargetIndexByName(LookupKind(categoryIndex), name); } @@ -139,9 +163,14 @@ struct CommandOptions /// Find all of the categories in the usage slice void findCategoryIndicesFromUsage(const UnownedStringSlice& usageSlice, List<Index>& outCategories) const; + + /// Splits usage into category slices + void splitUsage(const UnownedStringSlice& usageSlice, List<UnownedStringSlice>& outSlices) const; + /// Get all the option names associated with a category index void getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const; - + void appendCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const; + /// Set up a lookup kind from a category index static LookupKind makeLookupKind(Index categoryIndex) { return LookupKind(categoryIndex); } @@ -160,6 +189,7 @@ struct CommandOptions { } + protected: /// Returns name in the m_optionPool or -1 on error SlangResult _addOptionName(const UnownedStringSlice& name, Flags flags, Index targetIndex); SlangResult _addValueName(const UnownedStringSlice& name, Index categoryIndex, Index targetIndex); @@ -175,19 +205,7 @@ struct CommandOptions UnownedStringSlice _addString(const char* text); UnownedStringSlice _addString(const UnownedStringSlice& slice); - /// A key type that uses the combination of the lookup kind and a name index. - /// Maps to a target index that could be a category or an option index. - struct NameKey - { - typedef NameKey ThisType; - - SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return kind == rhs.kind && nameIndex == rhs.nameIndex; } - SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } - HashCode getHashCode() const { return combineHash(Slang::getHashCode(kind), Slang::getHashCode(nameIndex)); } - - LookupKind kind; ///< The kind of lookup - Index nameIndex; ///< The name index in the pool - }; + Index _findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name, NameKey* outNameKey) const; struct UserValueKey { @@ -216,38 +234,6 @@ struct CommandOptions MemoryArena m_arena; ///< For other misc storage }; -struct CommandOptionsWriter -{ - typedef CommandOptions::CategoryKind CategoryKind; - - /// Append descirption for a category - void appendDescriptionForCategory(const CommandOptions& options, Index categoryIndex); - /// Appends a description of all of the options - void appendDescription(const CommandOptions& options); - - /// Get the builder that string is being written to - StringBuilder& getBuilder() { return m_builder; } - - /// Ctor - CommandOptionsWriter(): - m_indentSlice(toSlice(" ")) - { - } - - Count _getCurrentLineLength(); - - void _appendWithWrap(Count indentCount, List<UnownedStringSlice>& slices, const UnownedStringSlice& delimit); - void _appendWithWrap(Count indentCount, List<UnownedStringSlice>& lines); - void _requireIndent(Count indentCount); - void _appendText(Count indentCount, const UnownedStringSlice& text); - - - UnownedStringSlice m_indentSlice; - Count m_lineLength = 80; - - StringBuilder m_builder; -}; - } // namespace Slang #endif diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp index 7de6846d5..ac8a176ad 100644 --- a/source/core/slang-string-util.cpp +++ b/source/core/slang-string-util.cpp @@ -35,6 +35,34 @@ namespace Slang { return areAllEqual(slicesA, slicesB, equalFn); } +/* static */void StringUtil::appendSplitOnWhitespace(const UnownedStringSlice& in, List<UnownedStringSlice>& outSlices) +{ + const char* start = in.begin(); + const char* end = in.end(); + + // Skip any at the start + while (start < end && CharUtil::isWhitespace(*start)) start++; + + while (start < end) + { + // Find all the non white space in a run + const char* cur = start; + while (cur < end && !CharUtil::isWhitespace(*cur)) + { + cur++; + } + + // Add to output + outSlices.add(UnownedStringSlice(start, cur)); + + // Find the next start + start = cur + 1; + + // Skip the split + while (start < end && CharUtil::isWhitespace(*start)) start++; + } +} + /* static */void StringUtil::appendSplit(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices) { const char* start = in.begin(); @@ -113,6 +141,12 @@ namespace Slang { appendSplit(in, splitSlice, outSlices); } +/* static */void StringUtil::splitOnWhitespace(const UnownedStringSlice& in, List<UnownedStringSlice>& outSlices) +{ + outSlices.clear(); + appendSplitOnWhitespace(in, outSlices); +} + /* static */Index StringUtil::split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices) { Index index = 0; diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h index 2293acf8d..4b9a4d5d1 100644 --- a/source/core/slang-string-util.h +++ b/source/core/slang-string-util.h @@ -32,6 +32,8 @@ struct StringUtil static Index split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices); /// Splits into outSlices up to maxSlices. Returns SLANG_OK if of 'in' consumed. static SlangResult split(const UnownedStringSlice& in, char splitChar, Index maxSlices, UnownedStringSlice* outSlices, Index& outSlicesCount); + /// Splits on white space + static void splitOnWhitespace(const UnownedStringSlice& in, List<UnownedStringSlice>& slicesOut); /// Split in, by specified splitChar append into slices out /// Slices contents will directly address into in, so contents will only stay valid as long as in does. @@ -40,6 +42,9 @@ struct StringUtil /// Slices contents will directly address into in, so contents will only stay valid as long as in does. static void appendSplit(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& slicesOut); + /// appends splits on white space + static void appendSplitOnWhitespace(const UnownedStringSlice& in, List<UnownedStringSlice>& slicesOut); + /// Append the joining of in items, separated by 'separator' onto out static void join(const List<String>& in, char separator, StringBuilder& out); static void join(const List<String>& in, const UnownedStringSlice& separator, StringBuilder& out); diff --git a/source/core/slang-type-text-util.cpp b/source/core/slang-type-text-util.cpp index 172426bba..6743800b3 100644 --- a/source/core/slang-type-text-util.cpp +++ b/source/core/slang-type-text-util.cpp @@ -7,9 +7,6 @@ namespace Slang { - - - namespace { // anonymous #define SLANG_SCALAR_TYPES(x) \ @@ -66,17 +63,16 @@ static const TypeTextUtil::CompileTargetInfo s_compileTargetInfos[] = { SLANG_HOST_HOST_CALLABLE, "", "host-host-callable", "Host callable for host execution" }, }; -static const NamesValue s_languageInfos[] = +static const NamesDescriptionValue s_languageInfos[] = { - { SLANG_SOURCE_LANGUAGE_C, "c,C" }, - { SLANG_SOURCE_LANGUAGE_CPP, "cpp,c++,C++,cxx" }, - { SLANG_SOURCE_LANGUAGE_SLANG, "slang" }, - { SLANG_SOURCE_LANGUAGE_GLSL, "glsl" }, - { SLANG_SOURCE_LANGUAGE_HLSL, "hlsl" }, - { SLANG_SOURCE_LANGUAGE_CUDA, "cu,cuda" }, + { SLANG_SOURCE_LANGUAGE_C, "c,C", "C language" }, + { SLANG_SOURCE_LANGUAGE_CPP, "cpp,c++,C++,cxx", "C++ language" }, + { SLANG_SOURCE_LANGUAGE_SLANG, "slang", "Slang language" }, + { SLANG_SOURCE_LANGUAGE_GLSL, "glsl", "GLSL language" }, + { SLANG_SOURCE_LANGUAGE_HLSL, "hlsl", "HLSL language" }, + { SLANG_SOURCE_LANGUAGE_CUDA, "cu,cuda", "CUDA" }, }; - static const NamesDescriptionValue s_compilerInfos[] = { { SLANG_PASS_THROUGH_NONE, "none", "Unknown" }, @@ -91,15 +87,14 @@ static const NamesDescriptionValue s_compilerInfos[] = { SLANG_PASS_THROUGH_LLVM, "llvm", "LLVM/Clang `slang-llvm`" }, }; -static const NameValue s_archiveTypeInfos[] = +static const NamesDescriptionValue s_archiveTypeInfos[] = { - { SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, "riff-deflate"}, - { SLANG_ARCHIVE_TYPE_RIFF_LZ4, "riff-lz4"}, - { SLANG_ARCHIVE_TYPE_ZIP, "zip"}, - { SLANG_ARCHIVE_TYPE_RIFF, "riff"}, + { SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, "riff-deflate", "Slang RIFF using deflate compression" }, + { SLANG_ARCHIVE_TYPE_RIFF_LZ4, "riff-lz4", "Slang RIFF using LZ4 compression" }, + { SLANG_ARCHIVE_TYPE_ZIP, "zip", "Zip file" }, + { SLANG_ARCHIVE_TYPE_RIFF, "riff", "Slang RIFF without compression" }, }; - static const NamesDescriptionValue s_debugInfoFormatInfos[] = { { SLANG_DEBUG_INFO_FORMAT_DEFAULT, "default-format", "Use the default debugging format for the target" }, @@ -134,14 +129,42 @@ static const NamesDescriptionValue s_floatingPointModes[] = "Default floating point mode" } }; +static const NamesDescriptionValue s_optimizationLevels[] = +{ + { SLANG_OPTIMIZATION_LEVEL_NONE, "0,none", "Disable all optimizations" }, + { SLANG_OPTIMIZATION_LEVEL_DEFAULT, "1,default", "Enable a default level of optimization.This is the default if no -o options are used." }, + { SLANG_OPTIMIZATION_LEVEL_HIGH, "2,high", "Enable aggressive optimizations for speed." }, + { SLANG_OPTIMIZATION_LEVEL_MAXIMAL, "3,maximal", "Enable further optimizations, which might have a significant impact on compile time, or involve unwanted tradeoffs in terms of code size." }, +}; + +static const NamesDescriptionValue s_debugLevels[] = +{ + { SLANG_DEBUG_INFO_LEVEL_NONE, "0,none", "Don't emit debug information at all." }, + { SLANG_DEBUG_INFO_LEVEL_MINIMAL, "1,minimal", "Emit as little debug information as possible, while still supporting stack traces." }, + { SLANG_DEBUG_INFO_LEVEL_STANDARD, "2,standard", "Emit whatever is the standard level of debug information for each target." }, + { SLANG_DEBUG_INFO_LEVEL_MAXIMAL, "3,maximal", "Emit as much debug information as possible for each target." }, +}; + +static const NamesDescriptionValue s_fileSystemTypes[] = +{ + { ValueInt(TypeTextUtil::FileSystemType::Default), "default", "Default fike system." }, + { ValueInt(TypeTextUtil::FileSystemType::LoadFile), "load-file", "Just implements loadFile interface, so will be wrapped with CacheFileSystem internally." }, + { ValueInt(TypeTextUtil::FileSystemType::Os), "os", "Use the OS based file system directly (without file system caching)" }, +}; + } // anonymous +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getFileSystemTypeInfos() +{ + return makeConstArrayView(s_fileSystemTypes); +} + /* static */ConstArrayView<TypeTextUtil::CompileTargetInfo> TypeTextUtil::getCompileTargetInfos() { return makeConstArrayView(s_compileTargetInfos); } -/* static */ConstArrayView<NamesValue> TypeTextUtil::getLanguageInfos() +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getLanguageInfos() { return makeConstArrayView(s_languageInfos); } @@ -151,7 +174,7 @@ static const NamesDescriptionValue s_floatingPointModes[] = return makeConstArrayView(s_compilerInfos); } -/* static */ConstArrayView<NameValue> TypeTextUtil::getArchiveTypeInfos() +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getArchiveTypeInfos() { return makeConstArrayView(s_archiveTypeInfos); } @@ -171,6 +194,16 @@ static const NamesDescriptionValue s_floatingPointModes[] = return makeConstArrayView(s_floatingPointModes); } +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getOptimizationLevelInfos() +{ + return makeConstArrayView(s_optimizationLevels); +} + +/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getDebugLevelInfos() +{ + return makeConstArrayView(s_debugLevels); +} + /* static */SlangArchiveType TypeTextUtil::findArchiveType(const UnownedStringSlice& slice) { return NameValueUtil::findValue(getArchiveTypeInfos(), slice, SLANG_ARCHIVE_TYPE_UNDEFINED); diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h index 2249b1215..cf146fb46 100644 --- a/source/core/slang-type-text-util.h +++ b/source/core/slang-type-text-util.h @@ -13,6 +13,13 @@ namespace Slang /// Utility class to allow conversion of types (such as enums) to and from text types struct TypeTextUtil { + enum class FileSystemType + { + Default, + LoadFile, + Os, + }; + struct CompileTargetInfo { SlangCompileTarget target; ///< The target @@ -25,17 +32,23 @@ struct TypeTextUtil static ConstArrayView<CompileTargetInfo> getCompileTargetInfos(); /// Get the language infos - static ConstArrayView<NamesValue> getLanguageInfos(); + static ConstArrayView<NamesDescriptionValue> getLanguageInfos(); /// Get the compiler infos static ConstArrayView<NamesDescriptionValue> getCompilerInfos(); /// Get the archive type infos - static ConstArrayView<NameValue> getArchiveTypeInfos(); + static ConstArrayView<NamesDescriptionValue> getArchiveTypeInfos(); /// Get the debug format types static ConstArrayView<NamesDescriptionValue> getDebugInfoFormatInfos(); - + /// Get the debug levels + static ConstArrayView<NamesDescriptionValue> getDebugLevelInfos(); + /// Get the floating point modes static ConstArrayView<NamesDescriptionValue> getFloatingPointModeInfos(); - + // Get the line directive infos static ConstArrayView<NamesDescriptionValue> getLineDirectiveInfos(); + /// Get the optimization level info + static ConstArrayView<NamesDescriptionValue> getOptimizationLevelInfos(); + /// Get the file system type infos + static ConstArrayView<NamesDescriptionValue> getFileSystemTypeInfos(); /// Get the scalar type as text. static Slang::UnownedStringSlice getScalarTypeName(slang::TypeReflection::ScalarType scalarType); diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 8738c71cc..0b6494ad5 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -70,14 +70,12 @@ DIAGNOSTIC( 14, Error, unknownProfile, "unknown profile '$0'") DIAGNOSTIC( 15, Error, unknownStage, "unknown stage '$0'") DIAGNOSTIC( 16, Error, unknownPassThroughTarget, "unknown pass-through target '$0'") DIAGNOSTIC( 17, Error, unknownCommandLineOption, "unknown command-line option '$0'") -DIAGNOSTIC( 18, Error, unknownFileSystemOption, "unknown file-system option '$0'") DIAGNOSTIC( 19, Error, unknownSourceLanguage, "unknown source language '$0'") DIAGNOSTIC( 20, Error, entryPointsNeedToBeAssociatedWithTranslationUnits, "when using multiple source files, entry points must be specified after their corresponding source file(s)") DIAGNOSTIC( 22, Error, unknownDownstreamCompiler, "unknown downstream compiler '$0'") DIAGNOSTIC( 26, Error, unknownOptimiziationLevel, "unknown optimization level '$0'") -DIAGNOSTIC( 27, Error, unknownDebugInfoLevel, "unknown debug info level '$0'") DIAGNOSTIC( 28, Error, unableToGenerateCodeForTarget, "unable to generate code for target '$0'") diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 6cb53496f..d6486ba18 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -32,7 +32,9 @@ #include "../core/slang-string-slice-pool.h" #include "../core/slang-char-util.h" -#include "../core/slang-command-options.h" +#include "../core/slang-name-value.h" + +#include "../core/slang-command-options-writer.h" #include <assert.h> @@ -49,6 +51,7 @@ enum class OptionKind DepFile, EntryPointName, Help, + HelpStyle, Include, Language, MatrixLayoutColumn, @@ -151,6 +154,10 @@ enum class ValueCategory Stage, LineDirectiveMode, DebugInfoFormat, + HelpStyle, + OptimizationLevel, + DebugLevel, + FileSystemType, CountOf, }; @@ -200,6 +207,18 @@ void initCommandOptions(CommandOptions& options) options.addCategory(CategoryKind::Value, "fp-mode", "Floating Point Mode", UserValue(ValueCategory::FloatingPointMode)); options.addValues(TypeTextUtil::getFloatingPointModeInfos()); + + options.addCategory(CategoryKind::Value, "help-style", "Help Style", UserValue(ValueCategory::HelpStyle)); + options.addValues(CommandOptionsWriter::getStyleInfos()); + + options.addCategory(CategoryKind::Value, "optimization-level", "Optimization Level", UserValue(ValueCategory::OptimizationLevel)); + options.addValues(TypeTextUtil::getOptimizationLevelInfos()); + + options.addCategory(CategoryKind::Value, "debug-level", "Debug Level", UserValue(ValueCategory::DebugLevel)); + options.addValues(TypeTextUtil::getDebugLevelInfos()); + + options.addCategory(CategoryKind::Value, "file-system-type", "File System Type", UserValue(ValueCategory::FileSystemType)); + options.addValues(TypeTextUtil::getFileSystemTypeInfos()); } /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! target !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @@ -307,15 +326,20 @@ void initCommandOptions(CommandOptions& options) const Option generalOpts[] = { - { OptionKind::MacroDefine, "-D?...", "-D<name>[=<value>], -D <name>[=<value>]", "Insert a preprocessor macro." }, + { OptionKind::MacroDefine, "-D?...", "-D<name>[=<value>], -D <name>[=<value>]", + "Insert a preprocessor macro.\n" + "The space between - D and <name> is optional. If no <value> is specified, Slang will define the macro with an empty value." }, { OptionKind::DepFile, "-depfile", "-depfile <path>", "Save the source file dependency list in a file." }, { OptionKind::EntryPointName, "-entry", "-entry <name>", "Specify the name of an entry-point function.\n" + "When compiling from a single file, this defaults to main if you specify a stage using -stage.\n" "Multiple -entry options may be used in a single invocation. " + "When they do, the file associated with the entry point will be the first one found when searching to the left in the command line.\n" "If no -entry options are given, compiler will use [shader(...)] " "attributes to detect entry points."}, { OptionKind::EmitIr, "-emit-ir", nullptr, "Emit IR typically as a '.slang-module' when outputting to a container." }, { OptionKind::Help, "-h,-help,--help", "-h or -h <help-category>", "Print this message, or help in specified category." }, + { OptionKind::HelpStyle, "-help-style", "-help-style <help-style>", "Help formatting style" }, { OptionKind::Include, "-I?...", "-I<path>, -I <path>", "Add a path to be used in resolving '#include' " "and 'import' operations."}, @@ -336,10 +360,10 @@ void initCommandOptions(CommandOptions& options) { OptionKind::Profile, "-profile", "-profile <profile>[+<capability>...]", "Specify the shader profile for code generation.\n" "Accepted profiles are:\n" - " sm_{4_0,4_1,5_0,5_1,6_0,6_1,6_2,6_3,6_4,6_5,6_6}\n" - " glsl_{110,120,130,140,150,330,400,410,420,430,440,450,460}\n" + "* sm_{4_0,4_1,5_0,5_1,6_0,6_1,6_2,6_3,6_4,6_5,6_6}\n" + "* glsl_{110,120,130,140,150,330,400,410,420,430,440,450,460}\n" "Additional profiles that include -stage information:\n" - " {vs,hs,ds,gs,ps}_<version>\n" + "* {vs,hs,ds,gs,ps}_<version>\n" "See -capability for information on <capability>\n" "When multiple -target options are present, each -profile associates " "with the first -target to its left."}, @@ -350,7 +374,9 @@ void initCommandOptions(CommandOptions& options) "May be omitted if entry-point function has a [shader(...)] attribute; " "otherwise required for each -entry option."}, { OptionKind::Target, "-target", "-target <target>", "Specifies the format in which code should be generated."}, - { OptionKind::Version, "-v,-version", nullptr, "Display the build version."}, + { OptionKind::Version, "-v,-version", nullptr, + "Display the build version. This is the contents of git describe --tags.\n" + "It is typically only set from automated builds(such as distros available on github).A user build will by default be 'unknown'."}, { OptionKind::WarningsAsErrors, "-warnings-as-errors", "-warnings-as-errors all or -warnings-as-errors <id>[,<id>...]", "all - Treat all warnings as errors.\n" "<id>[,<id>...]: Treat specific warning ids as errors.\n"}, @@ -377,19 +403,17 @@ void initCommandOptions(CommandOptions& options) { OptionKind::DisableSpecialization, "-disable-specialization", nullptr, "Disables generics and specialization pass." }, { OptionKind::FloatingPointMode, "-fp-mode,-floating-point-mode", "-fp-mode <fp-mode>, -floating-point-mode <fp-mode>", "Control floating point optimizations"}, - { OptionKind::DebugInformation, "-g...", "-g, -g<N>, -g<debug-info-format>", + { OptionKind::DebugInformation, "-g...", "-g, -g<debug-info-format>, -g<debug-level>", "Include debug information in the generated code, where possible.\n" - "N is the amount of information, 0..3, unspecified means 2\n" + "<debug-level> is the amount of information, 0..3, unspecified means 2\n" "<debug-info-format> specifies a debugging info format\n" - "It is valid to have multiple -g options, such as a level and a <debug-info-format>" }, + "It is valid to have multiple -g options, such as a <debug-level> and a <debug-info-format>" }, { OptionKind::LineDirectiveMode, "-line-directive-mode", "-line-directive-mode <line-directive-mode>", "Sets how the `#line` directives should be produced. Available options are:\n" "If not specified, default behavior is to use C-style `#line` directives " "for HLSL and C/C++ output, and traditional GLSL-style `#line` directives " "for GLSL output." }, - { OptionKind::Optimization, "-O...", "-O<N>", - "Set the optimization level.\n" - "N is the amount of optimization, 0..3, default is 1" }, + { OptionKind::Optimization, "-O...", "-O<optimization-level>", "Set the optimization level."}, { OptionKind::Obfuscate, "-obfuscate", nullptr, "Remove all source file information from outputs." }, }; @@ -424,8 +448,9 @@ void initCommandOptions(CommandOptions& options) "to the downstream compiler. -X<compiler>... options -X. will pass *all* of the options " "inbetween the opening -X and -X. to the downstream compiler."}, { OptionKind::PassThrough, "-pass-through", "-pass-through <compiler>", - "Pass the input through mostly unmodified to the \n" - "existing compiler <compiler>." }, + "Pass the input through mostly unmodified to the " + "existing compiler <compiler>.\n" + "These are intended for debugging/testing purposes, when you want to be able to see what these existing compilers do with the \"same\" input and options"}, }; _addOptions(makeConstArrayView(downstreamOpts), options); @@ -470,9 +495,8 @@ void initCommandOptions(CommandOptions& options) { OptionKind::EmitSpirvDirectly, "-emit-spirv-directly", nullptr, "Generate SPIR-V output directly (otherwise through " "GLSL and using the glslang compiler)"}, - { OptionKind::FileSystem, "-file-system", "-file-system <fs>", - "Set the filesystem hook to use for a compile request.\n" - "Accepted file systems: default, load-file, os" }, + { OptionKind::FileSystem, "-file-system", "-file-system <file-system-type>", + "Set the filesystem hook to use for a compile request."}, { OptionKind::Heterogeneous, "-heterogeneous", nullptr, "Output heterogeneity-related code." }, { OptionKind::NoMangle, "-no-mangle", nullptr, "Do as little mangling of names as possible." } }; @@ -1087,11 +1111,11 @@ struct OptionsParser return SLANG_OK; } - SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, DiagnosticSink* sink, CommandOptions::UserValue& outValue) + SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, const UnownedStringSlice& name, DiagnosticSink* sink, CommandOptions::UserValue& outValue) { auto& cmdOptions = asInternal(session)->m_commandOptions; - const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), arg.value.getUnownedSlice()); + const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), name); if (optionIndex < 0) { const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory)); @@ -1114,6 +1138,46 @@ struct OptionsParser outValue = cmdOptions.getOptionAt(optionIndex).userValue; return SLANG_OK; } + + SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, DiagnosticSink* sink, CommandOptions::UserValue& outValue) + { + return _getValue(valueCategory, arg, arg.value.getUnownedSlice(), sink, outValue); + } + + SlangResult _getValue(const ConstArrayView<ValueCategory>& valueCategories, const CommandLineArg& arg, const UnownedStringSlice& name, DiagnosticSink* sink, ValueCategory& outCat, CommandOptions::UserValue& outValue) + { + auto& cmdOptions = asInternal(session)->m_commandOptions; + + for (auto valueCategory : valueCategories) + { + const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), name); + if (optionIndex >= 0) + { + outCat = valueCategory; + outValue = cmdOptions.getOptionAt(optionIndex).userValue; + return SLANG_OK; + } + } + + List<UnownedStringSlice> names; + for (auto valueCategory : valueCategories) + { + const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory)); + SLANG_ASSERT(categoryIndex >= 0); + if (categoryIndex < 0) + { + return SLANG_FAIL; + } + cmdOptions.appendCategoryOptionNames(categoryIndex, names); + } + + StringBuilder buf; + StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), buf); + + sink->diagnose(arg.loc, Diagnostics::unknownCommandLineValue, buf); + return SLANG_FAIL; + } + SlangResult _expectValue(ValueCategory valueCategory, CommandLineReader& reader, DiagnosticSink* sink, CommandOptions::UserValue& outValue) { CommandLineArg arg; @@ -1208,7 +1272,8 @@ struct OptionsParser // Get the options on the session CommandOptions& options = asInternal(session)->m_commandOptions; - + CommandOptionsWriter::Style helpStyle = CommandOptionsWriter::Style::Text; + auto frontEndReq = requestImpl->getFrontEndReq(); while (reader.hasArg()) @@ -1771,86 +1836,45 @@ struct OptionsParser UnownedStringSlice levelSlice = argValue.getUnownedSlice().tail(2); SlangOptimizationLevel level = SLANG_OPTIMIZATION_LEVEL_DEFAULT; - const char c = levelSlice.getLength() == 1 ? levelSlice[0] : 0; - - switch (c) + if (levelSlice.getLength()) { - case '0': level = SLANG_OPTIMIZATION_LEVEL_NONE; break; - case '1': level = SLANG_OPTIMIZATION_LEVEL_DEFAULT; break; - case '2': level = SLANG_OPTIMIZATION_LEVEL_HIGH; break; - case '3': level = SLANG_OPTIMIZATION_LEVEL_MAXIMAL; break; - default: - { - sink->diagnose(arg.loc, Diagnostics::unknownOptimiziationLevel, arg.value); - return SLANG_FAIL; - } + CommandOptions::UserValue value; + SLANG_RETURN_ON_FAIL(_getValue(ValueCategory::OptimizationLevel, arg, levelSlice, sink, value)); + level = SlangOptimizationLevel(value); } - + compileRequest->setOptimizationLevel(level); break; } case OptionKind::DebugInformation: { + auto name = argValue.getUnownedSlice().tail(2); + // Note: unlike with `-O` above, we have to consider that other // options might have names that start with `-g` and so cannot // just detect it as a prefix. - if (argValue == toSlice("-g")) + if (name.getLength() == 0) { // The default is standard compileRequest->setDebugInfoLevel(SLANG_DEBUG_INFO_LEVEL_STANDARD); } - else if (argValue.getLength() == 3 && argValue[2] >= '0' && argValue[2] <= '3') + else { - // Extract the digit into an index - const Index levelIndex = argValue[2] - '0'; - SLANG_ASSERT(levelIndex >= 0 && levelIndex <= 3); + CommandOptions::UserValue value; + ValueCategory valueCat; + ValueCategory valueCats[] = { ValueCategory::DebugLevel, ValueCategory::DebugInfoFormat }; + SLANG_RETURN_ON_FAIL(_getValue(makeConstArrayView(valueCats), arg, name, sink, valueCat, value)); - // Map indices to enum values - const SlangDebugInfoLevel levels[] = + if (valueCat == ValueCategory::DebugLevel) { - SLANG_DEBUG_INFO_LEVEL_NONE, - SLANG_DEBUG_INFO_LEVEL_MINIMAL, - SLANG_DEBUG_INFO_LEVEL_STANDARD, - SLANG_DEBUG_INFO_LEVEL_MAXIMAL - }; - - const auto level = levels[levelIndex]; - compileRequest->setDebugInfoLevel(level); - } - else - { - // Perhaps it's trying to specify a format - auto formatName = argValue.getUnownedSlice().tail(2); - - SlangDebugInfoFormat format; - if (SLANG_FAILED(TypeTextUtil::findDebugInfoFormat(formatName, format))) + const auto level = (SlangDebugInfoLevel)value; + compileRequest->setDebugInfoLevel(level); + } + else { - List<String> debugOptions; - - debugOptions.add(toSlice("-g")); - - for (Int i = 0; i <= 3; ++i) - { - StringBuilder buf; - buf << toSlice("-g") << i; - debugOptions.add(buf); - } - - for (Index i = 0; i < SLANG_DEBUG_INFO_FORMAT_COUNT_OF; ++i) - { - StringBuilder buf; - buf << toSlice("-g") << TypeTextUtil::getDebugInfoFormatName(SlangDebugInfoFormat(i)); - debugOptions.add(buf); - } - - StringBuilder buf; - StringUtil::join(debugOptions, toSlice(", "), buf); - - sink->diagnose(arg.loc, Diagnostics::unknownDebugOption, buf); - return SLANG_FAIL; + const auto debugFormat = (SlangDebugInfoFormat)value; + compileRequest->setDebugInfoFormat(debugFormat); } - - compileRequest->setDebugInfoFormat(format); } break; } @@ -1858,27 +1882,15 @@ struct OptionsParser case OptionKind::Obfuscate: requestImpl->getLinkage()->m_obfuscateCode = true; break; case OptionKind::FileSystem: { - CommandLineArg name; - SLANG_RETURN_ON_FAIL(reader.expectArg(name)); + CommandOptions::UserValue value; + SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::FileSystemType, reader, sink, value)); + typedef TypeTextUtil::FileSystemType FileSystemType; - if (name.value == "default") - { - compileRequest->setFileSystem(nullptr); - } - else if (name.value == "load-file") + switch (FileSystemType(value)) { - // 'Simple' just implements loadFile interface, so will be wrapped with CacheFileSystem internally - compileRequest->setFileSystem(OSFileSystem::getLoadSingleton()); - } - else if (name.value == "os") - { - // 'Immutable' implements the ISlangFileSystemExt interface - and will be used directly - compileRequest->setFileSystem(OSFileSystem::getExtSingleton()); - } - else - { - sink->diagnose(name.loc, Diagnostics::unknownFileSystemOption, name.value); - return SLANG_FAIL; + case FileSystemType::Default: compileRequest->setFileSystem(nullptr); break; + case FileSystemType::LoadFile: compileRequest->setFileSystem(OSFileSystem::getLoadSingleton()); break; + case FileSystemType::Os: compileRequest->setFileSystem(OSFileSystem::getExtSingleton()); break; } break; } @@ -1950,9 +1962,15 @@ struct OptionsParser sink->diagnoseRaw(Severity::Note, session->getBuildTagString()); break; } + case OptionKind::HelpStyle: + { + CommandOptions::UserValue value; + SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::HelpStyle, reader, sink, value)); + helpStyle = CommandOptionsWriter::Style(value); + break; + } case OptionKind::Help: { - Index categoryIndex = -1; if (reader.hasArg()) @@ -1960,24 +1978,48 @@ struct OptionsParser auto catArg = reader.getArgAndAdvance(); categoryIndex = options.findCategoryByCaseInsensitiveName(catArg.value.getUnownedSlice()); - if (categoryIndex) + if (categoryIndex < 0) { sink->diagnose(catArg.loc, Diagnostics::unknownHelpCategory); return SLANG_FAIL; } } - CommandOptionsWriter writer; - auto& buf = writer.getBuilder(); + CommandOptionsWriter::Options writerOptions; + writerOptions.style = helpStyle; + + auto writer = CommandOptionsWriter::create(writerOptions); + + auto& buf = writer->getBuilder(); if (categoryIndex < 0) { - _appendUsageTitle(buf); - writer.appendDescription(options); + // If it's the text style we can inject usage at the top + if (helpStyle == CommandOptionsWriter::Style::Text) + { + _appendUsageTitle(buf); + } + else + { + // NOTE! We need this preamble because if we have links, + // we have to make sure the first thing in markdown *isn't* <> + + buf << "# Slang Command Line Options\n\n"; + buf << "*Usage:*\n"; + buf << "```\n"; + buf << "slangc [options...] [--] <input files>\n\n"; + buf << "# For help\n"; + buf << "slangc -h\n\n"; + buf << "# To generate this file\n"; + buf << "slangc -help-style markdown -h\n"; + buf << "```\n"; + } + + writer->appendDescription(&options); } else { - writer.appendDescriptionForCategory(options, categoryIndex); + writer->appendDescriptionForCategory(&options, categoryIndex); } sink->diagnoseRaw(Severity::Note, buf.getBuffer()); |
