summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2023-04-29 09:24:26 -0400
committerGitHub <noreply@github.com>2023-04-29 09:24:26 -0400
commit19c0866b050a022406867aa650302f4efbf8e010 (patch)
treef5ed4e1f5d27865518daf81c7e861b4908186b23
parentc571bcb025009f9c662e8d631fa49dbfed560287 (diff)
CommandOptions (#2856)
* 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.
-rw-r--r--build/visual-studio/core/core.vcxproj4
-rw-r--r--build/visual-studio/core/core.vcxproj.filters12
-rw-r--r--build/visual-studio/slang-rt/slang-rt.vcxproj4
-rw-r--r--build/visual-studio/slang-rt/slang-rt.vcxproj.filters12
-rw-r--r--source/core/slang-command-options.cpp842
-rw-r--r--source/core/slang-command-options.h253
-rw-r--r--source/core/slang-name-value.cpp163
-rw-r--r--source/core/slang-name-value.h76
-rw-r--r--source/core/slang-string-util.cpp24
-rw-r--r--source/core/slang-string-util.h7
-rw-r--r--source/core/slang-type-text-util.cpp292
-rw-r--r--source/core/slang-type-text-util.h31
-rw-r--r--source/slang/slang-capability.cpp9
-rw-r--r--source/slang/slang-capability.h4
-rw-r--r--source/slang/slang-compiler.cpp13
-rwxr-xr-xsource/slang/slang-compiler.h4
-rw-r--r--source/slang/slang-diagnostic-defs.h5
-rw-r--r--source/slang/slang-options.cpp1223
-rw-r--r--source/slang/slang-options.h4
-rw-r--r--source/slang/slang-profile.h8
-rw-r--r--source/slang/slang.cpp4
-rw-r--r--tests/diagnostics/command-line/unknown-line-directive-mode.slang.expected2
-rw-r--r--tests/diagnostics/command-line/unknown-option.slang.expected3
23 files changed, 2345 insertions, 654 deletions
diff --git a/build/visual-studio/core/core.vcxproj b/build/visual-studio/core/core.vcxproj
index 7799dd8f4..90fcfe8ef 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.h" />
<ClInclude Include="..\..\..\source\core\slang-common.h" />
<ClInclude Include="..\..\..\source\core\slang-compression-system.h" />
<ClInclude Include="..\..\..\source\core\slang-crypto.h" />
@@ -298,6 +299,7 @@
<ClInclude Include="..\..\..\source\core\slang-math.h" />
<ClInclude Include="..\..\..\source\core\slang-memory-arena.h" />
<ClInclude Include="..\..\..\source\core\slang-memory-file-system.h" />
+ <ClInclude Include="..\..\..\source\core\slang-name-value.h" />
<ClInclude Include="..\..\..\source\core\slang-offset-container.h" />
<ClInclude Include="..\..\..\source\core\slang-persistent-cache.h" />
<ClInclude Include="..\..\..\source\core\slang-platform.h" />
@@ -342,6 +344,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.cpp" />
<ClCompile Include="..\..\..\source\core\slang-crypto.cpp" />
<ClCompile Include="..\..\..\source\core\slang-deflate-compression-system.cpp" />
<ClCompile Include="..\..\..\source\core\slang-file-system.cpp" />
@@ -353,6 +356,7 @@
<ClCompile Include="..\..\..\source\core\slang-lz4-compression-system.cpp" />
<ClCompile Include="..\..\..\source\core\slang-memory-arena.cpp" />
<ClCompile Include="..\..\..\source\core\slang-memory-file-system.cpp" />
+ <ClCompile Include="..\..\..\source\core\slang-name-value.cpp" />
<ClCompile Include="..\..\..\source\core\slang-offset-container.cpp" />
<ClCompile Include="..\..\..\source\core\slang-persistent-cache.cpp" />
<ClCompile Include="..\..\..\source\core\slang-platform.cpp" />
diff --git a/build/visual-studio/core/core.vcxproj.filters b/build/visual-studio/core/core.vcxproj.filters
index 8ad3e42d5..41d0fafe1 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.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\core\slang-common.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -108,6 +111,9 @@
<ClInclude Include="..\..\..\source\core\slang-memory-file-system.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\core\slang-name-value.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\core\slang-offset-container.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -236,6 +242,9 @@
<ClCompile Include="..\..\..\source\core\slang-command-line.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\core\slang-command-options.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\core\slang-crypto.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -269,6 +278,9 @@
<ClCompile Include="..\..\..\source\core\slang-memory-file-system.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\core\slang-name-value.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\core\slang-offset-container.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 31627101c..41c44a051 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.h" />
<ClInclude Include="..\..\..\source\core\slang-common.h" />
<ClInclude Include="..\..\..\source\core\slang-compression-system.h" />
<ClInclude Include="..\..\..\source\core\slang-crypto.h" />
@@ -310,6 +311,7 @@
<ClInclude Include="..\..\..\source\core\slang-math.h" />
<ClInclude Include="..\..\..\source\core\slang-memory-arena.h" />
<ClInclude Include="..\..\..\source\core\slang-memory-file-system.h" />
+ <ClInclude Include="..\..\..\source\core\slang-name-value.h" />
<ClInclude Include="..\..\..\source\core\slang-offset-container.h" />
<ClInclude Include="..\..\..\source\core\slang-persistent-cache.h" />
<ClInclude Include="..\..\..\source\core\slang-platform.h" />
@@ -355,6 +357,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.cpp" />
<ClCompile Include="..\..\..\source\core\slang-crypto.cpp" />
<ClCompile Include="..\..\..\source\core\slang-deflate-compression-system.cpp" />
<ClCompile Include="..\..\..\source\core\slang-file-system.cpp" />
@@ -366,6 +369,7 @@
<ClCompile Include="..\..\..\source\core\slang-lz4-compression-system.cpp" />
<ClCompile Include="..\..\..\source\core\slang-memory-arena.cpp" />
<ClCompile Include="..\..\..\source\core\slang-memory-file-system.cpp" />
+ <ClCompile Include="..\..\..\source\core\slang-name-value.cpp" />
<ClCompile Include="..\..\..\source\core\slang-offset-container.cpp" />
<ClCompile Include="..\..\..\source\core\slang-persistent-cache.cpp" />
<ClCompile Include="..\..\..\source\core\slang-platform.cpp" />
diff --git a/build/visual-studio/slang-rt/slang-rt.vcxproj.filters b/build/visual-studio/slang-rt/slang-rt.vcxproj.filters
index 9ae64d433..4fa4373ff 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.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\core\slang-common.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -108,6 +111,9 @@
<ClInclude Include="..\..\..\source\core\slang-memory-file-system.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\core\slang-name-value.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\core\slang-offset-container.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -239,6 +245,9 @@
<ClCompile Include="..\..\..\source\core\slang-command-line.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\core\slang-command-options.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\core\slang-crypto.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -272,6 +281,9 @@
<ClCompile Include="..\..\..\source\core\slang-memory-file-system.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\core\slang-name-value.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\core\slang-offset-container.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/core/slang-command-options.cpp b/source/core/slang-command-options.cpp
new file mode 100644
index 000000000..4577bcb8e
--- /dev/null
+++ b/source/core/slang-command-options.cpp
@@ -0,0 +1,842 @@
+// slang-command-options.cpp
+
+#include "slang-command-options.h"
+
+#include "slang-string-util.h"
+#include "slang-char-util.h"
+#include "slang-byte-encode-util.h"
+
+namespace Slang {
+
+SlangResult CommandOptions::_addName(LookupKind kind, const UnownedStringSlice& name, Index targetIndex)
+{
+ NameKey nameKey;
+ nameKey.kind = kind;
+ nameKey.nameIndex = (Index)m_pool.add(name);
+
+ if (m_nameMap.tryGetValueOrAdd(nameKey, targetIndex))
+ {
+ SLANG_ASSERT(!"Option is already added!");
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+SlangResult CommandOptions::_addOptionName(const UnownedStringSlice& name, Flags flags, Index targetIndex)
+{
+ SLANG_RETURN_ON_FAIL(_addName(LookupKind::Option, name, targetIndex));
+
+ // Add to prefix flags
+ if (flags & (Flag::CanPrefix | Flag::IsPrefix))
+ {
+ const auto length = name.getLength();
+ SLANG_ASSERT(length < 32);
+ m_prefixSizes |= uint32_t(1) << length;
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult CommandOptions::_addValueName(const UnownedStringSlice& name, Index categoryIndex, Index optionIndex)
+{
+ return _addName(LookupKind(categoryIndex), name, optionIndex);
+}
+
+SlangResult CommandOptions::_addUserValue(LookupKind kind, UserValue userValue, Index targetIndex)
+{
+ // If it's invalid we don't need to add it
+ if (userValue == kInvalidUserValue)
+ {
+ return SLANG_OK;
+ }
+
+ UserValueKey userValueKey;
+ userValueKey.kind = kind;
+ userValueKey.userValue = userValue;
+
+ if (m_userValueMap.tryGetValueOrAdd(userValueKey, targetIndex))
+ {
+ SLANG_ASSERT(!"UserValue is already used for this kind!");
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+UnownedStringSlice CommandOptions::_addString(const char* text)
+{
+ if (text == nullptr)
+ {
+ return UnownedStringSlice();
+ }
+ return _addString(UnownedStringSlice(text));
+}
+
+UnownedStringSlice CommandOptions::_addString(const UnownedStringSlice& slice)
+{
+ const auto length = slice.getLength();
+ const char* dst = m_arena.allocateString(slice.begin(), length);
+ return UnownedStringSlice(dst, length);
+}
+
+Index CommandOptions::_addOption(const UnownedStringSlice& name, const Option& inOption)
+{
+ return _addOption(&name, 1, inOption);
+}
+
+Index CommandOptions::_addOption(const UnownedStringSlice* names, Count namesCount, const Option& inOption)
+{
+ SLANG_ASSERT(namesCount > 0);
+ SLANG_ASSERT(inOption.categoryIndex >= 0);
+
+ if (namesCount <= 0 || inOption.categoryIndex < 0)
+ {
+ return -1;
+ }
+
+ auto& cat = m_categories[inOption.categoryIndex];
+
+ // If there are already options associated with this category, we have to be in the run of the last ones added
+ if (cat.optionStartIndex != cat.optionEndIndex)
+ {
+ // If we aren't at the end then this is an error
+ if (cat.optionEndIndex != m_options.getCount())
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ // Move to the end of the option list
+ cat.optionStartIndex = m_options.getCount();
+ cat.optionEndIndex = cat.optionStartIndex;
+ }
+
+ Option option(inOption);
+
+ const Index optionIndex = m_options.getCount();
+
+ if (cat.kind == CategoryKind::Option)
+ {
+ for (Index i = 0; i < namesCount; ++i)
+ {
+ if (SLANG_FAILED(_addOptionName(names[i], inOption.flags, optionIndex)))
+ {
+ return -1;
+ }
+ }
+ if (SLANG_FAILED(_addUserValue(LookupKind::Option, inOption.userValue, optionIndex)))
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ for (Index i = 0; i < namesCount; ++i)
+ {
+ _addValueName(names[i], inOption.categoryIndex, optionIndex);
+ }
+ if (SLANG_FAILED(_addUserValue(LookupKind(inOption.categoryIndex), inOption.userValue, optionIndex)))
+ {
+ return -1;
+ }
+ }
+
+ if (namesCount == 1)
+ {
+ // We already have storage on the slice
+ option.names = m_pool.addAndGetSlice(names[0]);
+ }
+ else
+ {
+ // Put all of the names in the list
+ StringBuilder buf;
+ StringUtil::join(names, namesCount, ',', buf);
+ // Allocate storage no in the pool
+ option.names = _addString(buf.getUnownedSlice());
+ }
+
+ m_options.add(option);
+
+ // Set the end index
+ cat.optionEndIndex = optionIndex + 1;
+
+ return optionIndex;
+}
+
+static void _handlePostFix(UnownedStringSlice& ioSlice, CommandOptions::Flags& ioFlags)
+{
+ if (ioSlice.endsWith(toSlice("...")))
+ {
+ if (ioSlice.endsWith(toSlice("?...")))
+ {
+ ioFlags |= CommandOptions::Flag::CanPrefix;
+ ioSlice = ioSlice.head(ioSlice.getLength() - 4);
+ }
+ else
+ {
+ ioFlags |= CommandOptions::Flag::IsPrefix;
+ ioSlice = ioSlice.head(ioSlice.getLength() - 3);
+ }
+ }
+}
+
+void CommandOptions::add(const char* inName, const char* usage, const char* description, UserValue userValue)
+{
+ UnownedStringSlice nameSlice(inName);
+
+ Option option;
+ option.categoryIndex = m_currentCategoryIndex;
+ option.usage = _addString(usage);
+ option.description = _addString(UnownedStringSlice(description));
+ option.userValue = userValue;
+ option.flags = 0;
+
+ if (nameSlice.indexOf(',') >= 0)
+ {
+ List<UnownedStringSlice> names;
+ StringUtil::split(nameSlice, ',', names);
+
+ for (auto& name : names)
+ {
+ _handlePostFix(name, option.flags);
+ }
+
+ _addOption(names.getBuffer(), names.getCount(), option);
+ }
+ else
+ {
+ _handlePostFix(nameSlice, option.flags);
+
+ _addOption(&nameSlice, 1, option);
+ }
+}
+
+void CommandOptions::add(const UnownedStringSlice* names, Count namesCount, const char* usage, const char* description, UserValue userValue, Flags flags)
+{
+ Option option;
+ option.categoryIndex = m_currentCategoryIndex;
+ option.usage = _addString(usage);
+ option.description = _addString(UnownedStringSlice(description));
+ option.flags = flags;
+ option.userValue = userValue;
+
+ _addOption(names, namesCount, option);
+}
+
+Index CommandOptions::_addValue(const UnownedStringSlice& name, const Option& inOption)
+{
+ SLANG_ASSERT(m_currentCategoryIndex >= 0);
+ SLANG_ASSERT(m_categories[m_currentCategoryIndex].kind == CategoryKind::Value);
+
+ return _addOption(name, inOption);
+}
+
+void CommandOptions::addValues(const ValuePair* pairs, Count pairsCount)
+{
+ for (auto& pair : makeConstArrayView(pairs, pairsCount))
+ {
+ addValue(pair.name, pair.description);
+ }
+}
+
+void CommandOptions::addValues(const ConstArrayView<NameValue>& values)
+{
+ for (const auto& value : values)
+ {
+ addValue(value.name, UserValue(value.value));
+ }
+}
+
+void CommandOptions::addValues(const ConstArrayView<NamesValue>& values)
+{
+ for (const auto& value : values)
+ {
+ addValue(value.names, UserValue(value.value));
+ }
+}
+
+void CommandOptions::addValues(const ConstArrayView<NamesDescriptionValue>& values)
+{
+ for (const auto& value : values)
+ {
+ addValue(value.names, value.description, UserValue(value.value));
+ }
+}
+
+void CommandOptions::addValuesWithAliases(const ConstArrayView<NameValue>& inValues)
+{
+ List<NameValue> values;
+ values.addRange(inValues.getBuffer(), inValues.getCount());
+
+ values.sort([](const NameValue& a, const NameValue& b) -> bool { return a.value < b.value; });
+
+ List<UnownedStringSlice> names;
+
+ const Count count = values.getCount();
+ Index i = 0;
+ while (i < count)
+ {
+ names.clear();
+
+ const auto value = values[i].value;
+ names.add(UnownedStringSlice(values[i++].name));
+
+ for (; i < count && values[i].value == value; ++i)
+ {
+ names.add(UnownedStringSlice(values[i].name));
+ }
+
+ addValue(names.getBuffer(), names.getCount(), UserValue(value));
+ }
+}
+
+void CommandOptions::addValue(const UnownedStringSlice& name, UserValue userValue)
+{
+ Option option;
+ option.categoryIndex = m_currentCategoryIndex;
+ option.userValue = userValue;
+ _addValue(name, option);
+}
+
+void CommandOptions::addValue(const UnownedStringSlice& name, const UnownedStringSlice& description, UserValue userValue)
+{
+ Option option;
+ option.categoryIndex = m_currentCategoryIndex;
+ option.description = _addString(description);
+ option.userValue = userValue;
+ _addValue(name, option);
+}
+
+void CommandOptions::addValue(const UnownedStringSlice* names, Count namesCount, UserValue userValue)
+{
+ Option option;
+ option.categoryIndex = m_currentCategoryIndex;
+ option.userValue = userValue;
+
+ SLANG_ASSERT(m_currentCategoryIndex >= 0);
+ SLANG_ASSERT(m_categories[m_currentCategoryIndex].kind == CategoryKind::Value);
+
+ _addOption(names, namesCount, option);
+}
+
+void CommandOptions::addValue(const char* inName, const char* description, UserValue userValue)
+{
+ const UnownedStringSlice name(inName);
+
+ if (description)
+ {
+ addValue(name, UnownedStringSlice(description), userValue);
+ }
+ else
+ {
+ addValue(name, userValue);
+ }
+}
+
+void CommandOptions::addValue(const char* name, UserValue userValue)
+{
+ addValue(UnownedStringSlice(name), userValue);
+}
+
+Index CommandOptions::addCategory(CategoryKind kind, const char* name, const char* description, UserValue userValue)
+{
+ const UnownedStringSlice nameSlice(name);
+
+ const auto categoryIndex = m_categories.getCount();
+
+ if (SLANG_FAILED(_addName(LookupKind::Category, nameSlice, categoryIndex)))
+ {
+ return -1;
+ }
+
+ if (userValue != kInvalidUserValue)
+ {
+ _addUserValue(LookupKind::Category, userValue, categoryIndex);
+ }
+
+ Category cat;
+ cat.kind = kind;
+ cat.name = _addString(nameSlice);
+ cat.description = _addString(description);
+ cat.userValue = userValue;
+
+ m_currentCategoryIndex = categoryIndex;
+
+ m_categories.add(cat);
+
+ return categoryIndex;
+}
+
+void CommandOptions::setCategory(const char* name)
+{
+ const UnownedStringSlice nameSlice(name);
+
+ for (Index i = 0; i < m_categories.getCount(); ++i)
+ {
+ auto& cat = m_categories[i];
+ if (cat.name == nameSlice)
+ {
+ m_currentCategoryIndex = i;
+ return;
+ }
+ }
+
+ SLANG_ASSERT(!"Category not found");
+
+ m_currentCategoryIndex = -1;
+}
+
+Index CommandOptions::findTargetIndexByName(LookupKind kind, const UnownedStringSlice& name) 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
+ if (nameIndex < 0)
+ {
+ return -1;
+ }
+
+ NameKey key;
+ key.kind = kind;
+ key.nameIndex = nameIndex;
+
+ if (auto ptr = m_nameMap.tryGetValue(key))
+ {
+ return *ptr;
+ }
+
+ return -1;
+}
+
+Index CommandOptions::findTargetIndexByUserValue(LookupKind kind, UserValue userValue) const
+{
+ UserValueKey key;
+ key.kind = kind;
+ key.userValue = userValue;
+
+ if (auto ptr = m_userValueMap.tryGetValue(key))
+ {
+ return *ptr;
+ }
+
+ return -1;
+}
+
+Index CommandOptions::findCategoryByCaseInsensitiveName(const UnownedStringSlice& slice) const
+{
+ const Count count = m_categories.getCount();
+ for (Index i = 0; i < count; ++i)
+ {
+ const auto& cat = m_categories[i];
+
+ if (cat.name.caseInsensitiveEquals(slice))
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+Index CommandOptions::findOptionByCategoryUserValue(UserValue categoryUserValue, const UnownedStringSlice& name) const
+{
+ Index categoryIndex = findTargetIndexByUserValue(LookupKind::Category, categoryUserValue);
+ if (categoryIndex < 0)
+ {
+ return -1;
+ }
+
+ 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
+{
+ outNames.clear();
+ for (const auto& option : getOptionsForCategory(categoryIndex))
+ {
+ StringUtil::appendSplit(option.names, ',', outNames);
+ }
+}
+
+void CommandOptions::findCategoryIndicesFromUsage(const UnownedStringSlice& slice, List<Index>& outCategories) const
+{
+ const auto* cur = slice.begin();
+ const auto* end = slice.end();
+
+ while (cur < end)
+ {
+ // Find <
+ while (cur < end && *cur != '<') cur++;
+
+ // If we found it look for the end
+ if (cur < end && *cur == '<')
+ {
+ ++cur;
+ auto start = cur;
+ while (cur < end && (CharUtil::isAlphaOrDigit(*cur) || *cur == '-' || *cur == '_') && *cur != '>')
+ {
+ 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)
+ {
+ outCategories.add(categoryIndex);
+ }
+ }
+
+ cur++;
+ }
+ }
+}
+
+Count CommandOptions::getOptionCountInRange(Index categoryIndex, UserValue start, UserValue nonInclEnd) const
+{
+ const UserIndex startIndex = UserIndex(start);
+ const UserIndex endIndex = UserIndex(nonInclEnd);
+
+ Count count = 0;
+
+ for (auto& opt : getOptionsForCategory(categoryIndex))
+ {
+ const auto val = opt.userValue;
+ if (val == kInvalidUserValue)
+ {
+ continue;
+ }
+
+ const auto valIndex = UserIndex(val);
+ count += Index(valIndex >= startIndex && valIndex < endIndex);
+ }
+
+ return count;
+}
+
+Count CommandOptions::getOptionCountInRange(LookupKind kind, UserValue start, UserValue nonInclEnd) const
+{
+ Index count = 0;
+
+ if (kind == LookupKind::Category)
+ {
+ const UserIndex startIndex = UserIndex(start);
+ const UserIndex endIndex = UserIndex(nonInclEnd);
+
+ for (auto& cat : m_categories)
+ {
+ if (cat.userValue != kInvalidUserValue)
+ {
+ const auto valIndex = UserIndex(cat.userValue);
+ count += Index(valIndex >= startIndex && valIndex < endIndex);
+ }
+ }
+ }
+ if (kind == LookupKind::Option)
+ {
+ // If we are lookup up options, then we iterate over all option categories
+ const auto catCount = m_categories.getCount();
+ for (Index categoryIndex = 0; categoryIndex < catCount; ++categoryIndex)
+ {
+ if (m_categories[categoryIndex].kind == CategoryKind::Option)
+ {
+ count += getOptionCountInRange(categoryIndex, start, nonInclEnd);
+ }
+ }
+ }
+ else if (Index(kind) >= 0)
+ {
+ // It's a regular category
+ count = getOptionCountInRange(Index(kind), start, nonInclEnd);
+ }
+
+ return count;
+}
+
+
+bool CommandOptions::hasContiguousUserValueRange(LookupKind kind, UserValue start, UserValue nonInclEnd) const
+{
+ const Count rangeCount = Count(nonInclEnd) - Count(start);
+ SLANG_ASSERT(rangeCount >= 0);
+
+ if (rangeCount <= 0)
+ {
+ return true;
+ }
+
+ const Count count = getOptionCountInRange(kind, start, nonInclEnd);
+ 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
new file mode 100644
index 000000000..a92d4d8f8
--- /dev/null
+++ b/source/core/slang-command-options.h
@@ -0,0 +1,253 @@
+#ifndef SLANG_CORE_COMMAND_OPTIONS_H
+#define SLANG_CORE_COMMAND_OPTIONS_H
+
+#include "slang-basic.h"
+#include "slang-string-slice-pool.h"
+#include "slang-name-value.h"
+
+namespace Slang
+{
+
+/* For convenience we encode within "names" flags.
+"-D..." means that -D *must* be followed by the value
+"-D?..." means that -D *can* be a prefix, or it might be followed with the arg
+*/
+
+struct CommandOptions
+{
+ typedef uint32_t Flags;
+
+ typedef int32_t UserIndex;
+ enum class UserValue : UserIndex;
+ static const UserValue kInvalidUserValue = UserValue(0x80000000);
+
+ enum class LookupKind : int32_t
+ {
+ Category = -2, ///< Lookup a category name
+ Option = -1, ///< Lookup an option name (all options use the same lookup index even if in different categories)
+ Base = 0, ///< Lookup via category index
+ };
+
+ enum class CategoryKind
+ {
+ Option, ///< Command line option (like "-D")
+ Value, ///< One of a set of values (such as an enum or some other kind of list of values)
+ };
+
+ struct ValuePair
+ {
+ const char* name;
+ const char* description;
+ };
+
+ struct Category
+ {
+ UserValue userValue = kInvalidUserValue;
+
+ CategoryKind kind;
+ UnownedStringSlice name;
+ UnownedStringSlice description;
+
+ // Holds the span that defines all of the options associated with the category
+ Index optionStartIndex = 0;
+ Index optionEndIndex = 0;
+ };
+
+ struct Flag
+ {
+ enum Enum : Flags
+ {
+ CanPrefix = 0x1, /// Allows -Dfsggf or -D fdsfsd
+ IsPrefix = 0x2, /// Is an option that can only be a prefix
+ };
+ };
+
+ struct Option
+ {
+ UnownedStringSlice names; ///< Comma delimited list of names, first name is the default
+ UnownedStringSlice usage; ///< Describes usage, can be empty
+ UnownedStringSlice description; ///< A description of usage
+
+ UserValue userValue = kInvalidUserValue;
+
+ Index categoryIndex = -1; ///< Category this option belongs to
+ Flags flags = 0; ///< Flags about this option
+ };
+
+ /// 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
+ void setCategory(const char* name);
+
+ void add(const char* name, const char* usage, const char* description, UserValue userValue = kInvalidUserValue);
+ void add(const UnownedStringSlice* names, Count namesCount, const char* usage, const char* description, UserValue userValue = kInvalidUserValue, Flags flags = 0);
+
+ void addValue(const UnownedStringSlice& name, UserValue userValue = kInvalidUserValue);
+ void addValue(const UnownedStringSlice& name, const UnownedStringSlice& description, UserValue userValue = kInvalidUserValue);
+ void addValue(const char* name, const char* description, UserValue userValue = kInvalidUserValue);
+ void addValue(const char* name, UserValue userValue = kInvalidUserValue);
+ void addValue(const UnownedStringSlice* names, Count namesCount, UserValue userValue = kInvalidUserValue);
+
+ /// Add values (without UserValue association)
+ void addValues(const ValuePair* pairs, Count pairsCount);
+
+ /// Add values
+ void addValues(const ConstArrayView<NameValue>& values);
+ void addValues(const ConstArrayView<NamesValue>& values);
+ void addValues(const ConstArrayView<NamesDescriptionValue>& values);
+
+ /// Sometimes values are listed with *names* per value. This method will take into account the aliases
+ 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;
+ /// 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;
+ /// 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); }
+
+ /// Get the category index from a user value
+ Index findCategoryByUserValue(UserValue userValue) const { return findTargetIndexByUserValue(LookupKind::Category, userValue); }
+ /// Can only get options
+ Index findOptionByUserValue(UserValue userValue) const { return findTargetIndexByUserValue(LookupKind::Option, userValue); }
+ /// Get a value associated with a category
+ Index findValueByUserValue(Index categoryIndex, UserValue userValue) const { return findTargetIndexByUserValue(LookupKind(categoryIndex), userValue); }
+
+ /// Given a category user value, find the associated name
+ /// Returns -1 if not found
+ Index findOptionByCategoryUserValue(UserValue categoryUserValue, const UnownedStringSlice& name) const;
+
+ /// Find a category by case insensitive name. Returns -1 if not found
+ Index findCategoryByCaseInsensitiveName(const UnownedStringSlice& slice) const;
+
+ /// Given a category index returns all the options associated.
+ ConstArrayView<Option> getOptionsForCategory(Index categoryIndex) const;
+
+ /// Get the categories
+ const List<Category>& getCategories() const { return m_categories; }
+
+ /// Get all the options
+ const List<Option>& getOptions() const { return m_options; }
+
+ /// Get the option at the specified index
+ const Option& getOptionAt(Index index) const { return m_options[index]; }
+
+ /// Find all of the categories in the usage slice
+ void findCategoryIndicesFromUsage(const UnownedStringSlice& usageSlice, List<Index>& outCategories) const;
+ /// Get all the option names associated with a category index
+ void getCategoryOptionNames(Index categoryIndex, List<UnownedStringSlice>& outNames) const;
+
+ /// Set up a lookup kind from a category index
+ static LookupKind makeLookupKind(Index categoryIndex) { return LookupKind(categoryIndex); }
+
+ /// Returns true, if all values from [start, end) are found for the kind
+ bool hasContiguousUserValueRange(LookupKind kind, UserValue start, UserValue nonInclEnd) const;
+
+ /// Returns the number of options in the range
+ Count getOptionCountInRange(Index categoryIndex, UserValue start, UserValue nonInclEnd) const;
+ Count getOptionCountInRange(LookupKind kind, UserValue start, UserValue nonInclEnd) const;
+
+
+ /// Ctor
+ CommandOptions() :
+ m_pool(StringSlicePool::Style::Default),
+ m_arena(1024 * 2)
+ {
+ }
+
+ /// 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);
+ SlangResult _addName(LookupKind kind, const UnownedStringSlice& name, Index targetIndex);
+
+ SlangResult _addUserValue(LookupKind kind, UserValue userValue, Index targetIndex);
+
+ Index _addOption(const UnownedStringSlice& name, const Option& inOption);
+ Index _addOption(const UnownedStringSlice* names, Count namesCount, const Option& option);
+
+ Index _addValue(const UnownedStringSlice& name, const Option& inOption);
+
+ 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
+ };
+
+ struct UserValueKey
+ {
+ typedef UserValueKey ThisType;
+
+ SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return kind == rhs.kind && userValue == rhs.userValue; }
+ SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+ HashCode getHashCode() const { return combineHash(Slang::getHashCode(kind), Slang::getHashCode(userValue)); }
+
+ LookupKind kind; ///< The kind of lookup
+ UserValue userValue; ///< The user value
+ };
+
+ Index m_currentCategoryIndex = -1;
+
+ List<Category> m_categories;
+
+ // Holds a bit for all valid prefix sizes. Max prefix size is therefore 32 chars
+ uint32_t m_prefixSizes = 0;
+
+ List<Option> m_options; ///< All of the entries describing each of the options
+ StringSlicePool m_pool; ///< Only holds options, and handle therefore matches up to m_entries
+ Dictionary<NameKey, Index> m_nameMap; ///< Maps a name to an option index
+ Dictionary<UserValueKey, Index> m_userValueMap; ///< Maps a user value (for a kind) to an index
+
+ 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-name-value.cpp b/source/core/slang-name-value.cpp
new file mode 100644
index 000000000..5418043f5
--- /dev/null
+++ b/source/core/slang-name-value.cpp
@@ -0,0 +1,163 @@
+// slang-name-value.cpp
+
+#include "slang-name-value.h"
+
+#include "slang-string-util.h"
+#include "slang-char-util.h"
+
+namespace Slang {
+
+/* static */ValueInt NameValueUtil::findValue(const ConstArrayView<NameValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue)
+{
+ for (const auto& opt : opts)
+ {
+ if (UnownedStringSlice(opt.name) == slice)
+ {
+ return opt.value;
+ }
+ }
+ return defaultValue;
+}
+
+/* static */ValueInt NameValueUtil::findValue(const ConstArrayView<NamesValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue)
+{
+ for (const auto& opt : opts)
+ {
+ UnownedStringSlice names(opt.names);
+
+ if (StringUtil::indexOfInSplit(names, ',', slice) >= 0)
+ {
+ return opt.value;
+ }
+ }
+ return defaultValue;
+}
+
+/* static */ValueInt NameValueUtil::findValue(const ConstArrayView<NamesDescriptionValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue)
+{
+ for (const auto& opt : opts)
+ {
+ UnownedStringSlice names(opt.names);
+ if (StringUtil::indexOfInSplit(names, ',', slice) >= 0)
+ {
+ return opt.value;
+ }
+ }
+ return defaultValue;
+}
+
+/* static */ UnownedStringSlice NameValueUtil::findName(const ConstArrayView<NameValue>& opts, ValueInt value, const UnownedStringSlice& defaultName)
+{
+ for (const auto& opt : opts)
+ {
+ if (opt.value == value)
+ {
+ return UnownedStringSlice(opt.name);
+ }
+ }
+ return defaultName;
+}
+
+/* static */ UnownedStringSlice NameValueUtil::findName(const ConstArrayView<NamesValue>& opts, ValueInt value, const UnownedStringSlice& defaultName)
+{
+ for (const auto& opt : opts)
+ {
+ if (opt.value == value)
+ {
+ // Get the first name
+ return StringUtil::getAtInSplit(UnownedStringSlice(opt.names), ',', 0);
+ }
+ }
+
+ return defaultName;
+}
+
+/* static */ UnownedStringSlice NameValueUtil::findName(const ConstArrayView<NamesDescriptionValue>& opts, ValueInt value, const UnownedStringSlice& defaultName)
+{
+ for (const auto& opt : opts)
+ {
+ if (opt.value == value)
+ {
+ // Get the first name
+ return StringUtil::getAtInSplit(UnownedStringSlice(opt.names), ',', 0);
+ }
+ }
+
+ return defaultName;
+}
+
+
+/* static */UnownedStringSlice NameValueUtil::findDescription(const ConstArrayView<NamesDescriptionValue>& opts, ValueInt value, const UnownedStringSlice& defaultDescription)
+{
+ for (const auto& opt : opts)
+ {
+ if (opt.value == value && opt.description)
+ {
+ return UnownedStringSlice(opt.description);
+ }
+ }
+
+ return defaultDescription;
+}
+
+/* static */ void NameValueUtil::appendNames(NameKind kind, const ConstArrayView<NameValue>& opts, List<UnownedStringSlice>& out)
+{
+ SLANG_UNUSED(kind);
+ for (auto& opt : opts)
+ {
+ out.add(UnownedStringSlice(opt.name));
+ }
+}
+
+static void _appendNames(NameValueUtil::NameKind kind, const char* names, List<UnownedStringSlice>& out)
+{
+ if (kind == NameValueUtil::NameKind::All)
+ {
+ StringUtil::appendSplit(UnownedStringSlice(names), ',', out);
+ }
+ else
+ {
+ out.add(StringUtil::getAtInSplit(UnownedStringSlice(names), ',', 0));
+ }
+}
+
+/* static */ void NameValueUtil::appendNames(NameKind kind, const ConstArrayView<NamesValue>& opts, List<UnownedStringSlice>& out)
+{
+ for (auto& opt : opts)
+ {
+ _appendNames(kind, opt.names, out);
+ }
+}
+
+/* static */ void NameValueUtil::appendNames(NameKind kind, const ConstArrayView<NamesDescriptionValue>& opts, List<UnownedStringSlice>& out)
+{
+ for (auto& opt : opts)
+ {
+ _appendNames(kind, opt.names, out);
+ }
+}
+
+/* static */ List<UnownedStringSlice> NameValueUtil::getNames(NameKind kind, const ConstArrayView<NameValue>& opts)
+{
+ List<UnownedStringSlice> names;
+ appendNames(kind, opts, names);
+ return names;
+}
+
+/* static */ List<UnownedStringSlice> NameValueUtil::getNames(NameKind kind, const ConstArrayView<NamesValue>& opts)
+{
+ List<UnownedStringSlice> names;
+ appendNames(kind, opts, names);
+ return names;
+}
+
+/* static */ List<UnownedStringSlice> NameValueUtil::getNames(NameKind kind, const ConstArrayView<NamesDescriptionValue>& opts)
+{
+ List<UnownedStringSlice> names;
+ appendNames(kind, opts, names);
+ return names;
+}
+
+} // namespace Slang
+
+
diff --git a/source/core/slang-name-value.h b/source/core/slang-name-value.h
new file mode 100644
index 000000000..a50e149a8
--- /dev/null
+++ b/source/core/slang-name-value.h
@@ -0,0 +1,76 @@
+#ifndef SLANG_CORE_NAME_VALUE_H
+#define SLANG_CORE_NAME_VALUE_H
+
+#include "slang-basic.h"
+
+#include "slang-array-view.h"
+
+namespace Slang
+{
+
+// When associating values with names we use this type
+typedef int32_t ValueInt;
+
+struct NameValue
+{
+ ValueInt value;
+ const char* name;
+};
+
+struct NamesValue
+{
+ ValueInt value;
+ const char* names; ///< Can hold comma delimited list of names
+
+};
+
+struct NamesDescriptionValue
+{
+ ValueInt value;
+ const char* names; ///< Can hold comma delimited list of names
+ const char* description; ///< Optional description, can hold null ptr or empty string if not defined
+};
+
+struct NameValueUtil
+{
+ enum class NameKind
+ {
+ First, ///< Only the first name if there is more than one
+ All, ///< All valid associated names
+ };
+
+ /// Use the default type to infer the actual type desired
+ template <typename T>
+ static T findValue(const ConstArrayView<NameValue>& opts, const UnownedStringSlice& slice, T defaultValue) { return (T)findValue(opts, slice, ValueInt(defaultValue)); }
+ template <typename T>
+ static T findValue(const ConstArrayView<NamesValue>& opts, const UnownedStringSlice& slice, T defaultValue) { return (T)findValue(opts, slice, ValueInt(defaultValue)); }
+ template <typename T>
+ static T findValue(const ConstArrayView<NamesDescriptionValue>& opts, const UnownedStringSlice& slice, T defaultValue) { return (T)findValue(opts, slice, ValueInt(defaultValue)); }
+
+ /// Given a slice finds the associated value. If no entry is found, defaultValue is returned
+ static ValueInt findValue(const ConstArrayView<NameValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue = -1);
+ static ValueInt findValue(const ConstArrayView<NamesValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue = -1);
+ static ValueInt findValue(const ConstArrayView<NamesDescriptionValue>& opts, const UnownedStringSlice& slice, ValueInt defaultValue = -1);
+
+ /// Given a value find the name. If there are multiple names, returns the first name
+ static UnownedStringSlice findName(const ConstArrayView<NameValue>& opts, ValueInt value, const UnownedStringSlice& defaultName = UnownedStringSlice());
+ static UnownedStringSlice findName(const ConstArrayView<NamesValue>& opts, ValueInt value, const UnownedStringSlice& defaultName = UnownedStringSlice());
+ static UnownedStringSlice findName(const ConstArrayView<NamesDescriptionValue>& opts, ValueInt value, const UnownedStringSlice& defaultName = UnownedStringSlice());
+
+ /// Append all the names from opts to out
+ static void appendNames(NameKind kind, const ConstArrayView<NameValue>& opts, List<UnownedStringSlice>& out);
+ static void appendNames(NameKind kind, const ConstArrayView<NamesValue>& opts, List<UnownedStringSlice>& out);
+ static void appendNames(NameKind kind, const ConstArrayView<NamesDescriptionValue>& opts, List<UnownedStringSlice>& out);
+
+ /// Return the list of all valid names
+ static List<UnownedStringSlice> getNames(NameKind kind, const ConstArrayView<NameValue>& opts);
+ static List<UnownedStringSlice> getNames(NameKind kind, const ConstArrayView<NamesValue>& opts);
+ static List<UnownedStringSlice> getNames(NameKind kind, const ConstArrayView<NamesDescriptionValue>& opts);
+
+ /// Get the description
+ static UnownedStringSlice findDescription(const ConstArrayView<NamesDescriptionValue>& opts, ValueInt value, const UnownedStringSlice& defaultDescription = UnownedStringSlice());
+};
+
+} // namespace Slang
+
+#endif
diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp
index 29e0dad5d..7de6846d5 100644
--- a/source/core/slang-string-util.cpp
+++ b/source/core/slang-string-util.cpp
@@ -35,10 +35,8 @@ namespace Slang {
return areAllEqual(slicesA, slicesB, equalFn);
}
-/* static */void StringUtil::split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices)
+/* static */void StringUtil::appendSplit(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices)
{
- outSlices.clear();
-
const char* start = in.begin();
const char* end = in.end();
@@ -59,17 +57,15 @@ namespace Slang {
}
}
-/* static */void StringUtil::split(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& outSlices)
+/* static */void StringUtil::appendSplit(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& outSlices)
{
const Index splitLen = splitSlice.getLength();
if (splitLen == 1)
{
- return split(in, splitSlice[0], outSlices);
+ return appendSplit(in, splitSlice[0], outSlices);
}
- outSlices.clear();
-
SLANG_ASSERT(splitLen > 0);
if (splitLen <= 0)
{
@@ -96,7 +92,7 @@ namespace Slang {
cur++;
}
-
+
// Add to output
outSlices.add(UnownedStringSlice(start, cur));
@@ -105,6 +101,18 @@ namespace Slang {
}
}
+/* static */void StringUtil::split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices)
+{
+ outSlices.clear();
+ appendSplit(in, splitChar, outSlices);
+}
+
+/* static */void StringUtil::split(const UnownedStringSlice& in, const UnownedStringSlice& splitSlice, List<UnownedStringSlice>& outSlices)
+{
+ outSlices.clear();
+ appendSplit(in, splitSlice, 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 b7576da1d..2293acf8d 100644
--- a/source/core/slang-string-util.h
+++ b/source/core/slang-string-util.h
@@ -33,6 +33,13 @@ struct StringUtil
/// 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);
+ /// 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.
+ static void appendSplit(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& slicesOut);
+ /// Split in by the specified splitSlice
+ /// 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);
+
/// 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 0d396195f..172426bba 100644
--- a/source/core/slang-type-text-util.cpp
+++ b/source/core/slang-type-text-util.cpp
@@ -7,6 +7,11 @@
namespace Slang
{
+
+
+
+namespace { // anonymous
+
#define SLANG_SCALAR_TYPES(x) \
x(None, none) \
x(Void, void) \
@@ -19,28 +24,6 @@ namespace Slang
x(Float32, float) \
x(Float64, double)
-#define SLANG_PASS_THROUGH_TYPES(x) \
- x(none, NONE) \
- x(fxc, FXC) \
- x(dxc, DXC) \
- x(glslang, GLSLANG) \
- x(visualstudio, VISUAL_STUDIO) \
- x(clang, CLANG) \
- x(gcc, GCC) \
- x(genericcpp, GENERIC_C_CPP) \
- x(nvrtc, NVRTC) \
- x(llvm, LLVM)
-
-#define SLANG_DEBUG_INFO_FORMATS(x) \
- x(default-format, DEFAULT) \
- x(c7, C7) \
- x(pdb, PDB) \
- x(stabs, STABS) \
- x(coff, COFF) \
- x(dwarf, DWARF)
-
-namespace { // anonymous
-
struct ScalarTypeInfo
{
slang::TypeReflection::ScalarType type;
@@ -54,105 +37,160 @@ static const ScalarTypeInfo s_scalarTypeInfos[] =
SLANG_SCALAR_TYPES(SLANG_SCALAR_TYPE_INFO)
};
-struct CompileTargetInfo
-{
- SlangCompileTarget target; ///< The target
- const char* extensions; ///< Comma delimited list of extensions associated with the target
- const char* names; ///< Comma delimited list of names associated with the target. NOTE! First name is taken as the normal display name.
-};
-
// Make sure to keep this table in sync with that in slang/slang-options.cpp getHelpText
-static const CompileTargetInfo s_compileTargetInfos[] =
+static const TypeTextUtil::CompileTargetInfo s_compileTargetInfos[] =
{
{ SLANG_TARGET_UNKNOWN, "", "unknown"},
{ SLANG_TARGET_NONE, "", "none"},
- { SLANG_HLSL, "hlsl,fx", "hlsl"},
- { SLANG_DXBC, "dxbc", "dxbc"},
- { SLANG_DXBC_ASM, "dxbc-asm", "dxbc-asm,dxbc-assembly" },
- { SLANG_DXIL, "dxil", "dxil" },
- { SLANG_DXIL_ASM, "dxil-asm", "dxil-asm,dxil-assembly" },
- { SLANG_GLSL, "glsl,vert,frag,geom,tesc,tese,comp", "glsl" },
- { SLANG_GLSL_VULKAN, "", "glsl-vulkan" },
- { SLANG_GLSL_VULKAN_ONE_DESC, "", "glsl-vulkan-one-desc" },
- { SLANG_SPIRV, "spv", "spirv"},
- { SLANG_SPIRV_ASM, "spv-asm", "spirv-asm,spirv-assembly" },
- { SLANG_C_SOURCE, "c", "c" },
- { SLANG_CPP_SOURCE, "cpp,c++,cxx", "cpp,c++,cxx" },
- { SLANG_CPP_PYTORCH_BINDING, "cpp,c++,cxx", "torch,torch-binding,torch-cpp,torch-cpp-binding" },
- { SLANG_HOST_CPP_SOURCE, "cpp,c++,cxx", "host-cpp,host-c++,host-cxx"},
- { SLANG_HOST_EXECUTABLE,"exe", "exe,executable" },
- { SLANG_SHADER_SHARED_LIBRARY, "dll,so", "sharedlib,sharedlibrary,dll" },
- { SLANG_CUDA_SOURCE, "cu", "cuda,cu" },
- { SLANG_PTX, "ptx", "ptx" },
- { SLANG_CUDA_OBJECT_CODE, "obj,o", "cuobj,cubin" },
- { SLANG_SHADER_HOST_CALLABLE, "", "host-callable,callable" },
- { SLANG_OBJECT_CODE, "obj,o", "object-code" },
- { SLANG_HOST_HOST_CALLABLE, "", "host-host-callable" },
+ { SLANG_HLSL, "hlsl,fx", "hlsl", "HLSL source code"},
+ { SLANG_DXBC, "dxbc", "dxbc", "DirectX shader bytecode binary"},
+ { SLANG_DXBC_ASM, "dxbc-asm", "dxbc-asm,dxbc-assembly", "DirectX shader bytecode assembly" },
+ { SLANG_DXIL, "dxil", "dxil", "DirectX Intermediate Language binary" },
+ { SLANG_DXIL_ASM, "dxil-asm", "dxil-asm,dxil-assembly", "DirectX Intermediate Language assembly"},
+ { SLANG_GLSL, "glsl,vert,frag,geom,tesc,tese,comp", "glsl", "GLSL source code" },
+ { SLANG_GLSL_VULKAN, "", "glsl-vulkan", "GLSL Vulkan source code" },
+ { SLANG_GLSL_VULKAN_ONE_DESC, "", "glsl-vulkan-one-desc", "GLSL Vulkan source code" },
+ { SLANG_SPIRV, "spv", "spirv", "SPIR-V binary"},
+ { SLANG_SPIRV_ASM, "spv-asm", "spirv-asm,spirv-assembly", "SPIR-V assembly" },
+ { SLANG_C_SOURCE, "c", "c", "C source code" },
+ { SLANG_CPP_SOURCE, "cpp,c++,cxx", "cpp,c++,cxx", "C++ source code" },
+ { SLANG_CPP_PYTORCH_BINDING, "cpp,c++,cxx", "torch,torch-binding,torch-cpp,torch-cpp-binding", "C++ for pytorch binding" },
+ { SLANG_HOST_CPP_SOURCE, "cpp,c++,cxx", "host-cpp,host-c++,host-cxx", "C++ source for host execution"},
+ { SLANG_HOST_EXECUTABLE,"exe", "exe,executable", "Executable binary" },
+ { SLANG_SHADER_SHARED_LIBRARY, "dll,so", "sharedlib,sharedlibrary,dll", "Shared library/Dll" },
+ { SLANG_CUDA_SOURCE, "cu", "cuda,cu", "CUDA source code" },
+ { SLANG_PTX, "ptx", "ptx", "PTX assembly" },
+ { SLANG_CUDA_OBJECT_CODE, "obj,o", "cuobj,cubin", "CUDA binary" },
+ { SLANG_SHADER_HOST_CALLABLE, "", "host-callable,callable", "Host callable" },
+ { SLANG_OBJECT_CODE, "obj,o", "object-code", "Object code" },
+ { SLANG_HOST_HOST_CALLABLE, "", "host-host-callable", "Host callable for host execution" },
+};
+
+static const NamesValue 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" },
+};
+
+static const NamesDescriptionValue s_compilerInfos[] =
+{
+ { SLANG_PASS_THROUGH_NONE, "none", "Unknown" },
+ { SLANG_PASS_THROUGH_FXC, "fxc", "FXC HLSL compiler" },
+ { SLANG_PASS_THROUGH_DXC, "dxc", "DXC HLSL compiler" },
+ { SLANG_PASS_THROUGH_GLSLANG, "glslang", "GLSLANG GLSL compiler" },
+ { SLANG_PASS_THROUGH_VISUAL_STUDIO, "visualstudio,vs", "Visual Studio C/C++ compiler" },
+ { SLANG_PASS_THROUGH_CLANG, "clang", "Clang C/C++ compiler" },
+ { SLANG_PASS_THROUGH_GCC, "gcc", "GCC C/C++ compiler" },
+ { SLANG_PASS_THROUGH_GENERIC_C_CPP, "genericcpp,c,cpp", "A generic C++ compiler (can be any one of visual studio, clang or gcc depending on system and availability)" },
+ { SLANG_PASS_THROUGH_NVRTC, "nvrtc", "NVRTC CUDA compiler" },
+ { SLANG_PASS_THROUGH_LLVM, "llvm", "LLVM/Clang `slang-llvm`" },
+};
+static const NameValue 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"},
};
-struct ArchiveTypeInfo
+
+static const NamesDescriptionValue s_debugInfoFormatInfos[] =
{
- SlangArchiveType type;
- UnownedStringSlice text;
+ { SLANG_DEBUG_INFO_FORMAT_DEFAULT, "default-format", "Use the default debugging format for the target" },
+ { SLANG_DEBUG_INFO_FORMAT_C7, "c7", "CodeView C7 format (typically means debugging infomation is embedded in the binary)" },
+ { SLANG_DEBUG_INFO_FORMAT_PDB, "pdb", "Program database" },
+ { SLANG_DEBUG_INFO_FORMAT_STABS, "stabs", "STABS debug format" },
+ { SLANG_DEBUG_INFO_FORMAT_COFF, "coff", "COFF debug format" },
+ { SLANG_DEBUG_INFO_FORMAT_DWARF, "dwarf", "DWARF debug format"},
};
-static const ArchiveTypeInfo s_archiveTypeInfos[] =
+static const NamesDescriptionValue s_lineDirectiveInfos[] =
{
- { SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, UnownedStringSlice::fromLiteral("riff-deflate")},
- { SLANG_ARCHIVE_TYPE_RIFF_LZ4, UnownedStringSlice::fromLiteral("riff-lz4")},
- { SLANG_ARCHIVE_TYPE_ZIP, UnownedStringSlice::fromLiteral("zip")},
- { SLANG_ARCHIVE_TYPE_RIFF, UnownedStringSlice::fromLiteral("riff")},
+ { SLANG_LINE_DIRECTIVE_MODE_NONE, "none", "Don't emit `#line` directives at all"},
+ { SLANG_LINE_DIRECTIVE_MODE_SOURCE_MAP, "source-map", "Use source map to track line associations (doen't emit #line)"},
+ { SLANG_LINE_DIRECTIVE_MODE_DEFAULT, "default", "Default behavior"},
+ { SLANG_LINE_DIRECTIVE_MODE_STANDARD, "standard", "Emit standard C-style `#line` directives." },
+ { SLANG_LINE_DIRECTIVE_MODE_GLSL, "glsl", "Emit GLSL-style directives with file *number* instead of name." },
+};
+
+static const NamesDescriptionValue s_floatingPointModes[] =
+{
+ { SLANG_FLOATING_POINT_MODE_PRECISE, "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."},
+ { SLANG_FLOATING_POINT_MODE_FAST,"fast",
+ "Allow optimizations that may change results of floating-point "
+ "computations. Prefer the fastest version of special functions supported "
+ "by the target."},
+ { SLANG_FLOATING_POINT_MODE_DEFAULT, "default",
+ "Default floating point mode" }
};
} // anonymous
-/* static */SlangArchiveType TypeTextUtil::findArchiveType(const UnownedStringSlice& slice)
+/* static */ConstArrayView<TypeTextUtil::CompileTargetInfo> TypeTextUtil::getCompileTargetInfos()
{
- for (const auto& entry : s_archiveTypeInfos)
- {
- if (slice == entry.text)
- {
- return entry.type;
- }
- }
- return SLANG_ARCHIVE_TYPE_UNDEFINED;
+ return makeConstArrayView(s_compileTargetInfos);
}
-struct DebugInfoFormatTable
+/* static */ConstArrayView<NamesValue> TypeTextUtil::getLanguageInfos()
{
- UnownedStringSlice entries[SLANG_DEBUG_INFO_FORMAT_COUNT_OF];
+ return makeConstArrayView(s_languageInfos);
+}
- static DebugInfoFormatTable _makeTable()
- {
- DebugInfoFormatTable dst;
-#define SLANG_DEBUG_INFO_FORMAT_ENTRY(name, value) \
- dst.entries[SLANG_DEBUG_INFO_FORMAT_##value] = toSlice(#name);
- SLANG_DEBUG_INFO_FORMATS(SLANG_DEBUG_INFO_FORMAT_ENTRY)
- return dst;
- }
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getCompilerInfos()
+{
+ return makeConstArrayView(s_compilerInfos);
+}
- static Index findIndex(const UnownedStringSlice slice) { return makeConstArrayView(table.entries).indexOf(slice); }
- static UnownedStringSlice getSlice(SlangDebugInfoFormat format) { return table.entries[Index(format)]; }
+/* static */ConstArrayView<NameValue> TypeTextUtil::getArchiveTypeInfos()
+{
+ return makeConstArrayView(s_archiveTypeInfos);
+}
- static const DebugInfoFormatTable table;
-};
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getDebugInfoFormatInfos()
+{
+ return makeConstArrayView(s_debugInfoFormatInfos);
+}
-/* static */const DebugInfoFormatTable DebugInfoFormatTable::table = DebugInfoFormatTable::_makeTable();
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getLineDirectiveInfos()
+{
+ return makeConstArrayView(s_lineDirectiveInfos);
+}
+
+/* static */ConstArrayView<NamesDescriptionValue> TypeTextUtil::getFloatingPointModeInfos()
+{
+ return makeConstArrayView(s_floatingPointModes);
+}
+
+/* static */SlangArchiveType TypeTextUtil::findArchiveType(const UnownedStringSlice& slice)
+{
+ return NameValueUtil::findValue(getArchiveTypeInfos(), slice, SLANG_ARCHIVE_TYPE_UNDEFINED);
+}
/* static */SlangResult TypeTextUtil::findDebugInfoFormat(const Slang::UnownedStringSlice& text, SlangDebugInfoFormat& out)
{
- const auto index = DebugInfoFormatTable::findIndex(text);
- if (index >= 0)
+ const ValueInt value = NameValueUtil::findValue(getDebugInfoFormatInfos(), text, -1);
+ if (value >= 0)
{
- out = SlangDebugInfoFormat(index);
+ out = SlangDebugInfoFormat(value);
return SLANG_OK;
}
return SLANG_FAIL;
}
-/* static */UnownedStringSlice TypeTextUtil::getDebugInfoFormatName(SlangDebugInfoFormat format) { return DebugInfoFormatTable::getSlice(format); }
+/* static */UnownedStringSlice TypeTextUtil::getDebugInfoFormatName(SlangDebugInfoFormat format)
+{
+ return NameValueUtil::findName(getDebugInfoFormatInfos(), format, toSlice("unknown"));
+}
/* static */UnownedStringSlice TypeTextUtil::getScalarTypeName(slang::TypeReflection::ScalarType scalarType)
{
@@ -180,82 +218,20 @@ struct DebugInfoFormatTable
return slang::TypeReflection::ScalarType::None;
}
-#define SLANG_PASS_THROUGH_HUMAN_TEXT(x) \
- x(NONE, "Unknown") \
- x(VISUAL_STUDIO, "Visual Studio") \
- x(GCC, "GCC") \
- x(CLANG, "Clang") \
- x(NVRTC, "NVRTC") \
- x(FXC, "fxc") \
- x(DXC, "dxc") \
- x(GLSLANG, "glslang") \
- x(LLVM, "LLVM/Clang")
/* static */UnownedStringSlice TypeTextUtil::getPassThroughAsHumanText(SlangPassThrough type)
{
-#define SLANG_PASS_THROUGH_HUMAN_CASE(value, text) case SLANG_PASS_THROUGH_##value: return UnownedStringSlice::fromLiteral(text);
-
- switch (type)
- {
- default: [[fallthrough]]; /* fall-through to none */
- SLANG_PASS_THROUGH_HUMAN_TEXT(SLANG_PASS_THROUGH_HUMAN_CASE)
- }
-}
-
-/* static */SlangResult TypeTextUtil::findPassThroughFromHumanText(const UnownedStringSlice& inText, SlangPassThrough& outPassThrough)
-{
- #define SLANG_PASS_THROUGH_HUMAN_IF(value, text) if (inText == UnownedStringSlice::fromLiteral(text)) { outPassThrough = SLANG_PASS_THROUGH_##value; return SLANG_OK; } else
- SLANG_PASS_THROUGH_HUMAN_TEXT(SLANG_PASS_THROUGH_HUMAN_IF)
- return SLANG_FAIL;
+ return NameValueUtil::findName(getCompilerInfos(), type, toSlice("unknown"));
}
/* static */SlangSourceLanguage TypeTextUtil::findSourceLanguage(const UnownedStringSlice& text)
{
- if (text == "c" || text == "C")
- {
- return SLANG_SOURCE_LANGUAGE_C;
- }
- else if (text == "cpp" || text == "c++" || text == "C++" || text == "cxx")
- {
- return SLANG_SOURCE_LANGUAGE_CPP;
- }
- else if (text == "slang")
- {
- return SLANG_SOURCE_LANGUAGE_SLANG;
- }
- else if (text == "glsl")
- {
- return SLANG_SOURCE_LANGUAGE_GLSL;
- }
- else if (text == "hlsl")
- {
- return SLANG_SOURCE_LANGUAGE_HLSL;
- }
- else if (text == "cu" || text == "cuda")
- {
- return SLANG_SOURCE_LANGUAGE_CUDA;
- }
- return SLANG_SOURCE_LANGUAGE_UNKNOWN;
+ return NameValueUtil::findValue(getLanguageInfos(), text, SLANG_SOURCE_LANGUAGE_UNKNOWN);
}
/* static */SlangPassThrough TypeTextUtil::findPassThrough(const UnownedStringSlice& slice)
{
-#define SLANG_PASS_THROUGH_NAME_TO_TYPE(x, y) \
- if (slice == UnownedStringSlice::fromLiteral(#x)) return SLANG_PASS_THROUGH_##y;
-
- SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_NAME_TO_TYPE)
-
- // Other options
- if (slice == "c" || slice == "cpp")
- {
- return SLANG_PASS_THROUGH_GENERIC_C_CPP;
- }
- else if (slice == "vs")
- {
- return SLANG_PASS_THROUGH_VISUAL_STUDIO;
- }
-
- return SLANG_PASS_THROUGH_NONE;
+ return NameValueUtil::findValue(getCompilerInfos(), slice, SLANG_PASS_THROUGH_NONE);
}
/* static */SlangResult TypeTextUtil::findPassThrough(const UnownedStringSlice& slice, SlangPassThrough& outPassThrough)
@@ -271,15 +247,7 @@ struct DebugInfoFormatTable
/* static */UnownedStringSlice TypeTextUtil::getPassThroughName(SlangPassThrough passThru)
{
-#define SLANG_PASS_THROUGH_TYPE_TO_NAME(x, y) \
- case SLANG_PASS_THROUGH_##y: return UnownedStringSlice::fromLiteral(#x);
-
- switch (passThru)
- {
- SLANG_PASS_THROUGH_TYPES(SLANG_PASS_THROUGH_TYPE_TO_NAME)
- default: break;
- }
- return UnownedStringSlice::fromLiteral("unknown");
+ return NameValueUtil::findName(getCompilerInfos(), passThru, toSlice("unknown"));
}
/* static */SlangCompileTarget TypeTextUtil::findCompileTargetFromExtension(const UnownedStringSlice& slice)
diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h
index df9b83013..2249b1215 100644
--- a/source/core/slang-type-text-util.h
+++ b/source/core/slang-type-text-util.h
@@ -4,7 +4,8 @@
#include "../../slang.h"
#include "slang-string.h"
-
+#include "slang-array-view.h"
+#include "slang-name-value.h"
namespace Slang
{
@@ -12,6 +13,30 @@ namespace Slang
/// Utility class to allow conversion of types (such as enums) to and from text types
struct TypeTextUtil
{
+ struct CompileTargetInfo
+ {
+ SlangCompileTarget target; ///< The target
+ const char* extensions; ///< Comma delimited list of extensions associated with the target
+ const char* names; ///< Comma delimited list of names associated with the target. NOTE! First name is taken as the normal display name.
+ const char* description; ///< Description, can be null
+ };
+
+ /// Get the compile target infos
+ static ConstArrayView<CompileTargetInfo> getCompileTargetInfos();
+
+ /// Get the language infos
+ static ConstArrayView<NamesValue> getLanguageInfos();
+ /// Get the compiler infos
+ static ConstArrayView<NamesDescriptionValue> getCompilerInfos();
+ /// Get the archive type infos
+ static ConstArrayView<NameValue> getArchiveTypeInfos();
+ /// Get the debug format types
+ static ConstArrayView<NamesDescriptionValue> getDebugInfoFormatInfos();
+
+ static ConstArrayView<NamesDescriptionValue> getFloatingPointModeInfos();
+
+ static ConstArrayView<NamesDescriptionValue> getLineDirectiveInfos();
+
/// Get the scalar type as text.
static Slang::UnownedStringSlice getScalarTypeName(slang::TypeReflection::ScalarType scalarType);
@@ -26,9 +51,7 @@ struct TypeTextUtil
/// As human readable text
static UnownedStringSlice getPassThroughAsHumanText(SlangPassThrough type);
- /// Gets pass through from human text (as from getPassThroughAsHumanText)
- static SlangResult findPassThroughFromHumanText(const UnownedStringSlice& text, SlangPassThrough& outPassThrough);
-
+
/// Given a source language name returns a source language. Name here is distinct from extension
static SlangSourceLanguage findSourceLanguage(const UnownedStringSlice& text);
diff --git a/source/slang/slang-capability.cpp b/source/slang/slang-capability.cpp
index 770685247..28dea8d23 100644
--- a/source/slang/slang-capability.cpp
+++ b/source/slang/slang-capability.cpp
@@ -117,6 +117,15 @@ static CapabilityAtomInfo const& _getInfo(CapabilityAtom atom)
return kCapabilityAtoms[Int(atom)];
}
+void getCapabilityAtomNames(List<UnownedStringSlice>& ioNames)
+{
+ ioNames.setCount(Count(CapabilityAtom::Count));
+ for (Index i = 0; i < Count(CapabilityAtom::Count); ++i)
+ {
+ ioNames[i] = UnownedStringSlice(_getInfo(CapabilityAtom(i)).name);
+ }
+}
+
CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name)
{
// For now we are implementing a linear search over the
diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h
index 90b03ed29..55c5044ed 100644
--- a/source/slang/slang-capability.h
+++ b/source/slang/slang-capability.h
@@ -161,4 +161,8 @@ bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base);
/// Find a capability atom with the given `name`, or return CapabilityAtom::Invalid.
CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name);
+
+ /// Gets the capability names.
+void getCapabilityAtomNames(List<UnownedStringSlice>& ioNames);
+
}
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index 0aa7e48a9..4ca76c53a 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -372,13 +372,9 @@ namespace Slang
}
}
- static const struct
+ static const StageInfo kStages[] =
{
- char const* name;
- Stage stage;
- } kStages[] =
- {
- #define PROFILE_STAGE(ID, NAME, ENUM) \
+ #define PROFILE_STAGE(ID, NAME, ENUM) \
{ #NAME, Stage::ID },
#define PROFILE_STAGE_ALIAS(ID, NAME, VAL) \
@@ -387,6 +383,11 @@ namespace Slang
#include "slang-profile-defs.h"
};
+ ConstArrayView<StageInfo> getStageInfos()
+ {
+ return makeConstArrayView(kStages);
+ }
+
Stage findStageByName(String const& name)
{
for(auto entry : kStages)
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 83acbdd9d..fc46b25b6 100755
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -13,6 +13,7 @@
#include "../compiler-core/slang-command-line-args.h"
#include "../core/slang-std-writers.h"
+#include "../core/slang-command-options.h"
#include "../../slang-com-ptr.h"
@@ -3005,6 +3006,9 @@ namespace Slang
DownstreamCompilerLocatorFunc m_downstreamCompilerLocators[int(PassThroughMode::CountOf)];
Name* m_completionTokenName = nullptr; ///< The name of a completion request token.
+ /// For parsing command line options
+ CommandOptions m_commandOptions;
+
private:
void _initCodeGenTransitionMap();
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index cb441ade8..8738c71cc 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -76,8 +76,6 @@ 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( 24, Error, unknownLineDirectiveMode, "unknown '#line' directive mode '$0'")
-DIAGNOSTIC( 25, Error, unknownFloatingPointMode, "unknown floating-point mode '$0'")
DIAGNOSTIC( 26, Error, unknownOptimiziationLevel, "unknown optimization level '$0'")
DIAGNOSTIC( 27, Error, unknownDebugInfoLevel, "unknown debug info level '$0'")
@@ -104,6 +102,9 @@ DIAGNOSTIC( 50, Error, duplicateTargets, "the target '$0' has been specified
DIAGNOSTIC( 60, Error, cannotDeduceOutputFormatFromPath, "cannot infer an output format from the output path '$0'")
DIAGNOSTIC( 61, Error, cannotMatchOutputFileToTarget, "no specified '-target' option matches the output path '$0', which implies the '$1' format")
+DIAGNOSTIC( 62, Error, unknownCommandLineValue, "unknown value for option. Valid values are '$0'")
+DIAGNOSTIC( 63, Error, unknownHelpCategory, "unknown help category")
+
DIAGNOSTIC( 70, Error, cannotMatchOutputFileToEntryPoint, "the output path '$0' is not associated with any entry point; a '-o' option for a compiled kernel must follow the '-entry' option for its corresponding entry point")
DIAGNOSTIC( 80, Error, duplicateOutputPathsForEntryPointAndTarget, "multiple output paths have been specified entry point '$0' on target '$1'")
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 8817095df..6cb53496f 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -29,12 +29,494 @@
#include "../compiler-core/slang-artifact-desc-util.h"
#include "../compiler-core/slang-core-diagnostics.h"
+#include "../core/slang-string-slice-pool.h"
#include "../core/slang-char-util.h"
+#include "../core/slang-command-options.h"
+
#include <assert.h>
namespace Slang {
+namespace { // anonymous
+
+// All of the options are given an unique enum
+enum class OptionKind
+{
+ // General
+
+ MacroDefine,
+ DepFile,
+ EntryPointName,
+ Help,
+ Include,
+ Language,
+ MatrixLayoutColumn,
+ MatrixLayoutRow,
+ ModuleName,
+ Output,
+ Profile,
+ Stage,
+ Target,
+ Version,
+ WarningsAsErrors,
+ DisableWarnings,
+ EnableWarning,
+ DisableWarning,
+ DumpWarningDiagnostics,
+ InputFilesRemain,
+ EmitIr,
+
+ // Target
+
+ Capability,
+ DefaultImageFormatUnknown,
+ DisableDynamicDispatch,
+ DisableSpecialization,
+ FloatingPointMode,
+ DebugInformation,
+ LineDirectiveMode,
+ Optimization,
+ Obfuscate,
+
+ // Downstream
+
+ CompilerPath,
+ DefaultDownstreamCompiler,
+ DownstreamArgs,
+ PassThrough,
+
+ // Debugging
+
+ DumpAst,
+ DumpIntermediatePrefix,
+ DumpIntermediates,
+ DumpIr,
+ DumpIrIds,
+ DumpRepro,
+ DumpReproOnError,
+ PreprocessorOutput,
+ ExtractRepro,
+ LoadRepro,
+ LoadReproDirectory,
+ NoCodeGen,
+ OutputIncludes,
+ ReproFileSystem,
+ SerialIr,
+ SkipCodeGen,
+ ValidateIr,
+ VerbosePaths,
+ VerifyDebugSerialIr,
+
+ // Experimental
+
+ EmitSpirvDirectly,
+ FileSystem,
+ Heterogeneous,
+ NoMangle,
+
+ // Internal
+
+ ArchiveType,
+ CompileStdLib,
+ Doc,
+ IrCompression,
+ LoadStdLib,
+ ReferenceModule,
+ SaveStdLib,
+ SaveStdLibBinSource,
+ TrackLiveness,
+
+ // Depreciated
+ ParameterBlocksUseRegisterSpaces,
+
+ CountOf,
+};
+
+struct Option
+{
+ OptionKind optionKind;
+ const char* name;
+ const char* usage = nullptr;
+ const char* description = nullptr;
+};
+
+enum class ValueCategory
+{
+ Compiler,
+ Target,
+ Language,
+ FloatingPointMode,
+ ArchiveType,
+ Stage,
+ LineDirectiveMode,
+ DebugInfoFormat,
+
+ CountOf,
+};
+
+} // anonymous
+
+static void _addOptions(const ConstArrayView<Option>& options, CommandOptions& cmdOptions)
+{
+ for (auto& opt : options)
+ {
+ cmdOptions.add(opt.name, opt.usage, opt.description, CommandOptions::UserValue(opt.optionKind));
+ }
+}
+
+void initCommandOptions(CommandOptions& options)
+{
+ typedef CommandOptions::Flag::Enum Flag;
+ typedef CommandOptions::CategoryKind CategoryKind;
+ typedef CommandOptions::UserValue UserValue;
+
+ // Add all the option categories
+
+ options.addCategory(CategoryKind::Option, "General", "General options");
+ options.addCategory(CategoryKind::Option, "Target", "Target code generation options");
+ options.addCategory(CategoryKind::Option, "Downstream", "Downstream compiler options");
+ options.addCategory(CategoryKind::Option, "Debugging", "Compiler debugging/instrumentation options");
+ options.addCategory(CategoryKind::Option, "Experimental", "Experimental options (use at your own risk)");
+ options.addCategory(CategoryKind::Option, "Internal", "Internal-use options (use at your own risk)");
+ options.addCategory(CategoryKind::Option, "Depreciated", "Deprecated options (allowed but ignored; may be removed in future)");
+
+ // Do the easy ones
+ {
+ options.addCategory(CategoryKind::Value, "compiler", "Downstream Compilers (aka Pass through)", UserValue(ValueCategory::Compiler));
+ options.addValues(TypeTextUtil::getCompilerInfos());
+
+ options.addCategory(CategoryKind::Value, "language", "Language", UserValue(ValueCategory::Language));
+ options.addValues(TypeTextUtil::getLanguageInfos());
+
+ options.addCategory(CategoryKind::Value, "archive-type", "Archive Type", UserValue(ValueCategory::ArchiveType));
+ options.addValues(TypeTextUtil::getArchiveTypeInfos());
+
+ options.addCategory(CategoryKind::Value, "line-directive-mode", "Line Directive Mode", UserValue(ValueCategory::LineDirectiveMode));
+ options.addValues(TypeTextUtil::getLineDirectiveInfos());
+
+ options.addCategory(CategoryKind::Value, "debug-info-format", "Debug Info Format", UserValue(ValueCategory::DebugInfoFormat));
+ options.addValues(TypeTextUtil::getDebugInfoFormatInfos());
+
+ options.addCategory(CategoryKind::Value, "fp-mode", "Floating Point Mode", UserValue(ValueCategory::FloatingPointMode));
+ options.addValues(TypeTextUtil::getFloatingPointModeInfos());
+ }
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! target !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ {
+ options.addCategory(CategoryKind::Value, "target", "Target", UserValue(ValueCategory::Target));
+ for (auto opt : TypeTextUtil::getCompileTargetInfos())
+ {
+ options.addValue(opt.names, opt.description, UserValue(opt.target));
+ }
+ }
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stage !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ {
+ options.addCategory(CategoryKind::Value, "stage", "Stage", UserValue(ValueCategory::Stage));
+ List<NameValue> opts;
+ for (auto& info: getStageInfos())
+ {
+ opts.add({ValueInt(info.stage), info.name });
+ }
+ options.addValuesWithAliases(opts.getArrayView());
+ }
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! capabilities !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ {
+ options.addCategory(CategoryKind::Value, "capability",
+ "A capability describes an optional feature that a target may or "
+ "may not support. When a -capability is specified, the compiler "
+ "may assume that the target supports that capability, and generate "
+ "code accordingly.");
+
+ List<UnownedStringSlice> names;
+ getCapabilityAtomNames(names);
+
+ // We'll just add to keep the list more simple...
+ options.addValue("spirv_1_{ 0,1,2,3,4,5 }", "minimum supported SPIR - V version");
+
+ for (auto name : names)
+ {
+ if (name.startsWith("__") ||
+ name.startsWith("spirv_1_"))
+ {
+ continue;
+ }
+ else if (name.startsWith("GL_"))
+ {
+ // We'll assume it is an extension..
+ StringBuilder buf;
+ buf << "enables the " << name << " extension";
+ options.addValue(name, buf.getUnownedSlice());
+ }
+ else
+ {
+ options.addValue(name);
+ }
+ }
+ }
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! extension !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ {
+ options.addCategory(CategoryKind::Value, "file-extension",
+ "A <language>, <format>, and/or <stage> may be inferred from the "
+ "extension of an input or -o path");
+
+ // TODO(JS): It's concevable that these are enumerated via some other system
+ // rather than just being listed here
+
+ const CommandOptions::ValuePair pairs[] =
+ {
+ {"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"},
+ };
+ options.addValues(pairs, SLANG_COUNT_OF(pairs));
+ }
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! General !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ options.setCategory("General");
+
+ const Option generalOpts[] =
+ {
+ { OptionKind::MacroDefine, "-D?...", "-D<name>[=<value>], -D <name>[=<value>]", "Insert a preprocessor macro." },
+ { 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"
+ "Multiple -entry options may be used in a single invocation. "
+ "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::Include, "-I?...", "-I<path>, -I <path>",
+ "Add a path to be used in resolving '#include' "
+ "and 'import' operations."},
+ { OptionKind::Language, "-lang", "-lang <language>", "Set the language for the following input files."},
+ { OptionKind::MatrixLayoutColumn, "-matrix-layout-column-major", nullptr, "Set the default matrix layout to column-major."},
+ { OptionKind::MatrixLayoutRow,"-matrix-layout-row-major", nullptr, "Set the default matrix layout to row-major."},
+ { OptionKind::ModuleName, "-module-name", "-module-name <name>",
+ "Set the module name to use when compiling multiple .slang source files into a single module."},
+ { OptionKind::Output, "-o", "-o <path>",
+ "Specify a path where generated output should be written.\n"
+ "If no -target or -stage is specified, one may be inferred "
+ "from file extension (see <file-extension>). "
+ "If multiple -target options and a single -entry are present, each -o "
+ "associates with the first -target to its left. "
+ "Otherwise, if multiple -entry options are present, each -o associates "
+ "with the first -entry to its left, and with the -target that matches "
+ "the one inferred from <path>."},
+ { 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"
+ "Additional profiles that include -stage information:\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."},
+ { OptionKind::Stage, "-stage", "-stage <stage>",
+ "Specify the stage of an entry-point function.\n"
+ "When multiple -entry options are present, each -stage associated with "
+ "the first -entry to its left.\n"
+ "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::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"},
+ { OptionKind::DisableWarnings, "-warnings-disable", "-warnings-disable <id>[,<id>...]", "Disable specific warning ids."},
+ { OptionKind::EnableWarning, "-W...", "-W<id>", "Enable a warning with the specified id."},
+ { OptionKind::DisableWarning, "-Wno-...", "-Wno-<id>", "Disable warning with <id>"},
+ { OptionKind::DumpWarningDiagnostics, "-dump-warning-diagnostics", nullptr, "Dump to output list of warning diagnostic numeric and name ids." },
+ { OptionKind::InputFilesRemain, "--", nullptr, "Treat the rest of the command line as input files."}
+ };
+
+ _addOptions(makeConstArrayView(generalOpts), options);
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Target !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ options.setCategory("Target");
+
+ const Option targetOpts[] =
+ {
+ { OptionKind::Capability, "-capability", "-capability <capability>[+<capability>...]",
+ "Add optional capabilities to a code generation target. See Capabilities below."},
+ { OptionKind::DefaultImageFormatUnknown, "-default-image-format-unknown", nullptr,
+ "Set the format of R/W images with unspecified format to 'unknown'. Otherwise try to guess the format."},
+ { OptionKind::DisableDynamicDispatch, "-disable-dynamic-dispatch", nullptr, "Disables generating dynamic dispatch code." },
+ { 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>",
+ "Include debug information in the generated code, where possible.\n"
+ "N 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>" },
+ { 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::Obfuscate, "-obfuscate", nullptr, "Remove all source file information from outputs." },
+ };
+
+ _addOptions(makeConstArrayView(targetOpts), options);
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Downstream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ options.setCategory("Downstream");
+
+ {
+ auto namesList = NameValueUtil::getNames(NameValueUtil::NameKind::First, TypeTextUtil::getCompilerInfos());
+ StringBuilder names;
+ for (auto name : namesList)
+ {
+ names << "-" << name << "-path,";
+ }
+ // remove last ,
+ names.reduceLength(names.getLength() - 1);
+
+ options.add(names.getBuffer(), "-<compiler>-path <path>",
+ "Specify path to a downstream <compiler> "
+ "executable or library.\n",
+ UserValue(OptionKind::CompilerPath));
+ }
+
+ const Option downstreamOpts[] =
+ {
+ { OptionKind::DefaultDownstreamCompiler, "-default-downstream-compiler", "-default-downstream-compiler <language> <compiler>",
+ "Set a default compiler for the given language. See -lang for the list of languages." },
+ { OptionKind::DownstreamArgs, "-X...", "-X<compiler> <option> -X<compiler>... <options> -X.",
+ "Pass arguments to downstream <compiler>. Just -X<compiler> passes just the next argument "
+ "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>." },
+ };
+
+ _addOptions(makeConstArrayView(downstreamOpts), options);
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Debugging !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ options.setCategory("Debugging");
+
+ const Option debuggingOpts[] =
+ {
+ { OptionKind::DumpAst, "-dump-ast", nullptr, "Dump the AST to a .slang-ast file next to the input." },
+ { OptionKind::DumpIntermediatePrefix, "-dump-intermediate-prefix", "-dump-intermediate-prefix <prefix>",
+ "File name prefix for -dump-intermediates outputs, default is 'slang-dump-'"},
+ { OptionKind::DumpIntermediates, "-dump-intermediates", nullptr, "Dump intermediate outputs for debugging." },
+ { OptionKind::DumpIr, "-dump-ir", nullptr, "Dump the IR for debugging." },
+ { OptionKind::DumpIrIds, "-dump-ir-ids", nullptr, "Dump the IDs with -dump-ir (debug builds only)" },
+ { OptionKind::DumpRepro, "-dump-repro", nullptr, "Dump a `.slang-repro` file that can be used to reproduce "
+ "a compilation on another machine.\n"},
+ { OptionKind::DumpReproOnError, "-dump-repro-on-error", nullptr, "Dump `.slang-repro` file on any compilation error." },
+ { OptionKind::PreprocessorOutput, "-E,-output-preprocessor", nullptr, "Output the preprocessing result and exit." },
+ { OptionKind::ExtractRepro, "-extract-repro", "-extract-repro <name>", "Extract the repro files into a folder." },
+ { OptionKind::LoadReproDirectory, "-load-repro-directory", "-load-repro-directory <path>", "Use repro along specified path" },
+ { OptionKind::LoadRepro, "-load-repro", "-load-repro <name>", "Load repro"},
+ { OptionKind::NoCodeGen, "-no-codegen", nullptr, "Skip the code generation step, just check the code and generate layout." },
+ { OptionKind::OutputIncludes, "-output-includes", nullptr, "Print the hierarchy of the processed source files." },
+ { OptionKind::ReproFileSystem, "-repro-file-system", "-repro-file-system <name>", "Use a repro as a file system" },
+ { OptionKind::SerialIr, "-serial-ir", nullptr, "Serialize the IR between front-end and back-end." },
+ { OptionKind::SkipCodeGen, "-skip-codegen", nullptr, "Skip the code generation phase." },
+ { OptionKind::ValidateIr, "-validate-ir", nullptr, "Validate the IR between the phases." },
+ { OptionKind::VerbosePaths, "-verbose-paths", nullptr, "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." },
+ { OptionKind::VerifyDebugSerialIr, "-verify-debug-serial-ir", nullptr, "Verify IR in the front-end." }
+ };
+ _addOptions(makeConstArrayView(debuggingOpts), options);
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Experimental !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ options.setCategory("Experimental");
+
+ const Option experimentalOpts[] =
+ {
+ { 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::Heterogeneous, "-heterogeneous", nullptr, "Output heterogeneity-related code." },
+ { OptionKind::NoMangle, "-no-mangle", nullptr, "Do as little mangling of names as possible." }
+ };
+ _addOptions(makeConstArrayView(experimentalOpts), options);
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Internal !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ options.setCategory("Internal");
+
+ const Option internalOpts[] =
+ {
+ { OptionKind::ArchiveType, "-archive-type", "-archive-type <archive-type>", "Set the archive type for -save-stdlib. Default is zip." },
+ { OptionKind::CompileStdLib, "-compile-stdlib", nullptr,
+ "Compile the StdLib from embedded sources. "
+ "Will return a failure if there is already a StdLib available."},
+ { OptionKind::Doc, "-doc", nullptr, "Write documentation for -compile-stdlib" },
+ { OptionKind::IrCompression,"-ir-compression", "-ir-compression <type>",
+ "Set compression for IR and AST outputs.\n"
+ "Accepted compression types: none, lite"},
+ { OptionKind::LoadStdLib, "-load-stdlib", "-load-stdlib <filename>", "Load the StdLib from file." },
+ { OptionKind::ReferenceModule, "-r", "-r <name>", "reference module <name>" },
+ { OptionKind::SaveStdLib, "-save-stdlib", "-save-stdlib <filename>", "Save the StdLib modules to an archive file." },
+ { OptionKind::SaveStdLibBinSource, "-save-stdlib-bin-source","-save-stdlib-bin-source <filename>", "Same as -save-stdlib but output "
+ "the data as a C array.\n"},
+ { OptionKind::TrackLiveness, "-track-liveness", nullptr, "Enable liveness tracking. Places SLANG_LIVE_START, and SLANG_LIVE_END in output source to indicate value liveness." },
+ };
+ _addOptions(makeConstArrayView(internalOpts), options);
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Depreciated !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ options.setCategory("Depreciated");
+
+ const Option depreciatedOpts[] =
+ {
+ { OptionKind::ParameterBlocksUseRegisterSpaces, "-parameter-blocks-use-register-spaces", nullptr, "Parameter blocks will use register spaces" },
+ };
+ _addOptions(makeConstArrayView(depreciatedOpts), options);
+
+ // We can now check that the whole range is available. If this fails it means there
+ // is an enum in the list that hasn't been setup as an option!
+ SLANG_ASSERT(options.hasContiguousUserValueRange(CommandOptions::LookupKind::Option, UserValue(0), UserValue(OptionKind::CountOf)));
+ SLANG_ASSERT(options.hasContiguousUserValueRange(CommandOptions::LookupKind::Category, UserValue(0), UserValue(ValueCategory::CountOf)));
+}
+
SlangResult _addLibraryReference(EndToEndCompileRequest* req, IArtifact* artifact);
struct OptionsParser
@@ -516,231 +998,6 @@ struct OptionsParser
return SLANG_OK;
}
- static char const* getHelpText()
- {
-#ifdef _WIN32
-#define EXECUTABLE_EXTENSION ".exe"
-#else
-#define EXECUTABLE_EXTENSION ""
-#endif
-
- return
- "Usage: slangc" EXECUTABLE_EXTENSION " [options...] [--] <input files>\n"
- "\n"
- "General options:\n"
- "\n"
- " -D<name>[=<value>], -D <name>[=<value>]: Insert a preprocessor macro.\n"
- " -depfile <path>: Save the source file dependency list in a file.\n"
- " -entry <name>: Specify the name of an entry-point function.\n"
- " Multiple -entry options may be used in a single invocation.\n"
- " If no -entry options are given, compiler will use [shader(...)]\n"
- " attributes to detect entry points.\n"
- " -h, -help, --help: Print this message.\n"
- " -I<path>, -I <path>: Add a path to be used in resolving '#include'\n"
- " and 'import' operations.\n"
- " -lang <language>: Set the language for the following input files.\n"
- " Accepted languages are:\n"
- " c, cpp, c++, cxx, slang, glsl, hlsl, cu, cuda\n"
- " -matrix-layout-column-major: Set the default matrix layout to column-major.\n"
- " -matrix-layout-row-major: Set the default matrix layout to row-major.\n"
- " -module-name <name>: Set the module name to use when compiling multiple\n"
- " .slang source files into a single module.\n"
- " -o <path>: Specify a path where generated output should be written.\n"
- " If no -target or -stage is specified, one may be inferred\n"
- " from file extension (see File Extensions).\n"
- " If multiple -target options and a single -entry are present, each -o\n"
- " associates with the first -target to its left.\n"
- " Otherwise, if multiple -entry options are present, each -o associates\n"
- " with the first -entry to its left, and with the -target that matches\n"
- " the one inferred from <path>.\n"
- " -profile <profile>[+<capability>...]: Specify the shader profile for code\n"
- " 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"
- " Additional profiles that include -stage information:\n"
- " {vs,hs,ds,gs,ps}_<version>\n"
- " See -capability for information on <capability>\n"
- " When multiple -target options are present, each -profile associates\n"
- " with the first -target to its left.\n"
- " -stage <name>: Specify the stage of an entry-point function.\n"
- " Accepted stages are:\n"
- " vertex, hull, domain, geometry, fragment, compute,\n"
- " raygeneration, intersection, anyhit, closesthit, miss, callable\n"
- " When multiple -entry options are present, each -stage associated with\n"
- " the first -entry to its left.\n"
- " May be omitted if entry-point function has a [shader(...)] attribute;\n"
- " otherwise required for each -entry option.\n"
- " -target <format>: Specifies the format in which code should be generated.\n"
- " Accepted formats are:\n"
- " glsl, hlsl, spirv, spirv-assembly, dxbc,\n"
- " dxbc-assembly, dxil, dxil-assembly\n"
- " -v, -version: Display the build version.\n"
- " -warnings-as-errors all: Treat all warnings as errors.\n"
- " -warnings-as-errors <id>[,<id>...]: Treat specific warning ids as errors.\n"
- " -warnings-disable <id>[,<id>...]: Disable specific warning ids.\n"
- " -W<id>: Enable a warning with the specified id.\n"
- " -Wno-<id>: Disable a warning with the specified id.\n"
- " -dump-warning-diagnostics: Dump to output list of warning diagnostic numeric and name ids.\n"
- " --: Treat the rest of the command line as input files.\n"
- "\n"
- "Target code generation options:\n"
- "\n"
- " -capability <capability>[+<capability>...]: Add optional capabilities\n"
- " to a code generation target. See Capabilities below.\n"
- " -default-image-format-unknown: Set the format of R/W images with unspecified\n"
- " format to 'unknown'. Otherwise try to guess the format.\n"
- " -disable-dynamic-dispatch: Disables generating dynamic dispatch code.\n"
- " -disable-specialization: Disables generics and specialization pass.\n"
- " -fp-mode <mode>, -floating-point-mode <mode>: Set the floating point mode.\n"
- " Accepted modes are:\n"
- " precise : Disable optimization that could change the output of floating-\n"
- " point computations, including around infinities, NaNs, denormalized\n"
- " values, and negative zero. Prefer the most precise versions of special\n"
- " functions supported by the target.\n"
- " fast : Allow optimizations that may change results of floating-point\n"
- " computations. Prefer the fastest version of special functions supported\n"
- " by the target.\n"
- " -g, -g<N>: Include debug information in the generated code, where possible.\n"
- " N is the amount of information, 0..3, unspecified means 2\n"
- " -line-directive-mode <mode>: Sets how the `#line` directives should be\n"
- " produced. Available options are:\n"
- " none : Don't emit `#line` directives at all\n"
- " source-map : Use source map to track line associations (doen't emit #line)\n"
- " default : Default behavior\n"
- " If not specified, default behavior is to use C-style `#line` directives\n"
- " for HLSL and C/C++ output, and traditional GLSL-style `#line` directives\n"
- " for GLSL output.\n"
- " -O<N>: Set the optimization level.\n"
- " N is the amount of optimization, 0..3, default is 1\n"
- " -obfuscate: Remove all source file information from outputs.\n"
- "\n"
- "Downstream compiler options:\n"
- "\n"
- " -<compiler>-path: Specify path to a downstream <compiler>\n"
- " executable or library. Accepted compilers are:\n"
- " fxc (d3dcompiler_47.dll)\n"
- " dxc (dxcompiler.*)\n"
- " glslang (slang-glslang.*)\n"
- " vs = visualstudio (cl.exe)\n"
- " clang\n"
- " gcc (g++)\n"
- " c = cpp = genericcpp\n"
- " nvrtc\n"
- " llvm\n"
- " -default-downstream-compiler <language> <compiler>: Set a default compiler\n"
- " for the given language. See -lang for the list of languages.\n"
- " -X<compiler> <option>: Pass arguments to downstream <compiler>.\n"
- "\n"
- "Compiler debugging/instrumentation options:\n"
- "\n"
- " -dump-ast: Dump the AST to a .slang-ast file next to the input.\n"
- " -dump-intermediate-prefix <prefix>: File name prefix for -dump-intermediates \n"
- " outputs, default is 'slang-dump-'\n"
- " -dump-intermediates: Dump intermediate outputs for debugging.\n"
- " -dump-ir: Dump the IR for debugging.\n"
- " -dump-ir-ids: Dump the IDs with -dump-ir (debug builds only)\n"
- " -dump-repro: Dump a `.slang-repro` file that can be used to reproduce\n"
- " a compilation on another machine.\n"
- " -dump-repro-on-error: Dump `.slang-repro` file on any compilation error.\n"
- " -E, -output-preprocessor: Output the preprocessing result and exit.\n"
- " -extract-repro <name>: Extract the repro files into a folder.\n"
- " -load-repro <name>\n"
- " -load-repro-directory <path>\n"
- " -no-codegen: Skip the code generation step, just check the code and\n"
- " generate layout.\n"
- " -output-includes: Print the hierarchy of the processed source files.\n"
- " -pass-through <name>: Pass the input through mostly unmodified to the \n"
- " existing compiler <name>. Accepted compilers are:\n"
- " fxc, glslang, dxc\n"
- " -repro-file-system <name>\n"
- " -serial-ir: Serialize the IR between front-end and back-end.\n"
- " -skip-codegen: Skip the code generation phase.\n"
- " -validate-ir: Validate the IR between the phases.\n"
- " -verbose-paths: Display more detailed paths in diagnostic output.\n"
- " -verify-debug-serial-ir: Verify IR in the front-end.\n"
- "\n"
- "Experimental options (use at your own risk):\n"
- "\n"
- " -emit-spirv-directly: Generate SPIR-V output directly (otherwise through \n"
- " GLSL and using the glslang compiler)\n"
- " -file-system <fs>: Set the filesystem hook to use for a compile request.\n"
- " Accepted file systems:\n"
- " default, load-file, os\n"
- " -heterogeneous: Output heterogeneity-related code.\n"
- " -no-mangle: Do as little mangling of names as possible.\n"
- "\n"
- "Internal-use options (use at your own risk):\n"
- "\n"
- " -archive-type <type>: Set the archive type for -save-stdlib. Default is zip.\n"
- " Accepted archive types:\n"
- " zip, riff, riff-deflate, riff-lz4\n"
- " -compile-stdlib: Compile the StdLib from embedded sources.\n"
- " Will return a failure if there is already a StdLib available.\n"
- " -doc: Write documentation for -compile-stdlib\n"
- " -ir-compression <type>: Set compression for IR and AST outputs.\n"
- " Accepted compression types:\n"
- " none, lite\n"
- " -load-stdlib <filename>: Load the StdLib from file.\n"
- " -r <name>: reference module <name>\n"
- " -save-stdlib <filename>: Save the StdLib modules to an archive file.\n"
- " -save-stdlib-bin-source <filename>: Same as -save-stdlib but output\n"
- " the data as a C array.\n"
- " -track-liveness: Enable liveness tracking. Places SLANG_LIVE_START, and SLANG_LIVE_END in output source to indicate value liveness.\n"
- " -source-map: Enables outputting of a source map. Note this is *distinct* from line-directive-mode.\n"
- "\n"
- "Deprecated options (allowed but ignored; may be removed in future):\n"
- "\n"
- " -parameter-blocks-use-register-spaces\n"
- "\n"
- "File Extensions:\n"
- "\n"
- " A <language>, <format>, and/or <stage> may be inferred from the\n"
- " extension of an input or -o path:\n"
- "\n"
- " extension | language/format | stage\n"
- " ====================|=================|======\n"
- " .hlsl, .fx -> hlsl\n"
- " .dxbc -> dxbc\n"
- " .dxbc-asm -> dxbc-assembly\n"
- " .dxil -> dxil\n"
- " .dxil-asm -> dxil-assembly\n"
- " .glsl -> glsl\n"
- " .vert -> glsl vertex\n"
- " .frag -> glsl fragment\n"
- " .geom -> glsl geoemtry\n"
- " .tesc -> glsl hull\n"
- " .tese -> glsl domain\n"
- " .comp -> glsl compute\n"
- " .slang -> slang\n"
- " .spv -> spirv\n"
- " .spv-asm -> spirv-assembly\n"
- " .c -> c\n"
- " .cpp, .c++, .cxx -> c++\n"
- " .exe -> executable\n"
- " .dll, .so -> sharedlibrary\n"
- " .cu -> cuda\n"
- " .ptx -> ptx\n"
- " .obj, .o -> object-code\n"
- "\n"
- "Capabilities:\n"
- "\n"
- " A capability describes an optional feature that a target may or\n"
- " may not support. When a -capability is specified, the compiler\n"
- " may assume that the target supports that capability, and generate\n"
- " code accordingly.\n"
- " Currently defined capabilities are:\n"
- "\n"
- " spirv_1_{0,1,2,3,4,5} - minimum supported SPIR-V version\n"
- " GL_NV_ray_tracing - enables the GL_NV_ray_tracing extension\n"
- " GL_EXT_ray_tracing - enables the GL_EXT_ray_tracing extension\n"
- " GL_NV_fragment_shader_barycentric - enables the GL_NV_fragment_shader_barycentric extension\n"
- " GL_EXT_fragment_shader_barycentric - enables the GL_EXT_fragment_shader_barycentric extension\n"
- "\n";
-
-#undef EXECUTABLE_EXTENSION
- }
-
// Pass Severity::Disabled to allow any original severity
SlangResult _overrideDiagnostics(const UnownedStringSlice& identifierList, Severity originalSeverity, Severity overrideSeverity, DiagnosticSink* sink)
{
@@ -830,6 +1087,59 @@ struct OptionsParser
return SLANG_OK;
}
+ SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
+ {
+ auto& cmdOptions = asInternal(session)->m_commandOptions;
+
+ const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), arg.value.getUnownedSlice());
+ if (optionIndex < 0)
+ {
+ const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory));
+ SLANG_ASSERT(categoryIndex >= 0);
+ if (categoryIndex < 0)
+ {
+ return SLANG_FAIL;
+ }
+
+ List<UnownedStringSlice> names;
+ cmdOptions.getCategoryOptionNames(categoryIndex, names);
+
+ StringBuilder buf;
+ StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), buf);
+
+ sink->diagnose(arg.loc, Diagnostics::unknownCommandLineValue, buf);
+ return SLANG_FAIL;
+ }
+
+ outValue = cmdOptions.getOptionAt(optionIndex).userValue;
+ return SLANG_OK;
+ }
+ SlangResult _expectValue(ValueCategory valueCategory, CommandLineReader& reader, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
+ {
+ CommandLineArg arg;
+ SLANG_RETURN_ON_FAIL(reader.expectArg(arg));
+ SLANG_RETURN_ON_FAIL(_getValue(valueCategory, arg, sink, outValue));
+ return SLANG_OK;
+ }
+
+ void _appendUsageTitle(StringBuilder& out)
+ {
+ out << "Usage: slangc [options...] [--] <input files>\n\n";
+ }
+ void _appendMinimalUsage(StringBuilder& out)
+ {
+ _appendUsageTitle(out);
+ out << "For help: slangc -h\n";
+ }
+ void _outputMinimalUsage(DiagnosticSink* sink)
+ {
+ // Output usage info
+ StringBuilder buf;
+ _appendMinimalUsage(buf);
+
+ sink->diagnoseRaw(Severity::Note, buf.getUnownedSlice());
+ }
+
SlangResult parse(
int argc,
char const* const* argv)
@@ -896,23 +1206,39 @@ struct OptionsParser
slang::CompileStdLibFlags compileStdLibFlags = 0;
bool hasLoadedRepro = false;
+ // Get the options on the session
+ CommandOptions& options = asInternal(session)->m_commandOptions;
+
+ auto frontEndReq = requestImpl->getFrontEndReq();
+
while (reader.hasArg())
{
auto arg = reader.getArgAndAdvance();
const auto& argValue = arg.value;
- if (argValue[0] == '-')
+ // If it's not an option we assume it's a path
+ if (argValue[0] != '-')
{
- if(argValue == "-no-mangle" )
- {
- flags |= SLANG_COMPILE_FLAG_NO_MANGLING;
- }
- else if (argValue == toSlice("-emit-ir"))
- {
- // Enable emitting IR
- requestImpl->m_emitIr = true;
- }
- else if (argValue == "-load-stdlib")
+ SLANG_RETURN_ON_FAIL(addInputPath(argValue.getBuffer()));
+ continue;
+ }
+
+ const Index optionIndex = options.findOptionByName(argValue.getUnownedSlice());
+
+ if (optionIndex < 0)
+ {
+ sink->diagnose(arg.loc, Diagnostics::unknownCommandLineOption, argValue);
+ _outputMinimalUsage(sink);
+ return SLANG_FAIL;
+ }
+
+ const auto optionKind = OptionKind(options.getOptionAt(optionIndex).userValue);
+
+ switch (optionKind)
+ {
+ case OptionKind::NoMangle: flags |= SLANG_COMPILE_FLAG_NO_MANGLING; break;
+ case OptionKind::EmitIr: requestImpl->m_emitIr = true; break;
+ case OptionKind::LoadStdLib:
{
CommandLineArg fileName;
SLANG_RETURN_ON_FAIL(reader.expectArg(fileName));
@@ -921,24 +1247,17 @@ struct OptionsParser
ScopedAllocation contents;
SLANG_RETURN_ON_FAIL(File::readAllBytes(fileName.value, contents));
SLANG_RETURN_ON_FAIL(session->loadStdLib(contents.getData(), contents.getSizeInBytes()));
+ break;
}
- else if (argValue == "-compile-stdlib")
- {
- compileStdLib = true;
- }
- else if (argValue == "-archive-type")
+ case OptionKind::CompileStdLib: compileStdLib = true; break;
+ case OptionKind::ArchiveType:
{
- CommandLineArg archiveTypeName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(archiveTypeName));
-
- archiveType = TypeTextUtil::findArchiveType(archiveTypeName.value.getUnownedSlice());
- if (archiveType == SLANG_ARCHIVE_TYPE_UNDEFINED)
- {
- sink->diagnose(archiveTypeName.loc, Diagnostics::unknownArchiveType, archiveTypeName.value);
- return SLANG_FAIL;
- }
+ CommandOptions::UserValue value;
+ SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::ArchiveType, reader, sink, value));
+ archiveType = SlangArchiveType(value);
+ break;
}
- else if (argValue == "-save-stdlib")
+ case OptionKind::SaveStdLib:
{
CommandLineArg fileName;
SLANG_RETURN_ON_FAIL(reader.expectArg(fileName));
@@ -947,8 +1266,9 @@ struct OptionsParser
SLANG_RETURN_ON_FAIL(session->saveStdLib(archiveType, blob.writeRef()));
SLANG_RETURN_ON_FAIL(File::writeAllBytes(fileName.value, blob->getBufferPointer(), blob->getBufferSize()));
+ break;
}
- else if (argValue == "-save-stdlib-bin-source")
+ case OptionKind::SaveStdLibBinSource:
{
CommandLineArg fileName;
SLANG_RETURN_ON_FAIL(reader.expectArg(fileName));
@@ -963,61 +1283,45 @@ struct OptionsParser
SLANG_RETURN_ON_FAIL(HexDumpUtil::dumpSourceBytes((const uint8_t*)blob->getBufferPointer(), blob->getBufferSize(), 16, &writer));
File::writeAllText(fileName.value, builder);
+ break;
}
- else if (argValue == "-no-codegen")
- {
- flags |= SLANG_COMPILE_FLAG_NO_CODEGEN;
- }
- else if (argValue == "-dump-intermediates")
- {
- compileRequest->setDumpIntermediates(true);
- }
- else if (argValue == "-dump-ir-ids")
+ case OptionKind::NoCodeGen: flags |= SLANG_COMPILE_FLAG_NO_CODEGEN; break;
+ case OptionKind::DumpIntermediates: compileRequest->setDumpIntermediates(true); break;
+ case OptionKind::DumpIrIds:
{
- requestImpl->getFrontEndReq()->m_irDumpOptions.flags |= IRDumpOptions::Flag::DumpDebugIds;
+ frontEndReq->m_irDumpOptions.flags |= IRDumpOptions::Flag::DumpDebugIds;
+ break;
}
- else if (argValue == "-dump-intermediate-prefix")
+ case OptionKind::DumpIntermediatePrefix:
{
CommandLineArg prefix;
SLANG_RETURN_ON_FAIL(reader.expectArg(prefix));
requestImpl->m_dumpIntermediatePrefix = prefix.value;
+ break;
}
- else if (argValue == "-output-includes")
- {
- requestImpl->getFrontEndReq()->outputIncludes = true;
- }
- else if(argValue == "-dump-ir" )
- {
- requestImpl->getFrontEndReq()->shouldDumpIR = true;
- }
- else if (argValue == "-E" || argValue == "-output-preprocessor")
- {
- requestImpl->getFrontEndReq()->outputPreprocessor = true;
- }
- else if (argValue == "-dump-ast")
- {
- requestImpl->getFrontEndReq()->shouldDumpAST = true;
- }
- else if (argValue == "-doc")
+ case OptionKind::OutputIncludes: frontEndReq->outputIncludes = true; break;
+ case OptionKind::DumpIr: frontEndReq->shouldDumpIR = true; break;
+ case OptionKind::PreprocessorOutput: frontEndReq->outputPreprocessor = true; break;
+ case OptionKind::DumpAst: frontEndReq->shouldDumpAST = true; break;
+ case OptionKind::Doc:
{
// If compiling stdlib is enabled, will write out documentation
compileStdLibFlags |= slang::CompileStdLibFlag::WriteDocumentation;
// Enable writing out documentation on the req
- requestImpl->getFrontEndReq()->shouldDocument = true;
+ frontEndReq->shouldDocument = true;
+ break;
}
- else if (argValue == "-dump-repro")
+ case OptionKind::DumpRepro:
{
CommandLineArg dumpRepro;
SLANG_RETURN_ON_FAIL(reader.expectArg(dumpRepro));
requestImpl->m_dumpRepro = dumpRepro.value;
compileRequest->enableReproCapture();
+ break;
}
- else if (argValue == "-dump-repro-on-error")
- {
- requestImpl->m_dumpReproOnError = true;
- }
- else if (argValue == "-extract-repro")
+ case OptionKind::DumpReproOnError: requestImpl->m_dumpReproOnError = true; break;
+ case OptionKind::ExtractRepro:
{
CommandLineArg reproName;
SLANG_RETURN_ON_FAIL(reader.expectArg(reproName));
@@ -1030,15 +1334,17 @@ struct OptionsParser
return res;
}
}
+ break;
}
- else if (argValue == "-module-name")
+ case OptionKind::ModuleName:
{
CommandLineArg moduleName;
SLANG_RETURN_ON_FAIL(reader.expectArg(moduleName));
compileRequest->setDefaultModuleName(moduleName.value.getBuffer());
+ break;
}
- else if(argValue == "-load-repro")
+ case OptionKind::LoadRepro:
{
CommandLineArg reproName;
SLANG_RETURN_ON_FAIL(reader.expectArg(reproName));
@@ -1072,15 +1378,17 @@ struct OptionsParser
SLANG_RETURN_ON_FAIL(ReproUtil::load(base, requestState, fileSystem, requestImpl));
hasLoadedRepro = true;
+ break;
}
- else if (argValue == "-load-repro-directory")
+ case OptionKind::LoadReproDirectory:
{
CommandLineArg reproDirectory;
SLANG_RETURN_ON_FAIL(reader.expectArg(reproDirectory));
SLANG_RETURN_ON_FAIL(_compileReproDirectory(session, requestImpl, reproDirectory.value, sink));
+ break;
}
- else if (argValue == "-repro-file-system")
+ case OptionKind::ReproFileSystem:
{
CommandLineArg reproName;
SLANG_RETURN_ON_FAIL(reader.expectArg(reproName));
@@ -1122,32 +1430,15 @@ struct OptionsParser
// Set as the file system
compileRequest->setFileSystem(fileSystem);
+ break;
}
- else if (argValue == "-serial-ir")
- {
- requestImpl->getFrontEndReq()->useSerialIRBottleneck = true;
- }
- else if (argValue == "-disable-specialization")
- {
- requestImpl->disableSpecialization = true;
- }
- else if (argValue == "-disable-dynamic-dispatch")
- {
- requestImpl->disableDynamicDispatch = true;
- }
- else if (argValue == "-track-liveness")
- {
- requestImpl->setTrackLiveness(true);
- }
- else if (argValue == "-verbose-paths")
- {
- requestImpl->getSink()->setFlag(DiagnosticSink::Flag::VerbosePath);
- }
- else if (argValue == "-dump-warning-diagnostics")
- {
- _dumpDiagnostics(Severity::Warning, sink);
- }
- else if (argValue == "-warnings-as-errors")
+ case OptionKind::SerialIr: frontEndReq->useSerialIRBottleneck = true; break;
+ case OptionKind::DisableSpecialization: requestImpl->disableSpecialization = true; break;
+ case OptionKind::DisableDynamicDispatch: requestImpl->disableDynamicDispatch = true; break;
+ case OptionKind::TrackLiveness: requestImpl->setTrackLiveness(true); break;
+ case OptionKind::VerbosePaths: requestImpl->getSink()->setFlag(DiagnosticSink::Flag::VerbosePath); break;
+ case OptionKind::DumpWarningDiagnostics: _dumpDiagnostics(Severity::Warning, sink); break;
+ case OptionKind::WarningsAsErrors:
{
CommandLineArg operand;
SLANG_RETURN_ON_FAIL(reader.expectArg(operand));
@@ -1162,52 +1453,47 @@ struct OptionsParser
{
SLANG_RETURN_ON_FAIL(_overrideDiagnostics(operand.value.getUnownedSlice(), Severity::Warning, Severity::Error, sink));
}
+ break;
}
- else if (argValue == "-warnings-disable")
+ case OptionKind::DisableWarnings:
{
CommandLineArg operand;
SLANG_RETURN_ON_FAIL(reader.expectArg(operand));
SLANG_RETURN_ON_FAIL(_overrideDiagnostics(operand.value.getUnownedSlice(), Severity::Warning, Severity::Disable, sink));
+ break;
}
- else if (argValue.startsWith(toSlice("-W")))
- {
- auto name = argValue.getUnownedSlice().tail(2);
-
- // If prefixed with 'no-', disable the warning
- if (name.startsWith(toSlice("no-")))
- {
- SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name.tail(3), Severity::Warning, Severity::Disable, sink));
- }
- else
- {
- // Enable the warning
- SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Warning, sink));
- }
- }
- else if (argValue == "-verify-debug-serial-ir")
- {
- requestImpl->getFrontEndReq()->verifyDebugSerialization = true;
- }
- else if(argValue == "-validate-ir" )
+ case OptionKind::DisableWarning:
{
- requestImpl->getFrontEndReq()->shouldValidateIR = true;
+ // 5 because -Wno-
+ auto name = argValue.getUnownedSlice().tail(5);
+ SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Disable, sink));
+ break;
}
- else if(argValue == "-skip-codegen" )
+ case OptionKind::EnableWarning:
{
- requestImpl->m_shouldSkipCodegen = true;
+ // 2 because -W
+ auto name = argValue.getUnownedSlice().tail(5);
+ // Enable the warning
+ SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Warning, sink));
+ break;
}
- else if(argValue == "-parameter-blocks-use-register-spaces" )
+ case OptionKind::VerifyDebugSerialIr: frontEndReq->verifyDebugSerialization = true; break;
+ case OptionKind::ValidateIr: frontEndReq->shouldValidateIR = true; break;
+ case OptionKind::SkipCodeGen: requestImpl->m_shouldSkipCodegen = true; break;
+ case OptionKind::ParameterBlocksUseRegisterSpaces:
{
- getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_PARAMETER_BLOCKS_USE_REGISTER_SPACES;
+ getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_PARAMETER_BLOCKS_USE_REGISTER_SPACES;
+ break;
}
- else if (argValue == "-ir-compression")
+ case OptionKind::IrCompression:
{
CommandLineArg name;
SLANG_RETURN_ON_FAIL(reader.expectArg(name));
SLANG_RETURN_ON_FAIL(SerialParseUtil::parseCompressionType(name.value.getUnownedSlice(), requestImpl->getLinkage()->serialCompressionType));
+ break;
}
- else if (argValue == "-target")
+ case OptionKind::Target:
{
CommandLineArg name;
SLANG_RETURN_ON_FAIL(reader.expectArg(name));
@@ -1224,12 +1510,14 @@ struct OptionsParser
rawTarget.format = CodeGenTarget(format);
rawTargets.add(rawTarget);
+ break;
}
- // A "profile" can specify both a general capability level for
- // a target, and also (as a legacy/compatibility feature) a
- // specific stage to use for an entry point.
- else if (argValue == "-profile")
+ case OptionKind::Profile:
{
+ // A "profile" can specify both a general capability level for
+ // a target, and also (as a legacy/compatibility feature) a
+ // specific stage to use for an entry point.
+
CommandLineArg operand;
SLANG_RETURN_ON_FAIL(reader.expectArg(operand));
@@ -1284,8 +1572,10 @@ struct OptionsParser
addCapabilityAtom(getCurrentTarget(), atom);
}
+
+ break;
}
- else if( argValue == "-capability" )
+ case OptionKind::Capability:
{
// The `-capability` option is similar to `-profile` but does not set the actual profile
// for a target (it just adds capabilities).
@@ -1313,8 +1603,9 @@ struct OptionsParser
addCapabilityAtom(getCurrentTarget(), atom);
}
+ break;
}
- else if (argValue == "-stage")
+ case OptionKind::Stage:
{
CommandLineArg name;
SLANG_RETURN_ON_FAIL(reader.expectArg(name));
@@ -1329,8 +1620,9 @@ struct OptionsParser
{
setStage(getCurrentEntryPoint(), stage);
}
+ break;
}
- else if (argValue == "-entry")
+ case OptionKind::EntryPointName:
{
CommandLineArg name;
SLANG_RETURN_ON_FAIL(reader.expectArg(name));
@@ -1340,8 +1632,9 @@ struct OptionsParser
rawEntryPoint.translationUnitIndex = currentTranslationUnitIndex;
rawEntryPoints.add(rawEntryPoint);
+ break;
}
- else if (argValue == "-lang")
+ case OptionKind::Language:
{
CommandLineArg name;
SLANG_RETURN_ON_FAIL(reader.expectArg(name));
@@ -1360,8 +1653,9 @@ struct OptionsParser
SLANG_RETURN_ON_FAIL(addInputPath(reader.getValueAndAdvance().getBuffer(), sourceLanguage));
}
}
+ break;
}
- else if (argValue == "-pass-through")
+ case OptionKind::PassThrough:
{
CommandLineArg name;
SLANG_RETURN_ON_FAIL(reader.expectArg(name));
@@ -1374,8 +1668,9 @@ struct OptionsParser
}
compileRequest->setPassThrough(passThrough);
+ break;
}
- else if (argValue.getLength() >= 2 && argValue[1] == 'D')
+ case OptionKind::MacroDefine:
{
// The value to be defined might be part of the same option, as in:
// -DFOO
@@ -1396,7 +1691,7 @@ struct OptionsParser
const Index equalIndex = slice.indexOf('=');
// Now set the preprocessor define
-
+
if (equalIndex >= 0)
{
// If we found an `=`, we split the string...
@@ -1407,8 +1702,9 @@ struct OptionsParser
// If there was no `=`, then just #define it to an empty string
compileRequest->addPreprocessorDefine(String(slice).getBuffer(), "");
}
+ break;
}
- else if (argValue.getLength() >= 2 && argValue[1] == 'I')
+ case OptionKind::Include:
{
// The value to be defined might be part of the same option, as in:
// -IFOO
@@ -1426,18 +1722,19 @@ struct OptionsParser
}
compileRequest->addSearchPath(String(slice).getBuffer());
+ break;
}
- //
- // A `-o` option is used to specify a desired output file.
- else if (argValue == "-o")
+ case OptionKind::Output:
{
+ //
+ // A `-o` option is used to specify a desired output file.
CommandLineArg outputPath;
SLANG_RETURN_ON_FAIL(reader.expectArg(outputPath));
addOutputPath(outputPath.value.getBuffer());
+ break;
}
- // A -depfile option is used to specify the file name where the dependency lists will be written
- else if (argValue == "-depfile")
+ case OptionKind::DepFile:
{
CommandLineArg dependencyPath;
SLANG_RETURN_ON_FAIL(reader.expectArg(dependencyPath));
@@ -1451,65 +1748,25 @@ struct OptionsParser
sink->diagnose(dependencyPath.loc, Diagnostics::duplicateDependencyOutputPaths);
return SLANG_FAIL;
}
+ break;
}
- else if(argValue == "-matrix-layout-row-major")
- {
- defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_RowMajor);
- }
- else if(argValue == "-matrix-layout-column-major")
- {
- defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_ColumnMajor);
- }
- else if(argValue == "-line-directive-mode")
+ case OptionKind::MatrixLayoutRow: defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_RowMajor); break;
+ case OptionKind::MatrixLayoutColumn: defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_ColumnMajor); break;
+ case OptionKind::LineDirectiveMode:
{
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
-
- SlangLineDirectiveMode mode = SLANG_LINE_DIRECTIVE_MODE_DEFAULT;
-
- if(name.value == toSlice("none"))
- {
- mode = SLANG_LINE_DIRECTIVE_MODE_NONE;
- }
- else if (name.value == toSlice("source-map"))
- {
- mode = SLANG_LINE_DIRECTIVE_MODE_SOURCE_MAP;
- }
- else if (name.value == toSlice("default"))
- {
- mode = SLANG_LINE_DIRECTIVE_MODE_DEFAULT;
- }
- else
- {
- sink->diagnose(name.loc, Diagnostics::unknownLineDirectiveMode, name.value);
- return SLANG_FAIL;
- }
-
- compileRequest->setLineDirectiveMode(mode);
+ CommandOptions::UserValue value;
+ SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::LineDirectiveMode, reader, sink, value));
+ compileRequest->setLineDirectiveMode(SlangLineDirectiveMode(value));
+ break;
}
- else if( argValue == "-fp-mode" || argValue == "-floating-point-mode" )
+ case OptionKind::FloatingPointMode:
{
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
-
- FloatingPointMode mode = FloatingPointMode::Default;
- if(name.value == "fast")
- {
- mode = FloatingPointMode::Fast;
- }
- else if(name.value == "precise")
- {
- mode = FloatingPointMode::Precise;
- }
- else
- {
- sink->diagnose(name.loc, Diagnostics::unknownFloatingPointMode, name.value);
- return SLANG_FAIL;
- }
-
- setFloatingPointMode(getCurrentTarget(), mode);
+ CommandOptions::UserValue value;
+ SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::FloatingPointMode, reader, sink, value));
+ setFloatingPointMode(getCurrentTarget(), FloatingPointMode(value));
+ break;
}
- else if( argValue.getLength() >= 2 && argValue[1] == 'O' )
+ case OptionKind::Optimization:
{
UnownedStringSlice levelSlice = argValue.getUnownedSlice().tail(2);
SlangOptimizationLevel level = SLANG_OPTIMIZATION_LEVEL_DEFAULT;
@@ -1530,12 +1787,13 @@ struct OptionsParser
}
compileRequest->setOptimizationLevel(level);
+ break;
}
- // 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.
- else if (argValue.startsWith("-g"))
+ case OptionKind::DebugInformation:
{
+ // 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"))
{
// The default is standard
@@ -1594,16 +1852,11 @@ struct OptionsParser
compileRequest->setDebugInfoFormat(format);
}
+ break;
}
- else if( argValue == "-default-image-format-unknown" )
- {
- requestImpl->useUnknownImageFormatAsDefault = true;
- }
- else if (argValue == "-obfuscate")
- {
- requestImpl->getLinkage()->m_obfuscateCode = true;
- }
- else if (argValue == "-file-system")
+ case OptionKind::DefaultImageFormatUnknown: requestImpl->useUnknownImageFormatAsDefault = true; break;
+ case OptionKind::Obfuscate: requestImpl->getLinkage()->m_obfuscateCode = true; break;
+ case OptionKind::FileSystem:
{
CommandLineArg name;
SLANG_RETURN_ON_FAIL(reader.expectArg(name));
@@ -1627,8 +1880,9 @@ struct OptionsParser
sink->diagnose(name.loc, Diagnostics::unknownFileSystemOption, name.value);
return SLANG_FAIL;
}
+ break;
}
- else if (argValue == "-r")
+ case OptionKind::ReferenceModule:
{
CommandLineArg referenceModuleName;
SLANG_RETURN_ON_FAIL(reader.expectArg(referenceModuleName));
@@ -1689,21 +1943,50 @@ struct OptionsParser
artifact->addRepresentation(fileRep);
SLANG_RETURN_ON_FAIL(_addLibraryReference(requestImpl, artifact));
+ break;
}
- else if (argValue == "-v" || argValue == "-version")
+ case OptionKind::Version:
{
sink->diagnoseRaw(Severity::Note, session->getBuildTagString());
+ break;
}
- else if (argValue == "-h" || argValue == "-help" || argValue == "--help")
+ case OptionKind::Help:
{
- sink->diagnoseRaw(Severity::Note, getHelpText());
+
+ Index categoryIndex = -1;
+
+ if (reader.hasArg())
+ {
+ auto catArg = reader.getArgAndAdvance();
+
+ categoryIndex = options.findCategoryByCaseInsensitiveName(catArg.value.getUnownedSlice());
+ if (categoryIndex)
+ {
+ sink->diagnose(catArg.loc, Diagnostics::unknownHelpCategory);
+ return SLANG_FAIL;
+ }
+ }
+
+ CommandOptionsWriter writer;
+ auto& buf = writer.getBuilder();
+
+ if (categoryIndex < 0)
+ {
+ _appendUsageTitle(buf);
+ writer.appendDescription(options);
+ }
+ else
+ {
+ writer.appendDescriptionForCategory(options, categoryIndex);
+ }
+
+ sink->diagnoseRaw(Severity::Note, buf.getBuffer());
+
return SLANG_FAIL;
}
- else if( argValue == "-emit-spirv-directly" )
- {
- getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY;
- }
- else if (argValue == "-default-downstream-compiler")
+ case OptionKind::EmitSpirvDirectly: getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY; break;
+
+ case OptionKind::DefaultDownstreamCompiler:
{
CommandLineArg sourceLanguageArg, compilerArg;
SLANG_RETURN_ON_FAIL(reader.expectArg(sourceLanguageArg));
@@ -1728,8 +2011,34 @@ struct OptionsParser
sink->diagnose(arg.loc, Diagnostics::unableToSetDefaultDownstreamCompiler, compilerArg.value, sourceLanguageArg.value);
return SLANG_FAIL;
}
+ break;
}
- else if (argValue == "--")
+ case OptionKind::CompilerPath:
+ {
+ const Index index = argValue.lastIndexOf('-');
+ if (index >= 0)
+ {
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(reader.expectArg(name));
+
+ UnownedStringSlice passThroughSlice = argValue.getUnownedSlice().head(index).tail(1);
+
+ // Skip the initial -, up to the last -
+ SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE;
+ if (SLANG_SUCCEEDED(TypeTextUtil::findPassThrough(passThroughSlice, passThrough)))
+ {
+ session->setDownstreamCompilerPath(passThrough, name.value.getBuffer());
+ continue;
+ }
+ else
+ {
+ sink->diagnose(arg.loc, Diagnostics::unknownDownstreamCompiler, passThroughSlice);
+ return SLANG_FAIL;
+ }
+ }
+ break;
+ }
+ case OptionKind::InputFilesRemain:
{
// The `--` option causes us to stop trying to parse options,
// and treat the rest of the command line as input file names:
@@ -1738,43 +2047,16 @@ struct OptionsParser
SLANG_RETURN_ON_FAIL(addInputPath(reader.getValueAndAdvance().getBuffer()));
}
break;
- }
- else
+ }
+ default:
{
- if (argValue.endsWith("-path"))
- {
- const Index index = argValue.lastIndexOf('-');
- if (index >= 0)
- {
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
-
- UnownedStringSlice passThroughSlice = argValue.getUnownedSlice().head(index).tail(1);
-
- // Skip the initial -, up to the last -
- SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE;
- if (SLANG_SUCCEEDED(TypeTextUtil::findPassThrough(passThroughSlice, passThrough)))
- {
- session->setDownstreamCompilerPath(passThrough, name.value.getBuffer());
- continue;
- }
- else
- {
- sink->diagnose(arg.loc, Diagnostics::unknownDownstreamCompiler, passThroughSlice);
- return SLANG_FAIL;
- }
- }
- }
-
+ // Hmmm, we looked up and produced a valid enum, but it wasn't handled in the switch...
sink->diagnose(arg.loc, Diagnostics::unknownCommandLineOption, argValue);
- // TODO: print a usage message
+
+ _outputMinimalUsage(sink);
return SLANG_FAIL;
}
}
- else
- {
- SLANG_RETURN_ON_FAIL(addInputPath(argValue.getBuffer()));
- }
}
if (compileStdLib)
@@ -2371,7 +2653,6 @@ struct OptionsParser
}
};
-
SlangResult parseOptions(
SlangCompileRequest* inCompileRequest,
int argc,
diff --git a/source/slang/slang-options.h b/source/slang/slang-options.h
index 2426c08cb..bb05312a3 100644
--- a/source/slang/slang-options.h
+++ b/source/slang/slang-options.h
@@ -7,6 +7,7 @@
namespace Slang
{
+struct CommandOptions;
UnownedStringSlice getCodeGenTargetName(SlangCompileTarget target);
@@ -15,5 +16,8 @@ SlangResult parseOptions(
int argc,
char const* const* argv);
+// Initialize command options. Holds the details how parsing works.
+void initCommandOptions(CommandOptions& commandOptions);
+
}
#endif
diff --git a/source/slang/slang-profile.h b/source/slang/slang-profile.h
index 661274ad4..0adb9dee9 100644
--- a/source/slang/slang-profile.h
+++ b/source/slang/slang-profile.h
@@ -50,6 +50,7 @@ namespace Slang
#define PROFILE_STAGE(TAG, NAME, VAL) TAG = VAL,
#define PROFILE_STAGE_ALIAS(TAG, NAME, VAL) TAG = VAL,
#include "slang-profile-defs.h"
+
};
const char* getStageName(Stage stage);
@@ -110,7 +111,14 @@ namespace Slang
RawVal raw = Unknown;
};
+ struct StageInfo
+ {
+ const char* name;
+ Stage stage;
+ };
+ /// Note! There can be multiple stages with the same name
+ ConstArrayView<StageInfo> getStageInfos();
Stage findStageByName(String const& name);
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 8aea1bc37..9c75aa63e 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -132,6 +132,7 @@ void Session::init()
{
SLANG_ASSERT(BaseTypeInfo::check());
+
_initCodeGenTransitionMap();
::memset(m_downstreamCompilerLocators, 0, sizeof(m_downstreamCompilerLocators));
@@ -144,6 +145,9 @@ void Session::init()
m_sharedLibraryLoader = DefaultSharedLibraryLoader::getSingleton();
+ // Set up the command line options
+ initCommandOptions(m_commandOptions);
+
// Set up shared AST builder
m_sharedASTBuilder = new SharedASTBuilder;
m_sharedASTBuilder->init(this);
diff --git a/tests/diagnostics/command-line/unknown-line-directive-mode.slang.expected b/tests/diagnostics/command-line/unknown-line-directive-mode.slang.expected
index eef953199..160d3dfc7 100644
--- a/tests/diagnostics/command-line/unknown-line-directive-mode.slang.expected
+++ b/tests/diagnostics/command-line/unknown-line-directive-mode.slang.expected
@@ -1,6 +1,6 @@
result code = 1
standard error = {
-(1): error 24: unknown '#line' directive mode 'quizzical'
+(1): error 62: unknown value for option. Valid values are 'none, source-map, default, standard, glsl'
tests/diagnostics/command-line/unknown-line-directive-mode.slang -line-directive-mode quizzical
^
}
diff --git a/tests/diagnostics/command-line/unknown-option.slang.expected b/tests/diagnostics/command-line/unknown-option.slang.expected
index 5b99ce862..8f73029c6 100644
--- a/tests/diagnostics/command-line/unknown-option.slang.expected
+++ b/tests/diagnostics/command-line/unknown-option.slang.expected
@@ -3,6 +3,9 @@ standard error = {
(1): error 17: unknown command-line option '-destroy-all-humans'
tests/diagnostics/command-line/unknown-option.slang -destroy-all-humans
^
+Usage: slangc [options...] [--] <input files>
+
+For help: slangc -h
}
standard output = {
}