summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-io.h5
-rw-r--r--source/core/slang-string.h10
-rw-r--r--source/slang/compiler.h22
-rw-r--r--source/slang/options.cpp566
-rw-r--r--source/slang/parser.cpp14
-rw-r--r--source/slang/preprocessor.cpp88
-rw-r--r--source/slang/preprocessor.h24
-rw-r--r--source/slang/slang.cpp172
-rw-r--r--source/slang/slang.vcxproj1
-rw-r--r--source/slang/slang.vcxproj.filters1
-rw-r--r--source/slang/token-defs.h2
-rw-r--r--source/slangc/main.cpp600
12 files changed, 868 insertions, 637 deletions
diff --git a/source/core/slang-io.h b/source/core/slang-io.h
index 869fad873..2f140c3ad 100644
--- a/source/core/slang-io.h
+++ b/source/core/slang-io.h
@@ -20,11 +20,8 @@ namespace Slang
class Path
{
public:
-#ifdef _WIN32
- static const char PathDelimiter = '\\';
-#else
static const char PathDelimiter = '/';
-#endif
+
static String TruncateExt(const String & path);
static String ReplaceExt(const String & path, const char * newExt);
static String GetFileName(const String & path);
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index 80eb00605..448b351aa 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -141,6 +141,16 @@ namespace Slang
memcpy(buffer.Ptr(), str, length + 1);
}
}
+ String(const char* textBegin, char const* textEnd)
+ {
+ if (textBegin != textEnd)
+ {
+ length = (int)(textEnd - textBegin);
+ buffer = new char[length + 1];
+ memcpy(buffer.Ptr(), textBegin, length + 1);
+ buffer.Ptr()[length] = 0;
+ }
+ }
String(char chr)
{
if (chr)
diff --git a/source/slang/compiler.h b/source/slang/compiler.h
index f34be794b..38640fd0f 100644
--- a/source/slang/compiler.h
+++ b/source/slang/compiler.h
@@ -91,6 +91,26 @@ namespace Slang
Dictionary<String, String> preprocessorDefinitions;
};
+
+ struct SearchDirectory
+ {
+ enum Kind
+ {
+ Default,
+ AutoImport,
+ };
+
+ SearchDirectory() = default;
+ SearchDirectory(SearchDirectory const& other) = default;
+ SearchDirectory(String const& path, Kind kind)
+ : path(path)
+ , kind(kind)
+ {}
+
+ String path;
+ Kind kind;
+ };
+
class CompileOptions
{
public:
@@ -98,7 +118,7 @@ namespace Slang
CodeGenTarget Target = CodeGenTarget::Unknown;
// Directories to search for `#include` files or `import`ed modules
- List<String> SearchDirectories;
+ List<SearchDirectory> searchDirectories;
// Definitions to provide during preprocessing
Dictionary<String, String> preprocessorDefinitions;
diff --git a/source/slang/options.cpp b/source/slang/options.cpp
new file mode 100644
index 000000000..b3f0629c6
--- /dev/null
+++ b/source/slang/options.cpp
@@ -0,0 +1,566 @@
+// options.cpp
+
+// Implementation of options parsing for `slangc` command line,
+// and also for API interface that takes command-line argument strings.
+
+#include "../../slang.h"
+
+#include "profile.h"
+
+#include <assert.h>
+
+namespace Slang {
+
+char const* tryReadCommandLineArgumentRaw(char const* option, char const* const**ioCursor, char const* const*end)
+{
+ char const* const*& cursor = *ioCursor;
+ if (cursor == end)
+ {
+ fprintf(stderr, "expected an argument for command-line option '%s'", option);
+ exit(1);
+ }
+ else
+ {
+ return *cursor++;
+ }
+}
+
+String tryReadCommandLineArgument(char const* option, char const* const**ioCursor, char const* const*end)
+{
+ return String(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(
+ char const* inPath)
+ {
+ inputPathCount++;
+
+ // look at the extension on the file name to determine
+ // how we should handle it.
+ String path = String(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);
+ }
+ }
+
+ int parse(
+ int argc,
+ char const* const* argv)
+ {
+ char const* const* argCursor = &argv[0];
+ char const* const* argEnd = &argv[argc];
+ while (argCursor != argEnd)
+ {
+ char const* arg = *argCursor++;
+ if (arg[0] == '-')
+ {
+ String argStr = String(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
+ char 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.
+ char const* eqPos = nullptr;
+ for(char 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(defineStr, eqPos).begin(),
+ String(eqPos+1).begin());
+ }
+ else
+ {
+ // If there was no `=`, then just #define it to an empty string
+
+ spAddPreprocessorDefine(
+ compileRequest,
+ String(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)
+ char 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(includeDirStr).begin());
+ }
+ else if (argStr == "-auto-import-dir")
+ {
+ char const* importDirStr = tryReadCommandLineArgumentRaw(arg, &argCursor, argEnd);
+
+ spAddAutoImportPath(
+ compileRequest,
+ String(importDirStr).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);
+
+ // TODO(tfoley): This kind of validation needs to wait until
+ // after all options have been specified for API usage
+#if 0
+ 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);
+ }
+#endif
+
+ // 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
+
+ return 0;
+ }
+};
+
+
+int parseOptions(
+ SlangCompileRequest* compileRequest,
+ int argc,
+ char const* const* argv)
+{
+ OptionsParser parser;
+ parser.compileRequest = compileRequest;
+ return parser.parse(argc, argv);
+}
+
+
+} // namespace Slang
+
+SLANG_API int spProcessCommandLineArguments(
+ SlangCompileRequest* request,
+ char const* const* args,
+ int argCount)
+{
+ return Slang::parseOptions(request, argCount, args);
+}
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index b8997b1d7..fb66fbbcf 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -802,6 +802,18 @@ namespace Slang
return decl;
}
+ static RefPtr<Decl> parseAutoImportDecl(
+ Parser* parser)
+ {
+ Token importToken = parser->ReadToken(TokenType::AutoImport);
+
+ auto decl = new ImportDecl();
+ decl->nameToken = importToken;
+ decl->scope = parser->currentScope;
+
+ return decl;
+ }
+
static Token ParseDeclName(
Parser* parser)
{
@@ -2159,6 +2171,8 @@ parser->ReadToken(TokenType::Comma);
decl = parseModifierDecl(parser);
else if(parser->LookAheadToken("__import"))
decl = parseImportDecl(parser);
+ else if(parser->LookAheadToken(TokenType::AutoImport))
+ decl = parseAutoImportDecl(parser);
else if (AdvanceIf(parser, TokenType::Semicolon))
{
decl = new EmptyDecl();
diff --git a/source/slang/preprocessor.cpp b/source/slang/preprocessor.cpp
index b8a42d46d..6e0906d9e 100644
--- a/source/slang/preprocessor.cpp
+++ b/source/slang/preprocessor.cpp
@@ -16,7 +16,16 @@
// idioms for using the preprocessor, found in shader code in the wild.
-namespace Slang{
+namespace Slang {
+
+// Forward declaration for code provided in `slang.cpp`
+//
+// TODO: Need an appropriate header for this.
+String autoImportModule(
+ CompileRequest* request,
+ String const& path,
+ String const& source,
+ CodePosition const& loc);
// State of a preprocessor conditional, which can change when
// we encounter directives like `#elif` or `#endif`
@@ -170,6 +179,9 @@ struct Preprocessor
// Syntax for the program we are trying to parse
ProgramSyntaxNode* syntax;
+
+ // The over-arching compile request taht is invoking us
+ CompileRequest* compileRequest;
};
// Convenience routine to access the diagnostic sink
@@ -1475,7 +1487,6 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context)
String foundPath;
String foundSource;
-
IncludeHandler* includeHandler = context->preprocessor->includeHandler;
if (!includeHandler)
{
@@ -1483,10 +1494,17 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context)
GetSink(context)->diagnose(pathToken.Position, Diagnostics::noIncludeHandlerSpecified);
return;
}
- if (!includeHandler->TryToFindIncludeFile(path, pathIncludedFrom, &foundPath, &foundSource))
+ auto includeResult = includeHandler->TryToFindIncludeFile(path, pathIncludedFrom, &foundPath, &foundSource);
+
+ switch (includeResult)
{
+ case IncludeResult::NotFound:
+ case IncludeResult::Error:
GetSink(context)->diagnose(pathToken.Position, Diagnostics::includeFailed, path);
return;
+
+ case IncludeResult::FoundIncludeFile:
+ break;
}
// Do all checking related to the end of this directive before we push a new stream,
@@ -1494,12 +1512,50 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context)
// a switch of input stream
expectEndOfDirective(context);
- // Push the new file onto our stack of input streams
- // TODO(tfoley): check if we have made our include stack too deep
- PreprocessorInputStream* inputStream = CreateInputStreamForSource(context->preprocessor, foundSource, foundPath);
- inputStream->parent = context->preprocessor->inputStream;
- context->preprocessor->inputStream = inputStream;
-}
+ switch( includeResult )
+ {
+ case IncludeResult::FoundIncludeFile:
+ {
+ // Push the new file onto our stack of input streams
+ // TODO(tfoley): check if we have made our include stack too deep
+ PreprocessorInputStream* inputStream = CreateInputStreamForSource(context->preprocessor, foundSource, foundPath);
+ inputStream->parent = context->preprocessor->inputStream;
+ context->preprocessor->inputStream = inputStream;
+ }
+ break;
+
+ case IncludeResult::FoundAutoImportFile:
+ {
+ //
+
+ String autoImportName = autoImportModule(
+ context->preprocessor->compileRequest,
+ foundPath,
+ foundSource,
+ GetDirectiveLoc(context));
+
+ SourceTextInputStream* inputStream = new SourceTextInputStream();
+
+ Token token;
+ token.Type = TokenType::AutoImport;
+ token.Position = GetDirectiveLoc(context);
+ token.flags = 0;
+ token.Content = autoImportName;
+
+ inputStream->lexedTokens.mTokens.Add(token);
+
+ token.Type = TokenType::EndOfFile;
+ token.flags = TokenFlag::AfterWhitespace | TokenFlag::AtStartOfLine;
+ inputStream->lexedTokens.mTokens.Add(token);
+
+ inputStream->tokenReader = TokenReader(inputStream->lexedTokens);
+
+ inputStream->parent = context->preprocessor->inputStream;
+ context->preprocessor->inputStream = inputStream;
+ }
+ break;
+ }
+ }
// Handle a `#define` directive
static void HandleDefineDirective(PreprocessorDirectiveContext* context)
@@ -2007,16 +2063,18 @@ static TokenList ReadAllTokens(
}
TokenList preprocessSource(
- String const& source,
- String const& fileName,
- DiagnosticSink* sink,
- IncludeHandler* includeHandler,
- Dictionary<String, String> defines,
- ProgramSyntaxNode* syntax)
+ String const& source,
+ String const& fileName,
+ DiagnosticSink* sink,
+ IncludeHandler* includeHandler,
+ Dictionary<String, String> defines,
+ ProgramSyntaxNode* syntax,
+ CompileRequest* compileRequest)
{
Preprocessor preprocessor;
InitializePreprocessor(&preprocessor, sink);
preprocessor.syntax = syntax;
+ preprocessor.compileRequest = compileRequest;
preprocessor.includeHandler = includeHandler;
for (auto p : defines)
diff --git a/source/slang/preprocessor.h b/source/slang/preprocessor.h
index a3c5336f5..f16bc3929 100644
--- a/source/slang/preprocessor.h
+++ b/source/slang/preprocessor.h
@@ -7,14 +7,23 @@
namespace Slang {
+struct CompileRequest;
class DiagnosticSink;
class ProgramSyntaxNode;
+enum class IncludeResult
+{
+ Error,
+ NotFound,
+ FoundIncludeFile,
+ FoundAutoImportFile,
+};
+
// Callback interface for the preprocessor to use when looking
// for files in `#include` directives.
struct IncludeHandler
{
- virtual bool TryToFindIncludeFile(
+ virtual IncludeResult TryToFindIncludeFile(
String const& pathToInclude,
String const& pathIncludedFrom,
String* outFoundPath,
@@ -23,12 +32,13 @@ struct IncludeHandler
// Take a string of source code and preprocess it into a list of tokens.
TokenList preprocessSource(
- String const& source,
- String const& fileName,
- DiagnosticSink* sink,
- IncludeHandler* includeHandler,
- Dictionary<String, String> defines,
- ProgramSyntaxNode* syntax);
+ String const& source,
+ String const& fileName,
+ DiagnosticSink* sink,
+ IncludeHandler* includeHandler,
+ Dictionary<String, String> defines,
+ ProgramSyntaxNode* syntax,
+ CompileRequest* compileRequest);
} // namespace Slang
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index ce48f9032..5ec530592 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -130,9 +130,9 @@ struct CompileRequest
{
CompileRequest* request;
- List<String> searchDirs;
+ List<SearchDirectory> searchDirs;
- virtual bool TryToFindIncludeFile(
+ virtual IncludeResult TryToFindIncludeFile(
String const& pathToInclude,
String const& pathIncludedFrom,
String* outFoundPath,
@@ -146,12 +146,26 @@ struct CompileRequest
request->mDependencyFilePaths.Add(path);
- return true;
+ // HACK(tfoley): We might have found the file in the same directory,
+ // but what if this is also inside an auto-import path?
+ for (auto & dir : searchDirs)
+ {
+ // Only consider auto-import paths
+ if(dir.kind != SearchDirectory::Kind::AutoImport)
+ continue;
+
+ String otherPath = Path::Combine(dir.path, pathToInclude);
+
+ if(otherPath == path)
+ return IncludeResult::FoundAutoImportFile;
+ }
+
+ return IncludeResult::FoundIncludeFile;
}
for (auto & dir : searchDirs)
{
- path = Path::Combine(dir, pathToInclude);
+ path = Path::Combine(dir.path, pathToInclude);
if (File::Exists(path))
{
*outFoundPath = path;
@@ -159,10 +173,17 @@ struct CompileRequest
request->mDependencyFilePaths.Add(path);
- return true;
+ switch( dir.kind )
+ {
+ case SearchDirectory::Kind::Default:
+ return IncludeResult::FoundIncludeFile;
+
+ case SearchDirectory::Kind::AutoImport:
+ return IncludeResult::FoundAutoImportFile;
+ }
}
}
- return false;
+ return IncludeResult::NotFound;
}
};
@@ -205,9 +226,9 @@ struct CompileRequest
{
auto sourceFilePath = sourceFile->path;
- auto searchDirs = options.SearchDirectories;
+ auto searchDirs = options.searchDirectories;
searchDirs.Reverse();
- searchDirs.Add(Path::GetDirectoryName(sourceFilePath));
+ searchDirs.Add(SearchDirectory(Path::GetDirectoryName(sourceFilePath), SearchDirectory::Kind::Default));
searchDirs.Reverse();
includeHandler.searchDirs = searchDirs;
@@ -219,7 +240,8 @@ struct CompileRequest
mResult.GetErrorWriter(),
&includeHandler,
preprocessorDefinitions,
- translationUnitSyntax.Ptr());
+ translationUnitSyntax.Ptr(),
+ this);
parseSourceFile(
translationUnitSyntax.Ptr(),
@@ -448,6 +470,63 @@ struct CompileRequest
Dictionary<String, RefPtr<ProgramSyntaxNode>> loadedModules;
+ RefPtr<ProgramSyntaxNode> loadModule(
+ String const& name,
+ String const& path,
+ String const& source,
+ CodePosition const& loc)
+ {
+ // now we need to try compiling it, etc.
+
+ // We don't want to use the same options that the user specified
+ // for loading modules on-demand. In particular, we always want
+ // semantic checking to be enabled.
+ CompileOptions moduleOptions;
+ moduleOptions.searchDirectories = Options.searchDirectories;
+ moduleOptions.profile = Options.profile;
+
+ RefPtr<SourceFile> sourceFile = new SourceFile();
+ sourceFile->path = path;
+ sourceFile->content = source;
+
+ TranslationUnitOptions translationUnitOptions;
+ translationUnitOptions.sourceFiles.Add(sourceFile);
+
+ CompileUnit translationUnit = parseTranslationUnit(translationUnitOptions, moduleOptions);
+
+ // TODO: handle errors
+
+ checkTranslationUnit(translationUnit, moduleOptions);
+
+ // Skip code generation
+
+ //
+
+ RefPtr<ProgramSyntaxNode> moduleDecl = translationUnit.SyntaxNode;
+
+ loadedModules.Add(name, moduleDecl);
+
+ return moduleDecl;
+
+ }
+
+ String autoImportModule(
+ String const& path,
+ String const& source,
+ CodePosition const& loc)
+ {
+ // TODO: may want to have some kind of canonicalization step here
+ String name = path;
+
+ // Have we already loaded a module matching this name?
+ if (loadedModules.TryGetValue(name))
+ return name;
+
+ loadModule(name, path, source, loc);
+
+ return name;
+ }
+
RefPtr<ProgramSyntaxNode> findOrImportModule(
String const& name,
CodePosition const& loc)
@@ -486,47 +565,30 @@ struct CompileRequest
String foundPath;
String foundSource;
- bool found = includeHandler.TryToFindIncludeFile(fileName, pathIncludedFrom, &foundPath, &foundSource);
- if (!found)
+ IncludeResult includeResult = includeHandler.TryToFindIncludeFile(fileName, pathIncludedFrom, &foundPath, &foundSource);
+ switch( includeResult )
{
- this->mSink.diagnose(loc, Diagnostics::cannotFindFile, fileName);
+ case IncludeResult::NotFound:
+ case IncludeResult::Error:
+ {
+ this->mSink.diagnose(loc, Diagnostics::cannotFindFile, fileName);
+
+ loadedModules[name] = nullptr;
+ return nullptr;
+ }
+ break;
- loadedModules[name] = nullptr;
- return nullptr;
+ default:
+ break;
}
// We've found a file that we can load for the given module, so
- // now we need to try compiling it, etc.
-
- // We don't want to use the same options that the user specified
- // for loading modules on-demand. In particular, we always want
- // semantic checking to be enabled.
- CompileOptions moduleOptions;
- moduleOptions.SearchDirectories = Options.SearchDirectories;
- moduleOptions.profile = Options.profile;
-
- RefPtr<SourceFile> sourceFile = new SourceFile();
- sourceFile->path = foundPath;
- sourceFile->content = foundSource;
-
- TranslationUnitOptions translationUnitOptions;
- translationUnitOptions.sourceFiles.Add(sourceFile);
-
- CompileUnit translationUnit = parseTranslationUnit(translationUnitOptions, moduleOptions);
-
- // TODO: handle errors
-
- checkTranslationUnit(translationUnit, moduleOptions);
-
- // Skip code generation
-
- //
-
- moduleDecl = translationUnit.SyntaxNode;
-
- loadedModules.Add(name, moduleDecl);
-
- return moduleDecl;
+ // go ahead and perform the module-load action
+ return loadModule(
+ name,
+ foundPath,
+ foundSource,
+ loc);
}
};
@@ -539,6 +601,15 @@ RefPtr<ProgramSyntaxNode> findOrImportModule(
return request->findOrImportModule(name, loc);
}
+String autoImportModule(
+ CompileRequest* request,
+ String const& path,
+ String const& source,
+ CodePosition const& loc)
+{
+ return request->autoImportModule(path, source, loc);
+}
+
void Session::addBuiltinSource(
RefPtr<Scope> const& scope,
String const& path,
@@ -687,9 +758,16 @@ SLANG_API void spSetDiagnosticCallback(
SLANG_API void spAddSearchPath(
SlangCompileRequest* request,
- const char* searchDir)
+ const char* path)
+{
+ REQ(request)->Options.searchDirectories.Add(Slang::SearchDirectory(path, Slang::SearchDirectory::Kind::Default));
+}
+
+SLANG_API void spAddAutoImportPath(
+ SlangCompileRequest* request,
+ const char* path)
{
- REQ(request)->Options.SearchDirectories.Add(searchDir);
+ REQ(request)->Options.searchDirectories.Add(Slang::SearchDirectory(path, Slang::SearchDirectory::Kind::AutoImport));
}
SLANG_API void spAddPreprocessorDefine(
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index 7f5e0e32e..4a3313b07 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -193,6 +193,7 @@
<ClCompile Include="emit.cpp" />
<ClCompile Include="lexer.cpp" />
<ClCompile Include="lookup.cpp" />
+ <ClCompile Include="options.cpp" />
<ClCompile Include="parameter-binding.cpp" />
<ClCompile Include="parser.cpp" />
<ClCompile Include="preprocessor.cpp" />
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index 31c332295..7eee0643d 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -43,5 +43,6 @@
<ClCompile Include="syntax.cpp" />
<ClCompile Include="token.cpp" />
<ClCompile Include="type-layout.cpp" />
+ <ClCompile Include="options.cpp" />
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/source/slang/token-defs.h b/source/slang/token-defs.h
index f29574bbb..f7800de55 100644
--- a/source/slang/token-defs.h
+++ b/source/slang/token-defs.h
@@ -30,6 +30,8 @@ TOKEN(NewLine, "newline")
TOKEN(LineComment, "line comment")
TOKEN(BlockComment, "block comment")
+TOKEN(AutoImport, "automatic import directive")
+
#define PUNCTUATION(id, text) \
TOKEN(id, "'" text "'")
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