From 3e74d39f24fdfaa547ce900be177863e2bfe2dea Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Tue, 16 Oct 2018 18:49:11 -0400 Subject: Feature/include refactor (#675) * Refactor of path handling. * Added PathInfo * Changed ISlangFileSystem - such that has separate concepts of reading a file, getting a relative path and getting a canonical path * Added support for getting a canonical path for windows/linux * Made maps/testing around canonicalPaths * User output remains around 'foundPath' - which is the same as before * Small improvements around PathInfo * Added a type and make constructors to make clear the different 'path' uses * Fixed bug in findViewRecursively * Checking and reporting for ignored #pragma once. * Removed SLANG_PATH_TYPE_NONE as doesn't serve any useful purpose. * Improve comments in slang.h aroung ISlangFileSystem * Remove the need for in slang-io.cpp * Ran premake5. * Improvements and fixes around PathInfo. * Fix typo on linix GetCanonical * Make the ISlangFileSystem the same as before, and ISlangFileSystem contain the new methods. Internally it always uses the ISlangFileSystemExt, and will wrap a ISlangFileSystem with WrapFileSystem, if it is determined (via queryInterface) that it doesn't implement the full interface. --- slang.h | 66 ++++++- slang.sln | 164 ++++++++--------- source/core/core.vcxproj | 2 +- source/core/core.vcxproj.filters | 4 +- source/core/slang-io.cpp | 40 ++++- source/core/slang-io.h | 2 + source/slang/compiler.cpp | 5 +- source/slang/compiler.h | 12 +- source/slang/diagnostic-defs.h | 3 + source/slang/diagnostics.cpp | 2 +- source/slang/emit.cpp | 11 +- source/slang/include-file-system.cpp | 101 +++++++++++ source/slang/include-file-system.h | 81 +++++++++ source/slang/preprocessor.cpp | 62 ++++--- source/slang/preprocessor.h | 19 +- source/slang/slang.cpp | 196 ++++++++++++++++----- source/slang/slang.vcxproj | 12 +- source/slang/slang.vcxproj.filters | 16 +- source/slang/source-loc.cpp | 95 +++++----- source/slang/source-loc.h | 58 ++++-- tests/preprocessor/inappropriate-once.slang | 6 + .../preprocessor/inappropriate-once.slang.expected | 6 + tests/preprocessor/include-multiple.slang | 10 ++ tests/preprocessor/include-multiple.slang.expected | 9 + tools/slang-test/slang-test.vcxproj | 1 + tools/slang-test/slang-test.vcxproj.filters | 3 + tools/slang-test/unit-test-path.cpp | 26 +++ 27 files changed, 770 insertions(+), 242 deletions(-) create mode 100644 source/slang/include-file-system.cpp create mode 100644 source/slang/include-file-system.h create mode 100644 tests/preprocessor/inappropriate-once.slang create mode 100644 tests/preprocessor/inappropriate-once.slang.expected create mode 100644 tests/preprocessor/include-multiple.slang create mode 100644 tests/preprocessor/include-multiple.slang.expected create mode 100644 tools/slang-test/unit-test-path.cpp diff --git a/slang.h b/slang.h index 20334e864..4157f306d 100644 --- a/slang.h +++ b/slang.h @@ -656,12 +656,16 @@ extern "C" Slang can make use of this interface whenever it would otherwise try to load files from disk, allowing applications to hook and/or override filesystem access from the compiler. + + It is the responsibility of + the caller of any method that returns a ISlangBlob to release the blob when it is no + longer used (using 'release'). */ + struct ISlangFileSystem : public ISlangUnknown { public: /** Load a file from `path` and return a blob of its contents - @param path The path to load from, as a null-terminated UTF-8 string. @param outBlob A destination pointer to receive the blob of the file contents. @returns A `SlangResult` to indicate success or failure in loading the file. @@ -673,10 +677,68 @@ extern "C" */ virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile( char const* path, - ISlangBlob** outBlob) = 0; + ISlangBlob** outBlob) = 0; }; #define SLANG_UUID_ISlangFileSystem { 0x003A09FC, 0x3A4D, 0x4BA0, 0xAD, 0x60, 0x1F, 0xD8, 0x63, 0xA9, 0x15, 0xAB } + /* Type that identifies how a path should be interpreted */ + typedef unsigned int SlangPathType; + enum + { + SLANG_PATH_TYPE_DIRECTORY, /**< Path specified specifies a directory. */ + SLANG_PATH_TYPE_FILE, /**< Path specified is to a file. */ + }; + + /** An extended file system abstraction. + + Implementing and using this interface over ISlangFileSystem gives much more control over how paths + are managed, as well as how it is determined if two files 'are the same'. + + All paths as input char*, or output as ISlangBlobs are always encoded as UTF-8 strings. + Blobs that contain strings are always zero terminated. + */ + struct ISlangFileSystemExt : public ISlangFileSystem + { + public: + /** Get a canonical path which uniquely identifies an object of the file system. + + Given a path, returns a 'canonical' path which will return the same path for the same file/directory. + The canonical path is used to compare if two includes are the same file. + The string for the canonical path is held zero terminated in the ISlangBlob of canonicalPathOut. + + Note that a canonical path doesn't *have* to be a 'canonical' path, or a path at all + - it can just be a string that uniquely identifies a file. For example another possible mechanism + could be to store the filename combined with the file date time to uniquely identify it. + + The client must ensure the blob be released when no longer used, otherwise memory will leak. + + @param path + @param canonicalPathOut + @returns A `SlangResult` to indicate success or failure getting the canonical path. + */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanoncialPath( + const char* path, + ISlangBlob** canonicalPathOut) = 0; + + /** Get a path relative to a 'from' path. + + The client must ensure the blob be released when no longer used, otherwise memory will leak. + + @param fromPathType How to interpret the from path - as a file or a directory. + @param fromPath The from path. + @param path Path to be determined relative to the fromPath + @param pathOut Holds the string which is the relative path. The string is held in the blob zero terminated. + @returns A `SlangResult` to indicate success or failure in loading the file. + */ + virtual SLANG_NO_THROW SlangResult SLANG_MCALL calcRelativePath( + SlangPathType fromPathType, + const char* fromPath, + const char* path, + ISlangBlob** pathOut) = 0; + }; + + #define SLANG_UUID_ISlangFileSystemExt { 0x5fb632d2, 0x979d, 0x4481, { 0x9f, 0xee, 0x66, 0x3c, 0x3f, 0x14, 0x49, 0xe1 } } + /*! @brief An instance of the Slang library. */ diff --git a/slang.sln b/slang.sln index 8adbb3822..fcce05cf9 100644 --- a/slang.sln +++ b/slang.sln @@ -1,36 +1,36 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "source\core\core.vcxproj", "{F9BE7957-8399-899E-0C49-E714FDDD4B65}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello-world", "examples\hello-world\hello-world.vcxproj", "{5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "model-viewer", "examples\model-viewer\model-viewer.vcxproj", "{639B13F2-CF07-CFEC-98FB-664A0427F154}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "source\core\core.vcxproj", "{F9BE7957-8399-899E-0C49-E714FDDD4B65}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{FD47AE19-69FD-260F-F2F1-20E65EA61D13}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang", "source\slang\slang.vcxproj", "{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}" + ProjectSection(ProjectDependencies) = postProject + {66174227-8541-41FC-A6DF-4764FC66F78E} = {66174227-8541-41FC-A6DF-4764FC66F78E} + EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-generate", "tools\slang-generate\slang-generate.vcxproj", "{66174227-8541-41FC-A6DF-4764FC66F78E}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-glslang", "source\slang-glslang\slang-glslang.vcxproj", "{C495878A-832C-485B-B347-0998A90CC936}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-test", "tools\slang-test\slang-test.vcxproj", "{0C768A18-1D25-4000-9F37-DA5FE99E3B64}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slangc", "source\slangc\slangc.vcxproj", "{D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-reflection-test", "tools\slang-reflection-test\slang-reflection-test.vcxproj", "{22C45F4F-FB6B-4535-BED1-D3F5D0C71047}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{FD47AE19-69FD-260F-F2F1-20E65EA61D13}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-eval-test", "tools\slang-eval-test\slang-eval-test.vcxproj", "{205FCAB9-A13F-4980-86FA-F6221A7095EE}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfx", "tools\gfx\gfx.vcxproj", "{222F7498-B40C-4F3F-A704-DDEB91A4484A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "render-test", "tools\render-test\render-test.vcxproj", "{96610759-07B9-4EEB-A974-5C634A2E742B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfx", "tools\gfx\gfx.vcxproj", "{222F7498-B40C-4F3F-A704-DDEB91A4484A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-eval-test", "tools\slang-eval-test\slang-eval-test.vcxproj", "{205FCAB9-A13F-4980-86FA-F6221A7095EE}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slangc", "source\slangc\slangc.vcxproj", "{D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-generate", "tools\slang-generate\slang-generate.vcxproj", "{66174227-8541-41FC-A6DF-4764FC66F78E}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang", "source\slang\slang.vcxproj", "{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}" - ProjectSection(ProjectDependencies) = postProject - {66174227-8541-41FC-A6DF-4764FC66F78E} = {66174227-8541-41FC-A6DF-4764FC66F78E} - EndProjectSection +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-reflection-test", "tools\slang-reflection-test\slang-reflection-test.vcxproj", "{22C45F4F-FB6B-4535-BED1-D3F5D0C71047}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-glslang", "source\slang-glslang\slang-glslang.vcxproj", "{C495878A-832C-485B-B347-0998A90CC936}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-test", "tools\slang-test\slang-test.vcxproj", "{0C768A18-1D25-4000-9F37-DA5FE99E3B64}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -40,6 +40,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Win32.ActiveCfg = Debug|Win32 + {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Win32.Build.0 = Debug|Win32 + {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|x64.ActiveCfg = Debug|x64 + {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|x64.Build.0 = Debug|x64 + {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|Win32.ActiveCfg = Release|Win32 + {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|Win32.Build.0 = Release|Win32 + {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|x64.ActiveCfg = Release|x64 + {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|x64.Build.0 = Release|x64 {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Debug|Win32.ActiveCfg = Debug|Win32 {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Debug|Win32.Build.0 = Debug|Win32 {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA}.Debug|x64.ActiveCfg = Debug|x64 @@ -56,70 +64,6 @@ Global {639B13F2-CF07-CFEC-98FB-664A0427F154}.Release|Win32.Build.0 = Release|Win32 {639B13F2-CF07-CFEC-98FB-664A0427F154}.Release|x64.ActiveCfg = Release|x64 {639B13F2-CF07-CFEC-98FB-664A0427F154}.Release|x64.Build.0 = Release|x64 - {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Win32.ActiveCfg = Debug|Win32 - {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Win32.Build.0 = Debug|Win32 - {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|x64.ActiveCfg = Debug|x64 - {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|x64.Build.0 = Debug|x64 - {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|Win32.ActiveCfg = Release|Win32 - {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|Win32.Build.0 = Release|Win32 - {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|x64.ActiveCfg = Release|x64 - {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|x64.Build.0 = Release|x64 - {66174227-8541-41FC-A6DF-4764FC66F78E}.Debug|Win32.ActiveCfg = Debug|Win32 - {66174227-8541-41FC-A6DF-4764FC66F78E}.Debug|Win32.Build.0 = Debug|Win32 - {66174227-8541-41FC-A6DF-4764FC66F78E}.Debug|x64.ActiveCfg = Debug|x64 - {66174227-8541-41FC-A6DF-4764FC66F78E}.Debug|x64.Build.0 = Debug|x64 - {66174227-8541-41FC-A6DF-4764FC66F78E}.Release|Win32.ActiveCfg = Release|Win32 - {66174227-8541-41FC-A6DF-4764FC66F78E}.Release|Win32.Build.0 = Release|Win32 - {66174227-8541-41FC-A6DF-4764FC66F78E}.Release|x64.ActiveCfg = Release|x64 - {66174227-8541-41FC-A6DF-4764FC66F78E}.Release|x64.Build.0 = Release|x64 - {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|Win32.ActiveCfg = Debug|Win32 - {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|Win32.Build.0 = Debug|Win32 - {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|x64.ActiveCfg = Debug|x64 - {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|x64.Build.0 = Debug|x64 - {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|Win32.ActiveCfg = Release|Win32 - {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|Win32.Build.0 = Release|Win32 - {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|x64.ActiveCfg = Release|x64 - {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|x64.Build.0 = Release|x64 - {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Debug|Win32.ActiveCfg = Debug|Win32 - {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Debug|Win32.Build.0 = Debug|Win32 - {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Debug|x64.ActiveCfg = Debug|x64 - {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Debug|x64.Build.0 = Debug|x64 - {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Release|Win32.ActiveCfg = Release|Win32 - {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Release|Win32.Build.0 = Release|Win32 - {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Release|x64.ActiveCfg = Release|x64 - {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Release|x64.Build.0 = Release|x64 - {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Debug|Win32.ActiveCfg = Debug|Win32 - {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Debug|Win32.Build.0 = Debug|Win32 - {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Debug|x64.ActiveCfg = Debug|x64 - {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Debug|x64.Build.0 = Debug|x64 - {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Release|Win32.ActiveCfg = Release|Win32 - {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Release|Win32.Build.0 = Release|Win32 - {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Release|x64.ActiveCfg = Release|x64 - {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Release|x64.Build.0 = Release|x64 - {96610759-07B9-4EEB-A974-5C634A2E742B}.Debug|Win32.ActiveCfg = Debug|Win32 - {96610759-07B9-4EEB-A974-5C634A2E742B}.Debug|Win32.Build.0 = Debug|Win32 - {96610759-07B9-4EEB-A974-5C634A2E742B}.Debug|x64.ActiveCfg = Debug|x64 - {96610759-07B9-4EEB-A974-5C634A2E742B}.Debug|x64.Build.0 = Debug|x64 - {96610759-07B9-4EEB-A974-5C634A2E742B}.Release|Win32.ActiveCfg = Release|Win32 - {96610759-07B9-4EEB-A974-5C634A2E742B}.Release|Win32.Build.0 = Release|Win32 - {96610759-07B9-4EEB-A974-5C634A2E742B}.Release|x64.ActiveCfg = Release|x64 - {96610759-07B9-4EEB-A974-5C634A2E742B}.Release|x64.Build.0 = Release|x64 - {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Debug|Win32.ActiveCfg = Debug|Win32 - {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Debug|Win32.Build.0 = Debug|Win32 - {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Debug|x64.ActiveCfg = Debug|x64 - {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Debug|x64.Build.0 = Debug|x64 - {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|Win32.ActiveCfg = Release|Win32 - {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|Win32.Build.0 = Release|Win32 - {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|x64.ActiveCfg = Release|x64 - {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|x64.Build.0 = Release|x64 - {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.ActiveCfg = Debug|Win32 - {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.Build.0 = Debug|Win32 - {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|x64.ActiveCfg = Debug|x64 - {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|x64.Build.0 = Debug|x64 - {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|Win32.ActiveCfg = Release|Win32 - {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|Win32.Build.0 = Release|Win32 - {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|x64.ActiveCfg = Release|x64 - {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|x64.Build.0 = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|Win32.ActiveCfg = Debug|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|Win32.Build.0 = Debug|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|x64.ActiveCfg = Debug|x64 @@ -136,6 +80,62 @@ Global {C495878A-832C-485B-B347-0998A90CC936}.Release|Win32.Build.0 = Release|Win32 {C495878A-832C-485B-B347-0998A90CC936}.Release|x64.ActiveCfg = Release|x64 {C495878A-832C-485B-B347-0998A90CC936}.Release|x64.Build.0 = Release|x64 + {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.ActiveCfg = Debug|Win32 + {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.Build.0 = Debug|Win32 + {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|x64.ActiveCfg = Debug|x64 + {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|x64.Build.0 = Debug|x64 + {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|Win32.ActiveCfg = Release|Win32 + {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|Win32.Build.0 = Release|Win32 + {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|x64.ActiveCfg = Release|x64 + {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|x64.Build.0 = Release|x64 + {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Debug|Win32.ActiveCfg = Debug|Win32 + {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Debug|Win32.Build.0 = Debug|Win32 + {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Debug|x64.ActiveCfg = Debug|x64 + {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Debug|x64.Build.0 = Debug|x64 + {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|Win32.ActiveCfg = Release|Win32 + {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|Win32.Build.0 = Release|Win32 + {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|x64.ActiveCfg = Release|x64 + {222F7498-B40C-4F3F-A704-DDEB91A4484A}.Release|x64.Build.0 = Release|x64 + {96610759-07B9-4EEB-A974-5C634A2E742B}.Debug|Win32.ActiveCfg = Debug|Win32 + {96610759-07B9-4EEB-A974-5C634A2E742B}.Debug|Win32.Build.0 = Debug|Win32 + {96610759-07B9-4EEB-A974-5C634A2E742B}.Debug|x64.ActiveCfg = Debug|x64 + {96610759-07B9-4EEB-A974-5C634A2E742B}.Debug|x64.Build.0 = Debug|x64 + {96610759-07B9-4EEB-A974-5C634A2E742B}.Release|Win32.ActiveCfg = Release|Win32 + {96610759-07B9-4EEB-A974-5C634A2E742B}.Release|Win32.Build.0 = Release|Win32 + {96610759-07B9-4EEB-A974-5C634A2E742B}.Release|x64.ActiveCfg = Release|x64 + {96610759-07B9-4EEB-A974-5C634A2E742B}.Release|x64.Build.0 = Release|x64 + {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Debug|Win32.ActiveCfg = Debug|Win32 + {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Debug|Win32.Build.0 = Debug|Win32 + {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Debug|x64.ActiveCfg = Debug|x64 + {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Debug|x64.Build.0 = Debug|x64 + {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Release|Win32.ActiveCfg = Release|Win32 + {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Release|Win32.Build.0 = Release|Win32 + {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Release|x64.ActiveCfg = Release|x64 + {205FCAB9-A13F-4980-86FA-F6221A7095EE}.Release|x64.Build.0 = Release|x64 + {66174227-8541-41FC-A6DF-4764FC66F78E}.Debug|Win32.ActiveCfg = Debug|Win32 + {66174227-8541-41FC-A6DF-4764FC66F78E}.Debug|Win32.Build.0 = Debug|Win32 + {66174227-8541-41FC-A6DF-4764FC66F78E}.Debug|x64.ActiveCfg = Debug|x64 + {66174227-8541-41FC-A6DF-4764FC66F78E}.Debug|x64.Build.0 = Debug|x64 + {66174227-8541-41FC-A6DF-4764FC66F78E}.Release|Win32.ActiveCfg = Release|Win32 + {66174227-8541-41FC-A6DF-4764FC66F78E}.Release|Win32.Build.0 = Release|Win32 + {66174227-8541-41FC-A6DF-4764FC66F78E}.Release|x64.ActiveCfg = Release|x64 + {66174227-8541-41FC-A6DF-4764FC66F78E}.Release|x64.Build.0 = Release|x64 + {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Debug|Win32.ActiveCfg = Debug|Win32 + {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Debug|Win32.Build.0 = Debug|Win32 + {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Debug|x64.ActiveCfg = Debug|x64 + {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Debug|x64.Build.0 = Debug|x64 + {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Release|Win32.ActiveCfg = Release|Win32 + {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Release|Win32.Build.0 = Release|Win32 + {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Release|x64.ActiveCfg = Release|x64 + {22C45F4F-FB6B-4535-BED1-D3F5D0C71047}.Release|x64.Build.0 = Release|x64 + {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|Win32.ActiveCfg = Debug|Win32 + {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|Win32.Build.0 = Debug|Win32 + {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|x64.ActiveCfg = Debug|x64 + {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|x64.Build.0 = Debug|x64 + {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|Win32.ActiveCfg = Release|Win32 + {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|Win32.Build.0 = Release|Win32 + {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|x64.ActiveCfg = Release|x64 + {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -143,11 +143,11 @@ Global GlobalSection(NestedProjects) = preSolution {5CF41E7B-4883-A844-F1A1-BC3FDD0FB9EA} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} {639B13F2-CF07-CFEC-98FB-664A0427F154} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231} + {222F7498-B40C-4F3F-A704-DDEB91A4484A} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} + {96610759-07B9-4EEB-A974-5C634A2E742B} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} + {205FCAB9-A13F-4980-86FA-F6221A7095EE} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {66174227-8541-41FC-A6DF-4764FC66F78E} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} - {0C768A18-1D25-4000-9F37-DA5FE99E3B64} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {22C45F4F-FB6B-4535-BED1-D3F5D0C71047} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} - {205FCAB9-A13F-4980-86FA-F6221A7095EE} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} - {96610759-07B9-4EEB-A974-5C634A2E742B} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} - {222F7498-B40C-4F3F-A704-DDEB91A4484A} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} + {0C768A18-1D25-4000-9F37-DA5FE99E3B64} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} EndGlobalSection EndGlobal diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj index 2d0c67342..d1ed4715f 100644 --- a/source/core/core.vcxproj +++ b/source/core/core.vcxproj @@ -213,7 +213,7 @@ - + diff --git a/source/core/core.vcxproj.filters b/source/core/core.vcxproj.filters index 1548217b4..7936ca11b 100644 --- a/source/core/core.vcxproj.filters +++ b/source/core/core.vcxproj.filters @@ -130,8 +130,8 @@ - + Source Files - + \ No newline at end of file diff --git a/source/core/slang-io.cpp b/source/core/slang-io.cpp index 24d5aa412..ab093f577 100644 --- a/source/core/slang-io.cpp +++ b/source/core/slang-io.cpp @@ -1,13 +1,20 @@ #include "slang-io.h" #include "exception.h" + #ifndef __STDC__ -#define __STDC__ 1 +# define __STDC__ 1 #endif + #include + #ifdef _WIN32 -#include +# include #endif +#include /* PATH_MAX */ +#include +#include + namespace Slang { bool File::Exists(const String & fileName) @@ -125,6 +132,32 @@ namespace Slang #endif } + /* static */SlangResult Path::GetCanonical(const String & path, String & canonicalPathOut) + { +#if defined(_WIN32) + // https://msdn.microsoft.com/en-us/library/506720ff.aspx + wchar_t* absPath = ::_wfullpath(nullptr, path.ToWString(), 0); + if (!absPath) + { + return SLANG_FAIL; + } + + canonicalPathOut = String::FromWString(absPath); + ::free(absPath); + return SLANG_OK; +#else + // http://man7.org/linux/man-pages/man3/realpath.3.html + char* canonicalPath = ::realpath(path.begin(), nullptr); + if (canonicalPath) + { + canonicalPathOut = canonicalPath; + ::free(canonicalPath); + return SLANG_OK; + } + return SLANG_FAIL; +#endif + } + Slang::String File::ReadAllText(const Slang::String & fileName) { StreamReader reader(new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite)); @@ -152,4 +185,7 @@ namespace Slang StreamWriter writer(new FileStream(fileName, FileMode::Create)); writer.Write(text); } + + } + diff --git a/source/core/slang-io.h b/source/core/slang-io.h index 2f140c3ad..543b12adf 100644 --- a/source/core/slang-io.h +++ b/source/core/slang-io.h @@ -31,6 +31,8 @@ namespace Slang static String Combine(const String & path1, const String & path2); static String Combine(const String & path1, const String & path2, const String & path3); static bool CreateDir(const String & path); + + static SlangResult GetCanonical(const String & path, String& canonicalPathOut); }; } diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp index 7a5501a23..4b7fcb554 100644 --- a/source/slang/compiler.cpp +++ b/source/slang/compiler.cpp @@ -186,7 +186,10 @@ namespace Slang for(auto sourceFile : translationUnit->sourceFiles) { codeBuilder << "#line 1 \""; - for(auto c : sourceFile->path) + + const String& path = sourceFile->pathInfo.foundPath; + + for(auto c : path) { char buffer[] = { c, 0 }; switch(c) diff --git a/source/slang/compiler.h b/source/slang/compiler.h index a83d8b334..04508837f 100644 --- a/source/slang/compiler.h +++ b/source/slang/compiler.h @@ -13,6 +13,7 @@ namespace Slang { + struct PathInfo; struct IncludeHandler; class CompileRequest; class ProgramLayout; @@ -395,6 +396,11 @@ namespace Slang /// ComPtr fileSystem; + /// The extended file system implementation. Will be set to a default implementation + /// if fileSystem is nullptr. Otherwise it will either be fileSystem's interface, + /// or a wrapped impl that makes fileSystem operate as fileSystemExt + ComPtr fileSystemExt; + /// Load a file into memory using the configured file system. /// /// @param path The path to attempt to load from @@ -454,14 +460,14 @@ namespace Slang RefPtr loadModule( Name* name, - String const& path, - ISlangBlob* sourceBlob, + const PathInfo& filePathInfo, + ISlangBlob* fileContentsBlob, SourceLoc const& loc); void loadParsedModule( RefPtr const& translationUnit, Name* name, - String const& path); + PathInfo const& pathInfo); RefPtr findOrImportModule( Name* name, diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h index 1df7e23f5..89db9d0e3 100644 --- a/source/slang/diagnostic-defs.h +++ b/source/slang/diagnostic-defs.h @@ -125,6 +125,8 @@ DIAGNOSTIC(-1, Note, seeOpeningToken, "see opening '$0'") DIAGNOSTIC(15300, Error, includeFailed, "failed to find include file '$0'") DIAGNOSTIC(15301, Error, importFailed, "failed to find imported file '$0'") DIAGNOSTIC(-1, Error, noIncludeHandlerSpecified, "no `#include` handler was specified") +DIAGNOSTIC(15302, Error, noCanonicalPath, "`#include` handler didn't generate a canonical path for '$0'") + // 154xx - macro definition DIAGNOSTIC(15400, Warning, macroRedefinition, "redefinition of macro '$0'") @@ -138,6 +140,7 @@ DIAGNOSTIC(15501, Error, wrongNumberOfArgumentsToMacro, "wrong number of argumen // 156xx - pragmas DIAGNOSTIC(15600, Error, expectedPragmaDirectiveName, "expected a name after '#pragma'") DIAGNOSTIC(15601, Warning, unknownPragmaDirectiveIgnored, "ignoring unknown directive '#pragma $0'") +DIAGNOSTIC(15602, Warning, pragmaOnceIgnored, "pragma once was ignored - this is typically because is not placed in an include") // 159xx - user-defined error/warning DIAGNOSTIC(15900, Error, userDefinedError, "#error: $0") diff --git a/source/slang/diagnostics.cpp b/source/slang/diagnostics.cpp index ff664e81b..c4638f29a 100644 --- a/source/slang/diagnostics.cpp +++ b/source/slang/diagnostics.cpp @@ -171,7 +171,7 @@ static void formatDiagnostic( auto humaneLoc = sourceManager->getHumaneLoc(diagnostic.loc); - sb << humaneLoc.path; + sb << humaneLoc.pathInfo.foundPath; sb << "("; sb << humaneLoc.line; sb << "): "; diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index c306186fb..4961518f1 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -541,7 +541,7 @@ struct EmitVisitor emitRawText(buffer); // Only emit the path part of a `#line` directive if needed - if(sourceLocation.path != context->shared->loc.path) + if(sourceLocation.pathInfo.foundPath != context->shared->loc.pathInfo.foundPath) { emitRawText(" "); @@ -578,7 +578,7 @@ struct EmitVisitor if(shouldUseGLSLStyleLineDirective) { - auto path = sourceLocation.path; + auto path = sourceLocation.pathInfo.foundPath; // GLSL doesn't support the traditional form of a `#line` directive without // an extension. Rather than depend on that extension we will output @@ -609,7 +609,8 @@ struct EmitVisitor // in a module that tracks source files. emitRawText("\""); - for(auto c : sourceLocation.path) + const auto& path = sourceLocation.pathInfo.foundPath; + for(auto c : path) { char charBuffer[] = { c, 0 }; switch(c) @@ -673,7 +674,7 @@ struct EmitVisitor // a differnet file or line, *or* if the source location is // somehow later on the line than what we want to emit, // then we need to emit a new `#line` directive. - if(sourceLocation.path != context->shared->loc.path + if(sourceLocation.pathInfo.foundPath != context->shared->loc.pathInfo.foundPath || sourceLocation.line != context->shared->loc.line || sourceLocation.column < context->shared->loc.column) { @@ -682,7 +683,7 @@ struct EmitVisitor // to get us caught up. enum { kSmallLineCount = 3 }; auto lineDiff = sourceLocation.line - context->shared->loc.line; - if(sourceLocation.path == context->shared->loc.path + if(sourceLocation.pathInfo.foundPath == context->shared->loc.pathInfo.foundPath && sourceLocation.line > context->shared->loc.line && lineDiff <= kSmallLineCount) { diff --git a/source/slang/include-file-system.cpp b/source/slang/include-file-system.cpp new file mode 100644 index 000000000..d2c1670fe --- /dev/null +++ b/source/slang/include-file-system.cpp @@ -0,0 +1,101 @@ +#include "include-file-system.h" + +#include "../../slang-com-ptr.h" +#include "../core/slang-io.h" +#include "compiler.h" + +namespace Slang +{ + +// Allocate static const storage for the various interface IDs that the Slang API needs to expose +static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown; +static const Guid IID_ISlangFileSystem = SLANG_UUID_ISlangFileSystem; +static const Guid IID_ISlangFileSystemExt = SLANG_UUID_ISlangFileSystemExt; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!! IncludeFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +/* static */ISlangFileSystemExt* IncludeFileSystem::getDefault() +{ + static IncludeFileSystem s_includeFileSystem; + s_includeFileSystem.ensureRef(); + return &s_includeFileSystem; +} + +ISlangUnknown* IncludeFileSystem::getInterface(const Guid& guid) +{ + return (guid == IID_ISlangUnknown || guid == IID_ISlangFileSystem || guid == IID_ISlangFileSystemExt) ? static_cast(this) : nullptr; +} + +SlangResult IncludeFileSystem::getCanoncialPath(const char* path, ISlangBlob** canonicalPathOut) +{ + String canonicalPath; + SLANG_RETURN_ON_FAIL(Path::GetCanonical(path, canonicalPath)); + + *canonicalPathOut = createStringBlob(canonicalPath).detach(); + return SLANG_OK; +} + +SlangResult IncludeFileSystem::calcRelativePath(SlangPathType fromPathType, const char* fromPath, const char* path, ISlangBlob** pathOut) +{ + String relPath; + switch (fromPathType) + { + case SLANG_PATH_TYPE_FILE: + { + relPath = Path::Combine(Path::GetDirectoryName(fromPath), path); + break; + } + case SLANG_PATH_TYPE_DIRECTORY: + { + relPath = Path::Combine(fromPath, path); + break; + } + } + + *pathOut = createStringBlob(relPath).detach(); + return SLANG_OK; +} + +SlangResult SLANG_MCALL IncludeFileSystem::loadFile(char const* path, ISlangBlob** outBlob) +{ + // Otherwise, fall back to a default implementation that uses the `core` + // libraries facilities for talking to the OS filesystem. + // + // TODO: we might want to conditionally compile these in, so that + // a user could create a build of Slang that doesn't include any OS + // filesystem calls. + // + + if (!File::Exists(path)) + { + return SLANG_E_NOT_FOUND; + } + + try + { + String sourceString = File::ReadAllText(path); + ComPtr sourceBlob = createStringBlob(sourceString); + *outBlob = sourceBlob.detach(); + return SLANG_OK; + } + catch (...) + { + } + return SLANG_E_CANNOT_OPEN; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!! WrapFileSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +SlangResult SLANG_MCALL WrapFileSystem::loadFile(char const* path, ISlangBlob** outBlob) +{ + return m_fileSystem->loadFile(path, outBlob); +} + +SlangResult WrapFileSystem::getCanoncialPath(const char* path, ISlangBlob** canonicalPathOut) +{ + String canonicalPath(path); + *canonicalPathOut = createStringBlob(canonicalPath).detach(); + return SLANG_OK; +} + +} \ No newline at end of file diff --git a/source/slang/include-file-system.h b/source/slang/include-file-system.h new file mode 100644 index 000000000..4b34c2a0f --- /dev/null +++ b/source/slang/include-file-system.h @@ -0,0 +1,81 @@ +#ifndef SLANG_INCLUDE_FILE_SYSTEM_H_INCLUDED +#define SLANG_INCLUDE_FILE_SYSTEM_H_INCLUDED + +#include "../../slang.h" +#include "../../slang-com-helper.h" +#include "../../slang-com-ptr.h" + +namespace Slang +{ + +class IncludeFileSystem : public ISlangFileSystemExt +{ +public: + // ISlangUnknown + SLANG_IUNKNOWN_ALL + + // ISlangFileSystem + virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile( + char const* path, + ISlangBlob** outBlob) SLANG_OVERRIDE; + + // ISlangFileSystemExt + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanoncialPath( + const char* path, + ISlangBlob** canonicalPathOut) SLANG_OVERRIDE; + + virtual SLANG_NO_THROW SlangResult SLANG_MCALL calcRelativePath( + SlangPathType fromPathType, + const char* fromPath, + const char* path, + ISlangBlob** pathOut) SLANG_OVERRIDE; + + /// Get a default instance + static ISlangFileSystemExt* getDefault(); + +protected: + + /// If no ref, add one to the ref + void ensureRef() { m_refCount += (m_refCount == 0); } + + ISlangUnknown* getInterface(const Guid& guid); + uint32_t m_refCount = 0; +}; + +/* Wraps an ISlangFileSystem, and provides the extra methods required to make a ISlangFileSystemExt +interface, deferring to the contained file system to do reading. + +NOTE! That this behavior is the same as previously in that.... +1) getRelativePath, just returns the path as processed by the Path:: methods +2) getCanonicalPath, just returns the input path as the 'canonical' path. This will be wrong with a file multiply referenced through paths with .. and or . but +doing it this way means it works as before and requires no new functions. +*/ +class WrapFileSystem : public IncludeFileSystem +{ +public: + // So we don't need virtual dtor + SLANG_IUNKNOWN_RELEASE + + // ISlangFileSystem + virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadFile( + char const* path, + ISlangBlob** outBlob) SLANG_OVERRIDE; + + // ISlangFileSystemExt + virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCanoncialPath( + const char* path, + ISlangBlob** canonicalPathOut) SLANG_OVERRIDE; + + /// Ctor + WrapFileSystem(ISlangFileSystem* fileSystem): + m_fileSystem(fileSystem) + { + } + +protected: + ComPtr m_fileSystem; ///< The wrapped file system +}; + +} + +#endif \ No newline at end of file diff --git a/source/slang/preprocessor.cpp b/source/slang/preprocessor.cpp index 98347bd0c..ca11b24e8 100644 --- a/source/slang/preprocessor.cpp +++ b/source/slang/preprocessor.cpp @@ -838,7 +838,9 @@ top: SourceManager* sourceManager = preprocessor->getCompileRequest()->getSourceManager(); // We create a dummy file to represent the token-paste operation - SourceFile* sourceFile = sourceManager->createSourceFile("token paste", sb.ProduceString()); + PathInfo pathInfo = PathInfo::makeTokenPaste(); + + SourceFile* sourceFile = sourceManager->createSourceFile(pathInfo, sb.ProduceString()); SourceView* sourceView = sourceManager->createSourceView(sourceFile); @@ -1589,10 +1591,8 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context) auto directiveLoc = GetDirectiveLoc(context); - String pathIncludedFrom = context->preprocessor->translationUnit->compileRequest->getSourceManager()->getPath(directiveLoc, SourceLocType::Actual); - String foundPath; - ComPtr foundSourceBlob; - + PathInfo includedFromPathInfo = context->preprocessor->translationUnit->compileRequest->getSourceManager()->getPathInfo(directiveLoc, SourceLocType::Actual); + IncludeHandler* includeHandler = context->preprocessor->includeHandler; if (!includeHandler) { @@ -1600,17 +1600,20 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context) GetSink(context)->diagnose(pathToken.loc, Diagnostics::noIncludeHandlerSpecified); return; } - auto includeResult = includeHandler->TryToFindIncludeFile(path, pathIncludedFrom, &foundPath, foundSourceBlob.writeRef()); - - switch (includeResult) + + /* Find the path relative to the foundPath */ + PathInfo filePathInfo; + if (SLANG_FAILED(includeHandler->findFile(path, includedFromPathInfo.foundPath, filePathInfo))) { - case IncludeResult::NotFound: - case IncludeResult::Error: GetSink(context)->diagnose(pathToken.loc, Diagnostics::includeFailed, path); return; + } - case IncludeResult::Found: - break; + // We must have a canonical path to be compare + if (!filePathInfo.hasCanonicalPath()) + { + GetSink(context)->diagnose(pathToken.loc, Diagnostics::noCanonicalPath, path); + return; } // Do all checking related to the end of this directive before we push a new stream, @@ -1619,7 +1622,7 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context) expectEndOfDirective(context); // Check whether we've previously included this file and seen a `#pragma once` directive - if(context->preprocessor->pragmaOncePaths.Contains(foundPath)) + if(context->preprocessor->pragmaOncePaths.Contains(filePathInfo.canonicalPath)) { return; } @@ -1629,12 +1632,19 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context) auto sourceManager = context->preprocessor->getCompileRequest()->getSourceManager(); // See if this an already loaded source file - SourceFile* sourceFile = sourceManager->findSourceFile(foundPath); + SourceFile* sourceFile = sourceManager->findSourceFileRecursively(filePathInfo.canonicalPath); // If not create a new one, and add to the list of known source files if (!sourceFile) { - sourceFile = sourceManager->createSourceFile(foundPath, foundSourceBlob); - sourceManager->addSourceFile(foundPath, sourceFile); + ComPtr foundSourceBlob; + if (SLANG_FAILED(includeHandler->readFile(filePathInfo.canonicalPath, foundSourceBlob.writeRef()))) + { + GetSink(context)->diagnose(pathToken.loc, Diagnostics::includeFailed, path); + return; + } + + sourceFile = sourceManager->createSourceFile(filePathInfo, foundSourceBlob); + sourceManager->addSourceFile(filePathInfo.canonicalPath, sourceFile); } // This is a new parse (even if it's a pre-existing source file), so create a new SourceUnit @@ -1822,7 +1832,7 @@ static void HandleLineDirective(PreprocessorDirectiveContext* context) String file; if (PeekTokenType(context) == TokenType::EndOfDirective) { - file = sourceManager->getPath(directiveLoc); + file = sourceManager->getPathInfo(directiveLoc).foundPath; } else if (PeekTokenType(context) == TokenType::StringLiteral) { @@ -1875,9 +1885,16 @@ SLANG_PRAGMA_DIRECTIVE_CALLBACK(handlePragmaOnceDirective) // trivial cases of the "same" path. // auto directiveLoc = GetDirectiveLoc(context); - auto pathIssuedFrom = context->preprocessor->translationUnit->compileRequest->getSourceManager()->getPath(directiveLoc, SourceLocType::Actual); + auto issuedFromPathInfo = context->preprocessor->translationUnit->compileRequest->getSourceManager()->getPathInfo(directiveLoc, SourceLocType::Actual); + + // Must have a canonical path for a #pragma once to work + if (!issuedFromPathInfo.hasCanonicalPath()) + { + GetSink(context)->diagnose(subDirectiveToken, Diagnostics::pragmaOnceIgnored); + return; + } - context->preprocessor->pragmaOncePaths.Add(pathIssuedFrom); + context->preprocessor->pragmaOncePaths.Add(issuedFromPathInfo.canonicalPath); } // Information about a specific `#pragma` directive @@ -2243,13 +2260,14 @@ static void DefineMacro( String const& key, String const& value) { - String fileName = "command line"; + PathInfo pathInfo = PathInfo::makeCommandLine(); + PreprocessorMacro* macro = CreateMacro(preprocessor); auto sourceManager = preprocessor->translationUnit->compileRequest->getSourceManager(); - SourceFile* keyFile = sourceManager->createSourceFile(fileName, key); - SourceFile* valueFile = sourceManager->createSourceFile(fileName, value); + SourceFile* keyFile = sourceManager->createSourceFile(pathInfo, key); + SourceFile* valueFile = sourceManager->createSourceFile(pathInfo, value); SourceView* keyView = sourceManager->createSourceView(keyFile); SourceView* valueView = sourceManager->createSourceView(valueFile); diff --git a/source/slang/preprocessor.h b/source/slang/preprocessor.h index 0e30038bf..42a3b25f4 100644 --- a/source/slang/preprocessor.h +++ b/source/slang/preprocessor.h @@ -11,22 +11,17 @@ class DiagnosticSink; class ModuleDecl; class TranslationUnitRequest; -enum class IncludeResult -{ - Error, - NotFound, - Found, -}; - // Callback interface for the preprocessor to use when looking // for files in `#include` directives. struct IncludeHandler { - virtual IncludeResult TryToFindIncludeFile( - String const& pathToInclude, - String const& pathIncludedFrom, - String* outFoundPath, - ISlangBlob** outFoundSourceBlob) = 0; + + virtual SlangResult findFile(const String& pathToInclude, + const String& pathIncludedFrom, + PathInfo& pathInfoOut) = 0; + + virtual SlangResult readFile(const String& path, + ISlangBlob** blobOut) = 0; }; // Take a string of source code and preprocess it into a list of tokens. diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index c04aaa044..908bee283 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -9,6 +9,8 @@ #include "syntax-visitors.h" #include "../slang/type-layout.h" +#include "include-file-system.h" + #include "ir-serialize.h" // Used to print exception type names in internal-compiler-error messages @@ -75,39 +77,106 @@ Session::Session() addBuiltinSource(hlslLanguageScope, "hlsl", getHLSLLibraryCode()); } +static String getString(ISlangBlob* blob) +{ + if (blob) + { + size_t size = blob->getBufferSize(); + if (size > 0) + { + const char* contents = (const char*)blob->getBufferPointer(); + // Check it has terminating 0, if not we must construct as if it does + if (contents[size - 1] == 0) + { + size --; + } + return String(contents, contents + size); + } + } + return String(); +} + struct IncludeHandlerImpl : IncludeHandler { CompileRequest* request; - virtual IncludeResult TryToFindIncludeFile( + ISlangFileSystemExt* _getFileSystemExt() + { + return request->fileSystemExt; + } + + SlangResult _findFile(SlangPathType fromPathType, const String& fromPath, const String& path, PathInfo& pathInfoOut) + { + ISlangFileSystemExt* fileSystemExt = _getFileSystemExt(); + + // Get relative path + ComPtr relPathBlob; + SLANG_RETURN_ON_FAIL(fileSystemExt->calcRelativePath(fromPathType, fromPath.begin(), path.begin(), relPathBlob.writeRef())); + String relPath(getString(relPathBlob)); + if (relPath.Length() <= 0) + { + return SLANG_FAIL; + } + + // Get the canonical path + ComPtr canonicalPathBlob; + SLANG_RETURN_ON_FAIL(fileSystemExt->getCanoncialPath(relPath.begin(), canonicalPathBlob.writeRef())); + + // If the rel path exists -> the canonical path MUST exists too + String canonicalPath(getString(canonicalPathBlob)); + if (canonicalPath.Length() <= 0) + { + // Canonical path can't be empty + return SLANG_FAIL; + } + + pathInfoOut.type = PathInfo::Type::Normal; + pathInfoOut.foundPath = relPath; + pathInfoOut.canonicalPath = canonicalPath; + return SLANG_OK; + } + + virtual SlangResult findFile( String const& pathToInclude, String const& pathIncludedFrom, - String* outFoundPath, - ISlangBlob** outFoundSourceBlob) override + PathInfo& pathInfoOut) override { - String path = Path::Combine(Path::GetDirectoryName(pathIncludedFrom), pathToInclude); + pathInfoOut.type = PathInfo::Type::Unknown; - if(SLANG_SUCCEEDED(request->loadFile(path, outFoundSourceBlob))) + // Try just relative to current path { - *outFoundPath = path; - request->mDependencyFilePaths.Add(path); - return IncludeResult::Found; + SlangResult res = _findFile(SLANG_PATH_TYPE_FILE, pathIncludedFrom, pathToInclude, pathInfoOut); + // It either succeeded or wasn't found, anything else is a failure passed back + if (SLANG_SUCCEEDED(res) || res != SLANG_E_NOT_FOUND) + { + return res; + } } + // Search all the searchDirectories for (auto & dir : request->searchDirectories) { - path = Path::Combine(dir.path, pathToInclude); - - if(SLANG_SUCCEEDED(request->loadFile(path, outFoundSourceBlob))) + SlangResult res = _findFile(SLANG_PATH_TYPE_DIRECTORY, dir.path, pathToInclude, pathInfoOut); + if (SLANG_SUCCEEDED(res) || res != SLANG_E_NOT_FOUND) { - *outFoundPath = path; - request->mDependencyFilePaths.Add(path); - return IncludeResult::Found; + return res; } } - return IncludeResult::NotFound; + return SLANG_E_NOT_FOUND; + } + + virtual SlangResult readFile(const String& path, + ISlangBlob** blobOut) override + { + ISlangFileSystem* fileSystemExt = _getFileSystemExt(); + SLANG_RETURN_ON_FAIL(fileSystemExt->loadFile(path.begin(), blobOut)); + + request->mDependencyFilePaths.Add(path); + + return SLANG_OK; } + }; // @@ -244,6 +313,10 @@ CompileRequest::CompileRequest(Session* session) setSourceManager(&sourceManagerStorage); sourceManager->initialize(session->getBuiltinSourceManager()); + + // Set up the default file system + SLANG_ASSERT(fileSystem == nullptr); + fileSystemExt = IncludeFileSystem::getDefault(); } CompileRequest::~CompileRequest() @@ -376,7 +449,7 @@ RefPtr CompileRequest::parseTypeString(TranslationUnitRequest * translatio SourceManager localSourceManager; localSourceManager.initialize(sourceManager); - Slang::RefPtr srcFile(localSourceManager.createSourceFile(String("type string"), typeStr)); + Slang::RefPtr srcFile(localSourceManager.createSourceFile(PathInfo::makeTypeParse(), typeStr)); // We'll use a temporary diagnostic sink DiagnosticSink sink; @@ -706,7 +779,8 @@ void CompileRequest::addTranslationUnitSourceBlob( String const& path, ISlangBlob* sourceBlob) { - RefPtr sourceFile = getSourceManager()->createSourceFile(path, sourceBlob); + PathInfo pathInfo = PathInfo::makePath(path); + RefPtr sourceFile = getSourceManager()->createSourceFile(pathInfo, sourceBlob); addTranslationUnitSourceFile(translationUnitIndex, sourceFile); } @@ -716,7 +790,8 @@ void CompileRequest::addTranslationUnitSourceString( String const& path, String const& source) { - RefPtr sourceFile = getSourceManager()->createSourceFile(path, source); + PathInfo pathInfo = PathInfo::makePath(path); + RefPtr sourceFile = getSourceManager()->createSourceFile(pathInfo, source); addTranslationUnitSourceFile(translationUnitIndex, sourceFile); } @@ -789,13 +864,18 @@ UInt CompileRequest::addTarget( void CompileRequest::loadParsedModule( RefPtr const& translationUnit, Name* name, - String const& path) + const PathInfo& pathInfo) { // Note: we add the loaded module to our name->module listing // before doing semantic checking, so that if it tries to // recursively `import` itself, we can detect it. RefPtr loadedModule = new LoadedModule(); - mapPathToLoadedModule.Add(path, loadedModule); + + // Get a path + String mostUniquePath = pathInfo.getMostUniquePath(); + SLANG_ASSERT(mostUniquePath.Length() > 0); + + mapPathToLoadedModule.Add(mostUniquePath, loadedModule); mapNameToLoadedModules.Add(name, loadedModule); int errorCountBefore = mSink.GetErrorCount(); @@ -821,8 +901,8 @@ void CompileRequest::loadParsedModule( RefPtr CompileRequest::loadModule( Name* name, - String const& path, - ISlangBlob* sourceBlob, + const PathInfo& filePathInfo, + ISlangBlob* sourceBlob, SourceLoc const& srcLoc) { RefPtr translationUnit = new TranslationUnitRequest(); @@ -835,11 +915,11 @@ RefPtr CompileRequest::loadModule( // TODO: decide which options, if any, should be inherited. translationUnit->compileFlags = 0; - RefPtr sourceFile = getSourceManager()->createSourceFile(path, sourceBlob); + // Create with the 'friendly' name + RefPtr sourceFile = getSourceManager()->createSourceFile(filePathInfo, sourceBlob); translationUnit->sourceFiles.Add(sourceFile); - int errorCountBefore = mSink.GetErrorCount(); parseTranslationUnit(translationUnit.Ptr()); int errorCountAfter = mSink.GetErrorCount(); @@ -854,7 +934,7 @@ RefPtr CompileRequest::loadModule( loadParsedModule( translationUnit, name, - path); + filePathInfo); errorCountAfter = mSink.GetErrorCount(); @@ -891,7 +971,7 @@ RefPtr CompileRequest::findOrImportModule( } // Derive a file name for the module, by taking the given - // identifier, replacing all occurences of `_` with `-`, + // identifier, replacing all occurrences of `_` with `-`, // and then appending `.slang`. // // For example, `foo_bar` becomes `foo-bar.slang`. @@ -914,39 +994,37 @@ RefPtr CompileRequest::findOrImportModule( IncludeHandlerImpl includeHandler; includeHandler.request = this; - // Get the original path - String pathIncludedFrom= getSourceManager()->getPath(loc, SourceLocType::Actual); - - String foundPath; - ComPtr foundSourceBlob; - IncludeResult includeResult = includeHandler.TryToFindIncludeFile(fileName, pathIncludedFrom, &foundPath, foundSourceBlob.writeRef()); - switch( includeResult ) - { - case IncludeResult::NotFound: - case IncludeResult::Error: - { - this->mSink.diagnose(loc, Diagnostics::cannotFindFile, fileName); - - mapNameToLoadedModules[name] = nullptr; - return nullptr; - } - break; + // Get the original path info + PathInfo pathIncludedFromInfo = getSourceManager()->getPathInfo(loc, SourceLocType::Actual); + PathInfo filePathInfo; - default: - break; + // There is an argument to passing in the 'canonicalPath' instead of the foundPath, but either should work here + if (SLANG_FAILED(includeHandler.findFile(fileName, pathIncludedFromInfo.foundPath, filePathInfo))) + { + this->mSink.diagnose(loc, Diagnostics::cannotFindFile, fileName); + mapNameToLoadedModules[name] = nullptr; + return nullptr; } // Maybe this was loaded previously at a different relative name? - if (mapPathToLoadedModule.TryGetValue(foundPath, loadedModule)) + if (mapPathToLoadedModule.TryGetValue(filePathInfo.canonicalPath, loadedModule)) return loadedModule->moduleDecl; + // Try to load it + ComPtr fileContents; + if (SLANG_FAILED(includeHandler.readFile(filePathInfo.canonicalPath, fileContents.writeRef()))) + { + this->mSink.diagnose(loc, Diagnostics::cannotOpenFile, fileName); + mapNameToLoadedModules[name] = nullptr; + return nullptr; + } // We've found a file that we can load for the given module, so // go ahead and perform the module-load action return loadModule( name, - foundPath, - foundSourceBlob, + filePathInfo, + fileContents, loc); } @@ -1121,13 +1199,35 @@ SLANG_API void spDestroyCompileRequest( delete req; } +static const Slang::Guid IID_ISlangFileSystemExt = SLANG_UUID_ISlangFileSystemExt; + SLANG_API void spSetFileSystem( SlangCompileRequest* request, ISlangFileSystem* fileSystem) { if(!request) return; auto req = REQ(request); + + // Set the fileSystem req->fileSystem = fileSystem; + + // Set up fileSystemExt appropriately + if (fileSystem == nullptr) + { + req->fileSystemExt = Slang::IncludeFileSystem::getDefault(); + } + else + { + // See if we have the interface + fileSystem->queryInterface(IID_ISlangFileSystemExt, (void**)req->fileSystemExt.writeRef()); + + // If not wrap with WrapFileSytem that keeps the old behavior + if (!req->fileSystemExt) + { + // Construct a wrapper to emulate the extended interface behavior + req->fileSystemExt = new Slang::WrapFileSystem(fileSystem); + } + } } diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index db745b800..970f4f5fb 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -181,6 +181,7 @@ + @@ -230,6 +231,7 @@ + @@ -262,9 +264,6 @@ - - - Document @@ -272,7 +271,7 @@ "../../bin/windows-x64/debug/slang-generate" %(Identity) "../../bin/windows-x86/release/slang-generate" %(Identity) "../../bin/windows-x64/release/slang-generate" %(Identity) - %(Identity).h + ../../core.meta.slang.h slang-generate %(Identity) ../../bin/windows-x86/debug/slang-generate.exe ../../bin/windows-x64/debug/slang-generate.exe @@ -285,7 +284,7 @@ "../../bin/windows-x64/debug/slang-generate" %(Identity) "../../bin/windows-x86/release/slang-generate" %(Identity) "../../bin/windows-x64/release/slang-generate" %(Identity) - %(Identity).h + ../../hlsl.meta.slang.h slang-generate %(Identity) ../../bin/windows-x86/debug/slang-generate.exe ../../bin/windows-x64/debug/slang-generate.exe @@ -293,6 +292,9 @@ ../../bin/windows-x64/release/slang-generate.exe + + + {F9BE7957-8399-899E-0C49-E714FDDD4B65} diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index a17717b2d..e8c86a372 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -42,6 +42,9 @@ Header Files + + Header Files + Header Files @@ -185,6 +188,9 @@ Source Files + + Source Files + Source Files @@ -279,11 +285,6 @@ Source Files - - - Source Files - - Source Files @@ -292,4 +293,9 @@ Source Files + + + Source Files + + \ No newline at end of file diff --git a/source/slang/source-loc.cpp b/source/slang/source-loc.cpp index d6b75b909..1c3b8c70a 100644 --- a/source/slang/source-loc.cpp +++ b/source/slang/source-loc.cpp @@ -5,7 +5,19 @@ namespace Slang { -/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceUnit !!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceView !!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +const String PathInfo::getMostUniquePath() const +{ + switch (type) + { + case Type::Normal: return canonicalPath; + case Type::FoundPath: return foundPath; + default: return ""; + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceView !!!!!!!!!!!!!!!!!!!!!!!!!!!! */ int SourceView::findEntryIndex(SourceLoc sourceLoc) const { @@ -136,38 +148,33 @@ HumaneSourceLoc SourceView::getHumaneLoc(SourceLoc loc, SourceLocType type) pathHandle = entry.m_pathHandle; } + humaneLoc.pathInfo = _getPathInfo(pathHandle); + return humaneLoc; +} + +PathInfo SourceView::_getPathInfo(StringSlicePool::Handle pathHandle) const +{ // If there is no override path, then just the source files path if (pathHandle == StringSlicePool::Handle(0)) { - humaneLoc.path = m_sourceFile->path; + return m_sourceFile->pathInfo; } else { - humaneLoc.path = m_sourceManager->getStringSlicePool().getSlice(pathHandle); + // We don't have a full normal path (including 'canonical') so just go with FoundPath + return PathInfo::makePath(m_sourceManager->getStringSlicePool().getSlice(pathHandle)); } - - return humaneLoc; } -String SourceView::getPath(SourceLoc loc, SourceLocType type) +PathInfo SourceView::getPathInfo(SourceLoc loc, SourceLocType type) { if (type == SourceLocType::Actual) { - return m_sourceFile->path; + return m_sourceFile->pathInfo; } const int entryIndex = findEntryIndex(loc); - const StringSlicePool::Handle pathHandle = (entryIndex >= 0) ? m_entries[entryIndex].m_pathHandle : StringSlicePool::Handle(0); - - // If there is no override path, then just the source files path - if (pathHandle == StringSlicePool::Handle(0)) - { - return m_sourceFile->path; - } - else - { - return m_sourceManager->getStringSlicePool().getSlice(pathHandle); - } + return _getPathInfo((entryIndex >= 0) ? m_entries[entryIndex].m_pathHandle : StringSlicePool::Handle(0)); } /* !!!!!!!!!!!!!!!!!!!!!!! SourceFile !!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @@ -300,28 +307,24 @@ SourceRange SourceManager::allocateSourceRange(UInt size) return SourceRange(beginLoc, endLoc); } -SourceFile* SourceManager::createSourceFile( - String const& path, - ISlangBlob* contentBlob) +SourceFile* SourceManager::createSourceFile(const PathInfo& pathInfo, ISlangBlob* contentBlob) { char const* contentBegin = (char const*) contentBlob->getBufferPointer(); UInt contentSize = contentBlob->getBufferSize(); char const* contentEnd = contentBegin + contentSize; SourceFile* sourceFile = new SourceFile(); - sourceFile->path = path; + sourceFile->pathInfo = pathInfo; sourceFile->contentBlob = contentBlob; sourceFile->content = UnownedStringSlice(contentBegin, contentEnd); return sourceFile; } - -SourceFile* SourceManager::createSourceFile( - String const& path, - String const& content) + +SourceFile* SourceManager::createSourceFile(const PathInfo& pathInfo, const String& content) { ComPtr contentBlob = createStringBlob(content); - return createSourceFile(path, contentBlob); + return createSourceFile(pathInfo, contentBlob); } SourceView* SourceManager::createSourceView(SourceFile* sourceFile) @@ -394,13 +397,12 @@ SourceView* SourceManager::findSourceViewRecursively(SourceLoc loc) const const SourceManager* manager = this; do { - SourceView* sourceView = findSourceView(loc); + SourceView* sourceView = manager->findSourceView(loc); // If we found a hit we are done if (sourceView) { return sourceView; } - // Try the parent manager = manager->m_parent; } @@ -409,20 +411,31 @@ SourceView* SourceManager::findSourceViewRecursively(SourceLoc loc) const return nullptr; } -SourceFile* SourceManager::findSourceFile(const String& path) +SourceFile* SourceManager::findSourceFile(const String& canonicalPath) const +{ + RefPtr* filePtr = m_sourceFiles.TryGetValue(canonicalPath); + return (filePtr) ? filePtr->Ptr() : nullptr; +} + +SourceFile* SourceManager::findSourceFileRecursively(const String& canonicalPath) const { - RefPtr* filePtr = m_sourceFiles.TryGetValue(path); - if (filePtr) + const SourceManager* manager = this; + do { - return filePtr->Ptr(); - } - return m_parent ? m_parent->findSourceFile(path) : nullptr; + SourceFile* sourceFile = manager->findSourceFile(canonicalPath); + if (sourceFile) + { + return sourceFile; + } + manager = manager->m_parent; + } while (manager); + return nullptr; } -void SourceManager::addSourceFile(const String& path, SourceFile* sourceFile) +void SourceManager::addSourceFile(const String& canonicalPath, SourceFile* sourceFile) { - SLANG_ASSERT(!findSourceFile(path)); - m_sourceFiles.Add(path, sourceFile); + SLANG_ASSERT(!findSourceFileRecursively(canonicalPath)); + m_sourceFiles.Add(canonicalPath, sourceFile); } HumaneSourceLoc SourceManager::getHumaneLoc(SourceLoc loc, SourceLocType type) @@ -438,16 +451,16 @@ HumaneSourceLoc SourceManager::getHumaneLoc(SourceLoc loc, SourceLocType type) } } -String SourceManager::getPath(SourceLoc loc, SourceLocType type) +PathInfo SourceManager::getPathInfo(SourceLoc loc, SourceLocType type) { SourceView* sourceView = findSourceViewRecursively(loc); if (sourceView) { - return sourceView->getPath(loc, type); + return sourceView->getPathInfo(loc, type); } else { - return String("unknown"); + return PathInfo::makeUnknown(); } } diff --git a/source/slang/source-loc.h b/source/slang/source-loc.h index 48b7791c7..8f6b8f7cb 100644 --- a/source/slang/source-loc.h +++ b/source/slang/source-loc.h @@ -38,6 +38,40 @@ SourceViews are created, with unique SourceRanges. This is so that it is possibl interpretation for that lex/parse. */ +struct PathInfo +{ + /// To be more rigorous about where a path comes from, the type identifies what a paths origin is + enum class Type + { + Unknown, ///< The path is not known + Normal, ///< Normal has both path and canonical path + FoundPath, ///< Just has a found path (canonical path is unknown, or even 'unknowable') + TokenPaste, ///< No paths, just created to do a macro expansion + TypeParse, ///< No path, just created to do a type parse + CommandLine, ///< A macro constructed from the command line + }; + + /// True if has a canonical path + SLANG_FORCE_INLINE bool hasCanonicalPath() const { return type == Type::Normal && canonicalPath.Length() > 0; } + /// True if has a regular found path + SLANG_FORCE_INLINE bool hasFoundPath() const { return type == Type::Normal || type == Type::FoundPath; } + + /// The canonical path is unique, so return that if we have it. If not the regular path, otherwise the empty string. + const String getMostUniquePath() const; + + // So simplify construction. In normal usage it's safer to use make methods over constructing directly. + static PathInfo makeUnknown() { return PathInfo { Type::Unknown, "unknown" }; } + static PathInfo makeTokenPaste() { return PathInfo{ Type::TokenPaste, "token paste" }; } + static PathInfo makeNormal(const String& foundPathIn, const String& canonicalPathIn) { SLANG_ASSERT(canonicalPathIn.Length() > 0 && foundPathIn.Length() > 0); return PathInfo { Type::Normal, foundPathIn, canonicalPathIn }; } + static PathInfo makePath(const String& pathIn) { SLANG_ASSERT(pathIn.Length() > 0); return PathInfo { Type::FoundPath, pathIn }; } + static PathInfo makeTypeParse() { return PathInfo { Type::TypeParse, "type string" }; } + static PathInfo makeCommandLine() { return PathInfo { Type::CommandLine, "command line" }; } + + Type type; ///< The type of path + String foundPath; ///< The path where the file was found (might contain relative elements) + String canonicalPath; ///< Canonical version of the found path +}; + class SourceLoc { public: @@ -121,7 +155,7 @@ public: /// Calculate the offset for a line int calcColumnIndex(int line, int offset); - String path; ///< The logical file path to report for locations inside this span. + PathInfo pathInfo; ///< The path The logical file path to report for locations inside this span. ComPtr contentBlob; ///< A blob that owns the storage for the file contents UnownedStringSlice content; ///< The actual contents of the file. @@ -141,7 +175,7 @@ enum class SourceLocType // A source location in a format a human might like to see struct HumaneSourceLoc { - String path; + PathInfo pathInfo; Int line = 0; Int column = 0; }; @@ -200,7 +234,7 @@ class SourceView: public RefObject HumaneSourceLoc getHumaneLoc(SourceLoc loc, SourceLocType type = SourceLocType::Nominal); /// Get the path associated with a location - String getPath(SourceLoc loc, SourceLocType type = SourceLocType::Nominal); + PathInfo getPathInfo(SourceLoc loc, SourceLocType type = SourceLocType::Nominal); /// Ctor SourceView(SourceManager* sourceManager, SourceFile* sourceFile, SourceRange range): @@ -211,7 +245,8 @@ class SourceView: public RefObject } protected: - + PathInfo _getPathInfo(StringSlicePool::Handle pathHandle) const; + SourceManager* m_sourceManager; /// Get the manager this belongs to SourceRange m_range; ///< The range that this SourceView applies to RefPtr m_sourceFile; ///< The source file can hold the line breaks @@ -227,15 +262,15 @@ struct SourceManager SourceRange allocateSourceRange(UInt size); /// Create a SourceFile defined with the specified path, and content held within a blob - SourceFile* createSourceFile(String const& path, ISlangBlob* content); + SourceFile* createSourceFile(const PathInfo& pathInfo, ISlangBlob* content); /// Create a SourceFile with specified path. Create a Blob that contains the content. - SourceFile* createSourceFile(String const& path, String const& content); + SourceFile* createSourceFile(const PathInfo& pathInfo, String const& content); /// Get the humane source location HumaneSourceLoc getHumaneLoc(SourceLoc loc, SourceLocType type = SourceLocType::Nominal); /// Get the path associated with a location - String getPath(SourceLoc loc, SourceLocType type = SourceLocType::Nominal); + PathInfo getPathInfo(SourceLoc loc, SourceLocType type = SourceLocType::Nominal); /// Create a new source view from a file SourceView* createSourceView(SourceFile* sourceFile); @@ -251,10 +286,12 @@ struct SourceManager /// Searches this manager, and then the parent to see if can find a match for path. /// If not found returns nullptr. - SourceFile* findSourceFile(const String& path); + SourceFile* findSourceFileRecursively(const String& canonicalPath) const; + /// Find if the source file is defined on this manager. + SourceFile* findSourceFile(const String& canonicalPath) const; /// Add a source file, path must be unique for this manager AND any parents - void addSourceFile(const String& path, SourceFile* sourceFile); + void addSourceFile(const String& canonicalPath, SourceFile* sourceFile); /// Get the slice pool StringSlicePool& getStringSlicePool() { return m_slicePool; } @@ -283,7 +320,8 @@ struct SourceManager List > m_sourceViews; StringSlicePool m_slicePool; - Dictionary > m_sourceFiles; + // Maps canonical paths to source files + Dictionary > m_sourceFiles; }; } // namespace Slang diff --git a/tests/preprocessor/inappropriate-once.slang b/tests/preprocessor/inappropriate-once.slang new file mode 100644 index 000000000..6b750ec77 --- /dev/null +++ b/tests/preprocessor/inappropriate-once.slang @@ -0,0 +1,6 @@ +//TEST:SIMPLE: +// #include support + +#pragma once + +int foo() { return 0; } \ No newline at end of file diff --git a/tests/preprocessor/inappropriate-once.slang.expected b/tests/preprocessor/inappropriate-once.slang.expected new file mode 100644 index 000000000..04d7f7834 --- /dev/null +++ b/tests/preprocessor/inappropriate-once.slang.expected @@ -0,0 +1,6 @@ +result code = 0 +standard error = { +tests/preprocessor/inappropriate-once.slang(4): warning 15602: pragma once was ignored - this is typically because is not placed in an include +} +standard output = { +} diff --git a/tests/preprocessor/include-multiple.slang b/tests/preprocessor/include-multiple.slang new file mode 100644 index 000000000..6dd1624bc --- /dev/null +++ b/tests/preprocessor/include-multiple.slang @@ -0,0 +1,10 @@ +//TEST:SIMPLE: +// #include support + +int foo() { return 0; } + +#include "include-a.slang.h" +#include "../preprocessor/include-a.slang.h" +#include "./include-a.slang.h" + +int baz() { return bar(); } \ No newline at end of file diff --git a/tests/preprocessor/include-multiple.slang.expected b/tests/preprocessor/include-multiple.slang.expected new file mode 100644 index 000000000..d52a5a2ab --- /dev/null +++ b/tests/preprocessor/include-multiple.slang.expected @@ -0,0 +1,9 @@ +result code = -1 +standard error = { +tests/preprocessor/include-a.slang.h(3): error 30201: function 'bar' already has a body +tests/preprocessor/include-a.slang.h(3): note: see previous definition of 'bar' +tests/preprocessor/include-a.slang.h(3): error 30201: function 'bar' already has a body +tests/preprocessor/include-a.slang.h(3): note: see previous definition of 'bar' +} +standard output = { +} diff --git a/tools/slang-test/slang-test.vcxproj b/tools/slang-test/slang-test.vcxproj index 54ea6f342..acaa07d3e 100644 --- a/tools/slang-test/slang-test.vcxproj +++ b/tools/slang-test/slang-test.vcxproj @@ -174,6 +174,7 @@ + diff --git a/tools/slang-test/slang-test.vcxproj.filters b/tools/slang-test/slang-test.vcxproj.filters index b34d45798..fc65986f4 100644 --- a/tools/slang-test/slang-test.vcxproj.filters +++ b/tools/slang-test/slang-test.vcxproj.filters @@ -41,5 +41,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/tools/slang-test/unit-test-path.cpp b/tools/slang-test/unit-test-path.cpp new file mode 100644 index 000000000..ddaa39d6d --- /dev/null +++ b/tools/slang-test/unit-test-path.cpp @@ -0,0 +1,26 @@ +// unit-test-path.cpp + +#include "../../source/core/slang-io.h" + + +#include "test-context.h" + +using namespace Slang; + +static void pathUnitTest() +{ + { + String path; + SlangResult res = Path::GetCanonical(".", path); + SLANG_CHECK(SLANG_SUCCEEDED(res)); + + String parentPath; + res = Path::GetCanonical("..", parentPath); + SLANG_CHECK(SLANG_SUCCEEDED(res)); + + String parentPath2 = Path::GetDirectoryName(path); + SLANG_CHECK(parentPath == parentPath2); + } +} + +SLANG_UNIT_TEST("Path", pathUnitTest); \ No newline at end of file -- cgit v1.2.3