204 lines
6.3 KiB
C++
204 lines
6.3 KiB
C++
#include "ShaderCompiler.h"
|
|
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
namespace
|
|
{
|
|
int gFailures = 0;
|
|
|
|
void Expect(bool condition, const std::string& message)
|
|
{
|
|
if (condition)
|
|
return;
|
|
|
|
++gFailures;
|
|
std::cerr << "FAIL: " << message << "\n";
|
|
}
|
|
|
|
std::filesystem::path MakeTestRoot(const std::string& name)
|
|
{
|
|
const std::filesystem::path root =
|
|
std::filesystem::temp_directory_path() /
|
|
("shader-compiler-lookup-" + name + "-" + std::to_string(std::filesystem::file_time_type::clock::now().time_since_epoch().count()));
|
|
std::filesystem::create_directories(root);
|
|
return root;
|
|
}
|
|
|
|
void WriteFakeSlangCompiler(const std::filesystem::path& path)
|
|
{
|
|
std::filesystem::create_directories(path.parent_path());
|
|
std::ofstream output(path, std::ios::binary);
|
|
output << "not a real compiler";
|
|
}
|
|
|
|
std::string ReadEnvironmentVariable(const char* name)
|
|
{
|
|
#if defined(_MSC_VER)
|
|
char* value = nullptr;
|
|
std::size_t size = 0;
|
|
if (_dupenv_s(&value, &size, name) != 0 || value == nullptr)
|
|
return std::string();
|
|
std::string text(value);
|
|
std::free(value);
|
|
return text;
|
|
#else
|
|
const char* value = std::getenv(name);
|
|
return value ? std::string(value) : std::string();
|
|
#endif
|
|
}
|
|
|
|
void WriteEnvironmentVariable(const char* name, const std::string& value)
|
|
{
|
|
#if defined(_MSC_VER)
|
|
_putenv_s(name, value.c_str());
|
|
#else
|
|
if (value.empty())
|
|
unsetenv(name);
|
|
else
|
|
setenv(name, value.c_str(), 1);
|
|
#endif
|
|
}
|
|
|
|
class ScopedEnvironmentVariable
|
|
{
|
|
public:
|
|
ScopedEnvironmentVariable(const char* name, const std::string& value) :
|
|
mName(name),
|
|
mOriginal(ReadEnvironmentVariable(name))
|
|
{
|
|
WriteEnvironmentVariable(name, value);
|
|
}
|
|
|
|
~ScopedEnvironmentVariable()
|
|
{
|
|
WriteEnvironmentVariable(mName.c_str(), mOriginal);
|
|
}
|
|
|
|
ScopedEnvironmentVariable(const ScopedEnvironmentVariable&) = delete;
|
|
ScopedEnvironmentVariable& operator=(const ScopedEnvironmentVariable&) = delete;
|
|
|
|
private:
|
|
std::string mName;
|
|
std::string mOriginal;
|
|
};
|
|
|
|
bool SamePath(const std::filesystem::path& left, const std::filesystem::path& right)
|
|
{
|
|
std::error_code leftError;
|
|
std::error_code rightError;
|
|
return std::filesystem::weakly_canonical(left, leftError) == std::filesystem::weakly_canonical(right, rightError);
|
|
}
|
|
|
|
void TestSlangRootWinsOverThirdPartyRoot()
|
|
{
|
|
const std::filesystem::path root = MakeTestRoot("slang-root");
|
|
const std::filesystem::path expected = root / "env-slang" / "bin" / "slangc.exe";
|
|
const std::filesystem::path thirdPartyCompiler = root / "third-party" / "slang-2026.8-windows-x86_64" / "bin" / "slangc.exe";
|
|
WriteFakeSlangCompiler(expected);
|
|
WriteFakeSlangCompiler(thirdPartyCompiler);
|
|
|
|
ScopedEnvironmentVariable slangRoot("SLANG_ROOT", (root / "env-slang").string());
|
|
ScopedEnvironmentVariable thirdPartyRoot("THIRD_PARTY_ROOT", (root / "third-party").string());
|
|
|
|
std::filesystem::path actual;
|
|
std::string error;
|
|
Expect(ShaderCompiler::FindSlangCompilerPath(root, actual, error), "SLANG_ROOT compiler is found");
|
|
Expect(SamePath(actual, expected), "SLANG_ROOT has priority over THIRD_PARTY_ROOT");
|
|
|
|
std::filesystem::remove_all(root);
|
|
}
|
|
|
|
void TestThirdPartyRootUsesCMakeBundleLayout()
|
|
{
|
|
const std::filesystem::path root = MakeTestRoot("third-party-root");
|
|
const std::filesystem::path thirdPartyRootPath = root / "video-io-3rdParty";
|
|
const std::filesystem::path expected = thirdPartyRootPath / "slang-2026.8-windows-x86_64" / "bin" / "slangc.exe";
|
|
WriteFakeSlangCompiler(expected);
|
|
|
|
ScopedEnvironmentVariable slangRoot("SLANG_ROOT", "");
|
|
ScopedEnvironmentVariable thirdPartyRoot("THIRD_PARTY_ROOT", thirdPartyRootPath.string());
|
|
|
|
std::filesystem::path actual;
|
|
std::string error;
|
|
Expect(ShaderCompiler::FindSlangCompilerPath(root, actual, error), "THIRD_PARTY_ROOT compiler is found");
|
|
Expect(SamePath(actual, expected), "THIRD_PARTY_ROOT matches CMake's Slang bundle layout");
|
|
|
|
std::filesystem::remove_all(root);
|
|
}
|
|
|
|
void TestRepoVideoIoBundleWinsOverLegacyRepoBundle()
|
|
{
|
|
const std::filesystem::path root = MakeTestRoot("repo-video-io");
|
|
const std::filesystem::path expected = root / "video-io-3rdParty" / "slang-2026.8-windows-x86_64" / "bin" / "slangc.exe";
|
|
const std::filesystem::path legacy = root / "3rdParty" / "slang-2026.8-windows-x86_64" / "bin" / "slangc.exe";
|
|
WriteFakeSlangCompiler(expected);
|
|
WriteFakeSlangCompiler(legacy);
|
|
|
|
ScopedEnvironmentVariable slangRoot("SLANG_ROOT", "");
|
|
ScopedEnvironmentVariable thirdPartyRoot("THIRD_PARTY_ROOT", "");
|
|
|
|
std::filesystem::path actual;
|
|
std::string error;
|
|
Expect(ShaderCompiler::FindSlangCompilerPath(root, actual, error), "repo video-io-3rdParty compiler is found");
|
|
Expect(SamePath(actual, expected), "repo video-io-3rdParty has priority over repo 3rdParty");
|
|
|
|
std::filesystem::remove_all(root);
|
|
}
|
|
|
|
void TestPackagedSlangFolderIsAccepted()
|
|
{
|
|
const std::filesystem::path root = MakeTestRoot("packaged");
|
|
const std::filesystem::path expected = root / "3rdParty" / "slang" / "bin" / "slangc.exe";
|
|
WriteFakeSlangCompiler(expected);
|
|
|
|
ScopedEnvironmentVariable slangRoot("SLANG_ROOT", "");
|
|
ScopedEnvironmentVariable thirdPartyRoot("THIRD_PARTY_ROOT", "");
|
|
|
|
std::filesystem::path actual;
|
|
std::string error;
|
|
Expect(ShaderCompiler::FindSlangCompilerPath(root, actual, error), "packaged 3rdParty/slang compiler is found");
|
|
Expect(SamePath(actual, expected), "packaged Slang install layout is accepted");
|
|
|
|
std::filesystem::remove_all(root);
|
|
}
|
|
|
|
void TestLegacyThirdPartyScanStillAcceptsVersionedFolders()
|
|
{
|
|
const std::filesystem::path root = MakeTestRoot("legacy-scan");
|
|
const std::filesystem::path expected = root / "3rdParty" / "slang-custom-build" / "bin" / "slangc.exe";
|
|
WriteFakeSlangCompiler(expected);
|
|
|
|
ScopedEnvironmentVariable slangRoot("SLANG_ROOT", "");
|
|
ScopedEnvironmentVariable thirdPartyRoot("THIRD_PARTY_ROOT", "");
|
|
|
|
std::filesystem::path actual;
|
|
std::string error;
|
|
Expect(ShaderCompiler::FindSlangCompilerPath(root, actual, error), "legacy 3rdParty scan finds versioned Slang folders");
|
|
Expect(SamePath(actual, expected), "legacy scan returns the discovered compiler path");
|
|
|
|
std::filesystem::remove_all(root);
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
TestSlangRootWinsOverThirdPartyRoot();
|
|
TestThirdPartyRootUsesCMakeBundleLayout();
|
|
TestRepoVideoIoBundleWinsOverLegacyRepoBundle();
|
|
TestPackagedSlangFolderIsAccepted();
|
|
TestLegacyThirdPartyScanStillAcceptsVersionedFolders();
|
|
|
|
if (gFailures != 0)
|
|
{
|
|
std::cerr << gFailures << " ShaderCompiler lookup test failure(s).\n";
|
|
return 1;
|
|
}
|
|
|
|
std::cout << "ShaderCompiler lookup tests passed.\n";
|
|
return 0;
|
|
}
|