summaryrefslogtreecommitdiffstats
path: root/source/slangc/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slangc/main.cpp')
-rw-r--r--source/slangc/main.cpp600
1 files changed, 37 insertions, 563 deletions
diff --git a/source/slangc/main.cpp b/source/slangc/main.cpp
index ead286434..06f004531 100644
--- a/source/slangc/main.cpp
+++ b/source/slangc/main.cpp
@@ -8,536 +8,7 @@ using namespace Slang;
#include <assert.h>
-// Currently only used for looking up `Profile::` values that aren't
-// exported by the public API
-#include "../slang/profile.h"
-
// Try to read an argument for a command-line option.
-wchar_t const* tryReadCommandLineArgumentRaw(wchar_t const* option, wchar_t***ioCursor, wchar_t**end)
-{
- wchar_t**& cursor = *ioCursor;
- if (cursor == end)
- {
- fprintf(stderr, "expected an argument for command-line option '%S'", option);
- exit(1);
- }
- else
- {
- return *cursor++;
- }
-}
-
-String tryReadCommandLineArgument(wchar_t const* option, wchar_t***ioCursor, wchar_t**end)
-{
- return String::FromWString(tryReadCommandLineArgumentRaw(option, ioCursor, end));
-}
-
-struct OptionsParser
-{
- SlangSession* session = nullptr;
- SlangCompileRequest* compileRequest = nullptr;
-
- struct RawTranslationUnit
- {
- SlangSourceLanguage sourceLanguage;
- SlangProfileID implicitProfile;
- int translationUnitIndex;
- };
-
- // Collect translation units so that we can futz with them later
- List<RawTranslationUnit> rawTranslationUnits;
-
- struct RawEntryPoint
- {
- String name;
- SlangProfileID profileID = SLANG_PROFILE_UNKNOWN;
- int translationUnitIndex = -1;
- };
-
- // Collect entry point names, so that we can associate them
- // with entry points later...
- List<RawEntryPoint> rawEntryPoints;
-
- // The number of input files that have been specified
- int inputPathCount = 0;
-
- // If we already have a translation unit for Slang code, then this will give its index.
- // If not, it will be `-1`.
- int slangTranslationUnit = -1;
-
- int translationUnitCount = 0;
- int currentTranslationUnitIndex = -1;
-
- SlangProfileID currentProfileID = SLANG_PROFILE_UNKNOWN;
-
- SlangCompileFlags flags = 0;
-
- int addTranslationUnit(
- SlangSourceLanguage language,
- SlangProfileID implicitProfile = SLANG_PROFILE_UNKNOWN)
- {
- auto translationUnitIndex = spAddTranslationUnit(compileRequest, language, nullptr);
-
- assert(translationUnitIndex == rawTranslationUnits.Count());
-
- RawTranslationUnit rawTranslationUnit;
- rawTranslationUnit.sourceLanguage = language;
- rawTranslationUnit.implicitProfile = implicitProfile;
- rawTranslationUnit.translationUnitIndex = translationUnitIndex;
-
- rawTranslationUnits.Add(rawTranslationUnit);
-
- return translationUnitIndex;
- }
-
- void addInputSlangPath(
- String const& path)
- {
- // All of the input .slang files will be grouped into a single logical translation unit,
- // which we create lazily when the first .slang file is encountered.
- if( slangTranslationUnit == -1 )
- {
- translationUnitCount++;
- slangTranslationUnit = addTranslationUnit(SLANG_SOURCE_LANGUAGE_SLANG);
- }
-
- spAddTranslationUnitSourceFile(
- compileRequest,
- slangTranslationUnit,
- path.begin());
-
- // Set the translation unit to be used by subsequent entry points
- currentTranslationUnitIndex = slangTranslationUnit;
- }
-
- void addInputForeignShaderPath(
- String const& path,
- SlangSourceLanguage language,
- SlangProfileID implicitProfile = SLANG_PROFILE_UNKNOWN)
- {
- translationUnitCount++;
- currentTranslationUnitIndex = addTranslationUnit(language, implicitProfile);
-
- spAddTranslationUnitSourceFile(
- compileRequest,
- currentTranslationUnitIndex,
- path.begin());
- }
-
- void addInputPath(
- wchar_t const* inPath)
- {
- inputPathCount++;
-
- // look at the extension on the file name to determine
- // how we should handle it.
- String path = String::FromWString(inPath);
-
- if( path.EndsWith(".slang") )
- {
- // Plain old slang code
- addInputSlangPath(path);
- }
-#define CASE(EXT, LANG) \
- else if(path.EndsWith(EXT)) do { addInputForeignShaderPath(path, SLANG_SOURCE_LANGUAGE_##LANG); } while(0)
-
- CASE(".hlsl", HLSL);
- CASE(".fx", HLSL);
-
- CASE(".glsl", GLSL);
-#undef CASE
-
-#define CASE(EXT, LANG, PROFILE) \
- else if(path.EndsWith(EXT)) do { addInputForeignShaderPath(path, SLANG_SOURCE_LANGUAGE_##LANG, SlangProfileID(Slang::Profile::PROFILE)); } while(0)
- // TODO: need a way to pass along stage/profile and entry-point info for these cases...
- CASE(".vert", GLSL, GLSL_Vertex);
- CASE(".frag", GLSL, GLSL_Fragment);
- CASE(".geom", GLSL, GLSL_Geometry);
- CASE(".tesc", GLSL, GLSL_TessControl);
- CASE(".tese", GLSL, GLSL_TessEval);
- CASE(".comp", GLSL, GLSL_Compute);
-
-#undef CASE
-
- else
- {
- fprintf(stderr, "error: can't deduce language for input file '%S'\n", inPath);
- exit(1);
- }
- }
-
- void parse(
- int argc,
- wchar_t** argv)
- {
- wchar_t** argCursor = &argv[1];
- wchar_t** argEnd = &argv[argc];
- while (argCursor != argEnd)
- {
- wchar_t const* arg = *argCursor++;
- if (arg[0] == '-')
- {
- String argStr = String::FromWString(arg);
-
- // The argument looks like an option, so try to parse it.
-// if (argStr == "-outdir")
-// outputDir = tryReadCommandLineArgument(arg, &argCursor, argEnd);
-// if (argStr == "-out")
-// options.outputName = tryReadCommandLineArgument(arg, &argCursor, argEnd);
-// else if (argStr == "-symbo")
-// options.SymbolToCompile = tryReadCommandLineArgument(arg, &argCursor, argEnd);
- //else
- if (argStr == "-no-checking")
- flags |= SLANG_COMPILE_FLAG_NO_CHECKING;
- else if (argStr == "-backend" || argStr == "-target")
- {
- String name = tryReadCommandLineArgument(arg, &argCursor, argEnd);
- SlangCompileTarget target = SLANG_TARGET_UNKNOWN;
-
- if (name == "glsl")
- {
- target = SLANG_GLSL;
- }
- else if (name == "glsl_vk")
- {
- target = SLANG_GLSL_VULKAN;
- }
-// else if (name == "glsl_vk_onedesc")
-// {
-// options.Target = CodeGenTarget::GLSL_Vulkan_OneDesc;
-// }
- else if (name == "hlsl")
- {
- target = SLANG_HLSL;
- }
- else if (name == "spriv")
- {
- target = SLANG_SPIRV;
- }
- else if (name == "dxbc")
- {
- target = SLANG_DXBC;
- }
- else if (name == "dxbc-assembly")
- {
- target = SLANG_DXBC_ASM;
- }
- #define CASE(NAME, TARGET) \
- else if(name == #NAME) do { target = SLANG_##TARGET; } while(0)
-
- CASE(spirv, SPIRV);
- CASE(spirv-assembly, SPIRV_ASM);
-
- #undef CASE
-
- else if (name == "reflection-json")
- {
- target = SLANG_REFLECTION_JSON;
- }
- else
- {
- fprintf(stderr, "unknown code generation target '%S'\n", name.ToWString());
- exit(1);
- }
-
- spSetCodeGenTarget(compileRequest, target);
- }
- // A "profile" specifies both a specific target stage and a general level
- // of capability required by the program.
- else if (argStr == "-profile")
- {
- String name = tryReadCommandLineArgument(arg, &argCursor, argEnd);
-
- SlangProfileID profileID = spFindProfile(session, name.begin());
- if( profileID == SLANG_PROFILE_UNKNOWN )
- {
- fprintf(stderr, "unknown profile '%s'\n", name.begin());
- }
- else
- {
- currentProfileID = profileID;
- }
- }
- else if (argStr == "-entry")
- {
- String name = tryReadCommandLineArgument(arg, &argCursor, argEnd);
-
- RawEntryPoint entry;
- entry.name = name;
- entry.translationUnitIndex = currentTranslationUnitIndex;
-
- // TODO(tfoley): Allow user to fold a specification of a profile into the entry-point name,
- // for the case where they might be compiling multiple entry points in one invocation...
- //
- // For now, just use the last profile set on the command-line to specify this
-
- entry.profileID = currentProfileID;
-
- rawEntryPoints.Add(entry);
- }
-#if 0
- else if (argStr == "-stage")
- {
- String name = tryReadCommandLineArgument(arg, &argCursor, argEnd);
- StageTarget stage = StageTarget::Unknown;
- if (name == "vertex") { stage = StageTarget::VertexShader; }
- else if (name == "fragment") { stage = StageTarget::FragmentShader; }
- else if (name == "hull") { stage = StageTarget::HullShader; }
- else if (name == "domain") { stage = StageTarget::DomainShader; }
- else if (name == "compute") { stage = StageTarget::ComputeShader; }
- else
- {
- fprintf(stderr, "unknown stage '%S'\n", name.ToWString());
- }
- options.stage = stage;
- }
-#endif
- else if (argStr == "-pass-through")
- {
- String name = tryReadCommandLineArgument(arg, &argCursor, argEnd);
- SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE;
- if (name == "fxc") { passThrough = SLANG_PASS_THROUGH_FXC; }
- else if (name == "glslang") { passThrough = SLANG_PASS_THROUGH_GLSLANG; }
- else
- {
- fprintf(stderr, "unknown pass-through target '%S'\n", name.ToWString());
- exit(1);
- }
-
- spSetPassThrough(
- compileRequest,
- passThrough);
- }
-// else if (argStr == "-genchoice")
-// options.Mode = CompilerMode::GenerateChoice;
- else if (argStr[1] == 'D')
- {
- // The value to be defined might be part of the same option, as in:
- // -DFOO
- // or it might come separately, as in:
- // -D FOO
- wchar_t const* defineStr = arg + 2;
- if (defineStr[0] == 0)
- {
- // Need to read another argument from the command line
- defineStr = tryReadCommandLineArgumentRaw(arg, &argCursor, argEnd);
- }
- // The string that sets up the define can have an `=` between
- // the name to be defined and its value, so we search for one.
- wchar_t const* eqPos = nullptr;
- for(wchar_t const* dd = defineStr; *dd; ++dd)
- {
- if (*dd == '=')
- {
- eqPos = dd;
- break;
- }
- }
-
- // Now set the preprocessor define
- //
- if (eqPos)
- {
- // If we found an `=`, we split the string...
-
- spAddPreprocessorDefine(
- compileRequest,
- String::FromWString(defineStr, eqPos).begin(),
- String::FromWString(eqPos+1).begin());
- }
- else
- {
- // If there was no `=`, then just #define it to an empty string
-
- spAddPreprocessorDefine(
- compileRequest,
- String::FromWString(defineStr).begin(),
- "");
- }
- }
- else if (argStr[1] == 'I')
- {
- // The value to be defined might be part of the same option, as in:
- // -IFOO
- // or it might come separately, as in:
- // -I FOO
- // (see handling of `-D` above)
- wchar_t const* includeDirStr = arg + 2;
- if (includeDirStr[0] == 0)
- {
- // Need to read another argument from the command line
- includeDirStr = tryReadCommandLineArgumentRaw(arg, &argCursor, argEnd);
- }
-
- spAddSearchPath(
- compileRequest,
- String::FromWString(includeDirStr).begin());
- }
- else if (argStr == "--")
- {
- // The `--` option causes us to stop trying to parse options,
- // and treat the rest of the command line as input file names:
- while (argCursor != argEnd)
- {
- addInputPath(*argCursor++);
- }
- break;
- }
- else
- {
- fprintf(stderr, "unknown command-line option '%S'\n", argStr.ToWString());
- // TODO: print a usage message
- exit(1);
- }
- }
- else
- {
- addInputPath(arg);
- }
- }
-
- spSetCompileFlags(compileRequest, flags);
-
- if (inputPathCount == 0)
- {
- fprintf(stderr, "error: no input file specified\n");
- exit(1);
- }
-
- // No point in moving forward if there is nothing to compile
- if( translationUnitCount == 0 )
- {
- fprintf(stderr, "error: no compilation requested\n");
- exit(1);
- }
-
- // If the user didn't list any explicit entry points, then we can
- // try to infer one from the type of input file
- if(rawEntryPoints.Count() == 0)
- {
- for(auto rawTranslationUnit : rawTranslationUnits)
- {
- // Dont' add implicit entry points when compiling from Slang files,
- // since Slang doesn't require entry points to be named on the
- // command line.
- if(rawTranslationUnit.sourceLanguage == SLANG_SOURCE_LANGUAGE_SLANG )
- continue;
-
- // Use a default entry point name
- char const* entryPointName = "main";
-
- // Try to determine a profile
- SlangProfileID entryPointProfile = SLANG_PROFILE_UNKNOWN;
-
- // If a profile was specified on the command line, then we use it
- if(currentProfileID != SLANG_PROFILE_UNKNOWN)
- {
- entryPointProfile = currentProfileID;
- }
- // Otherwise, check if the translation unit implied a profile
- // (e.g., a `*.vert` file implies the `GLSL_Vertex` profile)
- else if(rawTranslationUnit.implicitProfile != SLANG_PROFILE_UNKNOWN)
- {
- entryPointProfile = rawTranslationUnit.implicitProfile;
- }
-
- RawEntryPoint entry;
- entry.name = entryPointName;
- entry.translationUnitIndex = rawTranslationUnit.translationUnitIndex;
- entry.profileID = entryPointProfile;
- rawEntryPoints.Add(entry);
- }
- }
-
- // For any entry points that were given without an explicit profile, we can now apply
- // the profile that was given to them.
- if( rawEntryPoints.Count() != 0 )
- {
- bool anyEntryPointWithoutProfile = false;
- for( auto& entryPoint : rawEntryPoints )
- {
- // Skip entry points that are already associated with a translation unit...
- if( entryPoint.profileID != SLANG_PROFILE_UNKNOWN )
- continue;
-
- anyEntryPointWithoutProfile = true;
- break;
- }
-
- if( anyEntryPointWithoutProfile )
- {
- fprintf(stderr, "error: no profile specified; use the '-profile <profile name>' option");
- exit(1);
- }
- // TODO: issue an error if we have multiple `-profile` options *and*
- // there are entry points that didn't get a profile.
- else
- {
- for( auto& e : rawEntryPoints )
- {
- if( e.profileID == SLANG_PROFILE_UNKNOWN )
- {
- e.profileID = currentProfileID;
- }
- }
- }
- }
-
- // Next, we want to make sure that entry points get attached to the appropriate translation
- // unit that will provide them.
- {
- bool anyEntryPointWithoutTranslationUnit = false;
- for( auto& entryPoint : rawEntryPoints )
- {
- // Skip entry points that are already associated with a translation unit...
- if( entryPoint.translationUnitIndex != -1 )
- continue;
-
- anyEntryPointWithoutTranslationUnit = true;
- entryPoint.translationUnitIndex = 0;
- }
-
- if( anyEntryPointWithoutTranslationUnit && translationUnitCount != 1 )
- {
- fprintf(stderr, "error: when using multiple translation units, entry points must be specified after their translation unit file(s)");
- exit(1);
- }
-
- // Now place all those entry points where they belong
- for( auto& entryPoint : rawEntryPoints )
- {
- spAddTranslationUnitEntryPoint(
- compileRequest,
- entryPoint.translationUnitIndex,
- entryPoint.name.begin(),
- entryPoint.profileID);
- }
- }
-
-#if 0
- // Automatically derive an output directory based on the first file specified.
- //
- // TODO: require manual specification if there are multiple input files, in different directories
- String fileName = options.translationUnits[0].sourceFilePaths[0];
- if (outputDir.Length() == 0)
- {
- outputDir = Path::GetDirectoryName(fileName);
- }
-#endif
- }
-
-};
-
-
-void parseOptions(
- SlangCompileRequest* compileRequest,
- int argc,
- wchar_t** argv)
-{
- OptionsParser parser;
- parser.compileRequest = compileRequest;
- parser.parse(argc, argv);
-}
static void diagnosticCallback(
char const* message,
@@ -547,14 +18,28 @@ static void diagnosticCallback(
fflush(stderr);
}
-int wmain(int argc, wchar_t* argv[])
+#ifdef _WIN32
+#define MAIN slangc_main
+#else
+#define MAIN main
+#endif
+
+int MAIN(int argc, char** argv)
{
// Parse any command-line options
SlangSession* session = spCreateSession(nullptr);
SlangCompileRequest* compileRequest = spCreateCompileRequest(session);
- parseOptions(compileRequest, argc, argv);
+ char const* appName = "slangc";
+ if(argc > 0) appName = argv[0];
+
+ int err = spProcessCommandLineArguments(compileRequest, &argv[1], argc - 1);
+ if( err )
+ {
+ // TODO: print usage message
+ exit(1);
+ }
spSetDiagnosticCallback(
compileRequest,
@@ -604,40 +89,29 @@ int wmain(int argc, wchar_t* argv[])
}
#endif
-#if 0
- int returnValue = -1;
- {
+#ifdef _MSC_VER
+ _CrtDumpMemoryLeaks();
+#endif
+ return 0;
+}
+#ifdef _WIN32
+int wmain(int argc, wchar_t** argv)
+{
+ // Conver the wide-character Unicode arguments to UTF-8,
+ // since that is what Slang expects on the API side.
- auto sourceDir = Path::GetDirectoryName(fileName);
- CompileResult result;
- try
- {
- auto files = SlangLib::CompileShaderSourceFromFile(result, fileName, options);
- for (auto & f : files)
- {
- try
- {
- f.SaveToFile(Path::Combine(outputDir, f.MetaData.ShaderName + ".cse"));
- }
- catch (Exception &)
- {
- result.GetErrorWriter()->diagnose(CodePosition(0, 0, 0, ""), Diagnostics::cannotWriteOutputFile, Path::Combine(outputDir, f.MetaData.ShaderName + ".cse"));
- }
- }
- }
- catch (Exception & e)
- {
- printf("internal compiler error: %S\n", e.Message.ToWString());
- }
- result.PrintDiagnostics();
- if (result.GetErrorCount() == 0)
- returnValue = 0;
+ List<String> args;
+ for(int ii = 0; ii < argc; ++ii)
+ {
+ args.Add(String::FromWString(argv[ii]));
+ }
+ List<char const*> argBuffers;
+ for(int ii = 0; ii < argc; ++ii)
+ {
+ argBuffers.Add(args[ii].Buffer());
}
-#endif
-#ifdef _MSC_VER
- _CrtDumpMemoryLeaks();
+ return MAIN(argc, (char**) &argBuffers[0]);
+}
#endif
- return 0;
-} \ No newline at end of file