summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--slang.h66
-rw-r--r--slang.sln164
-rw-r--r--source/core/core.vcxproj2
-rw-r--r--source/core/core.vcxproj.filters4
-rw-r--r--source/core/slang-io.cpp40
-rw-r--r--source/core/slang-io.h2
-rw-r--r--source/slang/compiler.cpp5
-rw-r--r--source/slang/compiler.h12
-rw-r--r--source/slang/diagnostic-defs.h3
-rw-r--r--source/slang/diagnostics.cpp2
-rw-r--r--source/slang/emit.cpp11
-rw-r--r--source/slang/include-file-system.cpp101
-rw-r--r--source/slang/include-file-system.h81
-rw-r--r--source/slang/preprocessor.cpp62
-rw-r--r--source/slang/preprocessor.h19
-rw-r--r--source/slang/slang.cpp196
-rw-r--r--source/slang/slang.vcxproj12
-rw-r--r--source/slang/slang.vcxproj.filters16
-rw-r--r--source/slang/source-loc.cpp95
-rw-r--r--source/slang/source-loc.h58
-rw-r--r--tests/preprocessor/inappropriate-once.slang6
-rw-r--r--tests/preprocessor/inappropriate-once.slang.expected6
-rw-r--r--tests/preprocessor/include-multiple.slang10
-rw-r--r--tests/preprocessor/include-multiple.slang.expected9
-rw-r--r--tools/slang-test/slang-test.vcxproj1
-rw-r--r--tools/slang-test/slang-test.vcxproj.filters3
-rw-r--r--tools/slang-test/unit-test-path.cpp26
27 files changed, 770 insertions, 242 deletions
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 @@
<ClCompile Include="token-reader.cpp" />
</ItemGroup>
<ItemGroup>
- <None Include="core.natvis" />
+ <Natvis Include="core.natvis" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
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 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
- <None Include="core.natvis">
+ <Natvis Include="core.natvis">
<Filter>Source Files</Filter>
- </None>
+ </Natvis>
</ItemGroup>
</Project> \ 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 <sys/stat.h>
+
#ifdef _WIN32
-#include <direct.h>
+# include <direct.h>
#endif
+#include <limits.h> /* PATH_MAX */
+#include <stdio.h>
+#include <stdlib.h>
+
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<ISlangFileSystem> 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<ISlangFileSystemExt> 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<ModuleDecl> loadModule(
Name* name,
- String const& path,
- ISlangBlob* sourceBlob,
+ const PathInfo& filePathInfo,
+ ISlangBlob* fileContentsBlob,
SourceLoc const& loc);
void loadParsedModule(
RefPtr<TranslationUnitRequest> const& translationUnit,
Name* name,
- String const& path);
+ PathInfo const& pathInfo);
RefPtr<ModuleDecl> 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<ISlangFileSystemExt*>(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<ISlangBlob> 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<ISlangFileSystem> 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<ISlangBlob> 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<ISlangBlob> 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<ISlangBlob> 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<ISlangBlob> 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<Expr> CompileRequest::parseTypeString(TranslationUnitRequest * translatio
SourceManager localSourceManager;
localSourceManager.initialize(sourceManager);
- Slang::RefPtr<Slang::SourceFile> srcFile(localSourceManager.createSourceFile(String("type string"), typeStr));
+ Slang::RefPtr<Slang::SourceFile> 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> sourceFile = getSourceManager()->createSourceFile(path, sourceBlob);
+ PathInfo pathInfo = PathInfo::makePath(path);
+ RefPtr<SourceFile> sourceFile = getSourceManager()->createSourceFile(pathInfo, sourceBlob);
addTranslationUnitSourceFile(translationUnitIndex, sourceFile);
}
@@ -716,7 +790,8 @@ void CompileRequest::addTranslationUnitSourceString(
String const& path,
String const& source)
{
- RefPtr<SourceFile> sourceFile = getSourceManager()->createSourceFile(path, source);
+ PathInfo pathInfo = PathInfo::makePath(path);
+ RefPtr<SourceFile> sourceFile = getSourceManager()->createSourceFile(pathInfo, source);
addTranslationUnitSourceFile(translationUnitIndex, sourceFile);
}
@@ -789,13 +864,18 @@ UInt CompileRequest::addTarget(
void CompileRequest::loadParsedModule(
RefPtr<TranslationUnitRequest> 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> 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<ModuleDecl> CompileRequest::loadModule(
Name* name,
- String const& path,
- ISlangBlob* sourceBlob,
+ const PathInfo& filePathInfo,
+ ISlangBlob* sourceBlob,
SourceLoc const& srcLoc)
{
RefPtr<TranslationUnitRequest> translationUnit = new TranslationUnitRequest();
@@ -835,11 +915,11 @@ RefPtr<ModuleDecl> CompileRequest::loadModule(
// TODO: decide which options, if any, should be inherited.
translationUnit->compileFlags = 0;
- RefPtr<SourceFile> sourceFile = getSourceManager()->createSourceFile(path, sourceBlob);
+ // Create with the 'friendly' name
+ RefPtr<SourceFile> 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<ModuleDecl> CompileRequest::loadModule(
loadParsedModule(
translationUnit,
name,
- path);
+ filePathInfo);
errorCountAfter = mSink.GetErrorCount();
@@ -891,7 +971,7 @@ RefPtr<ModuleDecl> 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<ModuleDecl> CompileRequest::findOrImportModule(
IncludeHandlerImpl includeHandler;
includeHandler.request = this;
- // Get the original path
- String pathIncludedFrom= getSourceManager()->getPath(loc, SourceLocType::Actual);
-
- String foundPath;
- ComPtr<ISlangBlob> 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<ISlangBlob> 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 @@
<ClInclude Include="expr-defs.h" />
<ClInclude Include="glsl.meta.slang.h" />
<ClInclude Include="hlsl.meta.slang.h" />
+ <ClInclude Include="include-file-system.h" />
<ClInclude Include="ir-constexpr.h" />
<ClInclude Include="ir-dominators.h" />
<ClInclude Include="ir-inst-defs.h" />
@@ -230,6 +231,7 @@
<ClCompile Include="diagnostics.cpp" />
<ClCompile Include="dxc-support.cpp" />
<ClCompile Include="emit.cpp" />
+ <ClCompile Include="include-file-system.cpp" />
<ClCompile Include="ir-constexpr.cpp" />
<ClCompile Include="ir-dominators.cpp" />
<ClCompile Include="ir-legalize-types.cpp" />
@@ -263,16 +265,13 @@
<ClCompile Include="vm.cpp" />
</ItemGroup>
<ItemGroup>
- <None Include="slang.natvis" />
- </ItemGroup>
- <ItemGroup>
<CustomBuild Include="core.meta.slang">
<FileType>Document</FileType>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../../bin/windows-x86/debug/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-generate" %(Identity)</Command>
- <Outputs>%(Identity).h</Outputs>
+ <Outputs>../../core.meta.slang.h</Outputs>
<Message>slang-generate %(Identity)</Message>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../bin/windows-x86/debug/slang-generate.exe</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../bin/windows-x64/debug/slang-generate.exe</AdditionalInputs>
@@ -285,7 +284,7 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-generate" %(Identity)</Command>
- <Outputs>%(Identity).h</Outputs>
+ <Outputs>../../hlsl.meta.slang.h</Outputs>
<Message>slang-generate %(Identity)</Message>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../bin/windows-x86/debug/slang-generate.exe</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../bin/windows-x64/debug/slang-generate.exe</AdditionalInputs>
@@ -294,6 +293,9 @@
</CustomBuild>
</ItemGroup>
<ItemGroup>
+ <Natvis Include="slang.natvis" />
+ </ItemGroup>
+ <ItemGroup>
<ProjectReference Include="..\core\core.vcxproj">
<Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project>
</ProjectReference>
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 @@
<ClInclude Include="hlsl.meta.slang.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="include-file-system.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="ir-constexpr.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -185,6 +188,9 @@
<ClCompile Include="emit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="include-file-system.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="ir-constexpr.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -280,11 +286,6 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
- <None Include="slang.natvis">
- <Filter>Source Files</Filter>
- </None>
- </ItemGroup>
- <ItemGroup>
<CustomBuild Include="core.meta.slang">
<Filter>Source Files</Filter>
</CustomBuild>
@@ -292,4 +293,9 @@
<Filter>Source Files</Filter>
</CustomBuild>
</ItemGroup>
+ <ItemGroup>
+ <Natvis Include="slang.natvis">
+ <Filter>Source Files</Filter>
+ </Natvis>
+ </ItemGroup>
</Project> \ 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<ISlangBlob> 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<SourceFile>* filePtr = m_sourceFiles.TryGetValue(canonicalPath);
+ return (filePtr) ? filePtr->Ptr() : nullptr;
+}
+
+SourceFile* SourceManager::findSourceFileRecursively(const String& canonicalPath) const
{
- RefPtr<SourceFile>* 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<ISlangBlob> 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<SourceFile> 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<RefPtr<SourceView> > m_sourceViews;
StringSlicePool m_slicePool;
- Dictionary<String, RefPtr<SourceFile> > m_sourceFiles;
+ // Maps canonical paths to source files
+ Dictionary<String, RefPtr<SourceFile> > 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 @@
<ClCompile Include="unit-test-byte-encode.cpp" />
<ClCompile Include="unit-test-free-list.cpp" />
<ClCompile Include="unit-test-memory-arena.cpp" />
+ <ClCompile Include="unit-test-path.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\source\core\core.vcxproj">
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 @@
<ClCompile Include="unit-test-memory-arena.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="unit-test-path.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
</Project> \ 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