diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/slang-io.h | 5 | ||||
| -rw-r--r-- | source/core/slang-string.h | 10 | ||||
| -rw-r--r-- | source/slang/compiler.h | 22 | ||||
| -rw-r--r-- | source/slang/options.cpp | 566 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 14 | ||||
| -rw-r--r-- | source/slang/preprocessor.cpp | 88 | ||||
| -rw-r--r-- | source/slang/preprocessor.h | 24 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 172 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 1 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 1 | ||||
| -rw-r--r-- | source/slang/token-defs.h | 2 | ||||
| -rw-r--r-- | source/slangc/main.cpp | 600 |
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 |
