From 9cb75371f5ea45640ae0e3998eb27bcda0a22cd9 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Tue, 23 Apr 2019 17:19:20 -0400 Subject: Feature/premake build (#951) * * Remove Makefile * Document how to create build using premake5 * Added support for finding the executable path * If binDir not set on command line use the executable path * Fix getting exe path on linux. * Removed CalcExecutablePath from Path:: interface, made implementation internal. * Documentation improvements. * Fixes based on review * Fix some typos * Removed unused/needed global --- Makefile | 174 ----------------------------------- docs/building.md | 76 ++++++++++++++- source/core/slang-io.cpp | 127 +++++++++++++++++++++++++ source/core/slang-io.h | 5 + source/slang/slang.vcxproj | 1 - source/slang/slang.vcxproj.filters | 11 +-- tools/slang-test/options.cpp | 10 ++ tools/slang-test/options.h | 6 +- tools/slang-test/slang-test-main.cpp | 14 +-- 9 files changed, 228 insertions(+), 196 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index bce0ab803..000000000 --- a/Makefile +++ /dev/null @@ -1,174 +0,0 @@ -# Makefile -# -# This file provides a simpliistic (perhaps *overly* simplistic) way to -# build Slang from source on Linux, which could probably be adapted for -# building on other Unix-y targets. -# -# This build is not intended to be used for active development right now, -# so it doesn't actually try to compile the `.cpp` files separately, -# or track fine-grained dependencies, so almost any source change will -# trigger a full rebuild. Anybody who wants to do their active development -# on a platform supported by this Makefile should feel free to contribute -# improvements, with the caveat that we will not be adopting autoconf, -# CMake, or any other build system that has a tendency to "infect" a codebase. -# - -PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]') -ARCHITECTURE := $(shell uname -p) - -ifeq (,$(CONFIGURATION)) - CONFIGURATION := release -endif - -ifeq (,$(SLANG_TEST_CATEGORY)) - SLANG_TEST_CATEGORY := full -endif - -# -# The Windows build (using Visual Studio) tries to output things to -# directories that take the target platform (and build configuration) into -# account. For now we will do something simplistic and request the target -# "triple" from the compiler (which we assume is either gcc or clang) and -# call that our target "platformm" -# -TARGET := $(PLATFORM)-$(ARCHITECTURE) -# -# TODO: We need a way to control the "configuration" (debug vs. release) -# but for now just geting *something* working will be a good start. -# - -# -# We will define an ouput directory for our binaries, based on -# the target platform chosen. If we ever have steps that need to -# output intermediate files, we'd set up the directory here. -# -OUTPUTDIR := bin/$(TARGET)/$(CONFIGURATION)/ -INTERMEDIATEDIR := intermediate/$(TARGET)/$(CONFIGURATION)/ - -# -# Now we will start defining a bunch of variables for build -# options and properties of the target. We are going to unconditionallyy -# set these to what we need/want on our Linux target for now, but -# we will eventually need to do a bit more work to detect the -# platform. -# -SHARED_LIB_PREFIX := lib -SHARED_LIB_SUFFIX := .so -BIN_SUFFIX := -# Note: we set `visibility=hidden` to avoid exporting more symbols than -# we really need. -CFLAGS := -std=c++11 -fvisibility=hidden -fno-delete-null-pointer-checks -CFLAGS += -I. -LDFLAGS := -L$(OUTPUTDIR) -SHARED_LIB_LDFLAGS := -shared -SHARED_LIB_CFLAGS := -fPIC - -ifeq (debug,$(CONFIGURATION)) -CFLAGS += -g -else -CFLAGS += -O2 -endif - -# Make sure that shared library inherits build flags -# from the default case. -SHARED_LIB_LDFLAGS += $(LDFLAGS) -SHARED_LIB_CFLAGS += $(CFLAGS) - -RELATIVE_RPATH_INCANTATION := "-Wl,-rpath,"'$$'"ORIGIN/" - -# TODO: Make sure I'm using these Makefile incantations correctly. -.SUFFIXES: -.PHONY: all clean slang slangc test - -# -# Here we define lists of files (source vs. header dependencies) -# for each logical project we want to build. -# This is the one place where we do any kiind of "dependency" work, -# by making a project depend on the headers for sub-projects it usses. -# -CORE_SOURCES := source/core/*.cpp -CORE_HEADERS := source/core/*.h - -SLANG_SOURCES := source/slang/*.cpp -SLANG_HEADERS := slang.h source/slang/*.h -# -SLANG_SOURCES += $(CORE_SOURCES) -SLANG_HEADERS += $(CORE_HEADERS) - -SLANGC_SOURCES := source/slangc/*.cpp -SLANGC_HEADERS := $(SLANG_HEADERS) -# -SLANGC_SOURCES += $(CORE_SOURCES) - -SLANG_GLSLANG_SOURCES := source/slang-glslang/*.cpp -SLANG_GLSLANG_HEADERS := source/slang-glslang/*.h - -SLANG_REFLECTION_TEST_SOURCES := tools/slang-reflection-test/*.cpp -SLANG_REFLECTION_TEST_HEADERS := - -# Add `glslang` sources to the build or `slang-glslang` -# -# Note: We aren't going to wasttte time trying to work with -# the existing CMake-based build for `glslang`. -# -SLANG_GLSLANG_SOURCES += \ - external/glslang/OGLCompilersDLL/*.cpp \ - external/glslang/SPIRV/*.cpp \ - external/glslang/glslang/GenericCodeGen/*.cpp \ - external/glslang/glslang/MachineIndependent/*.cpp \ - external/glslang/glslang/MachineIndependent/preprocessor/*.cpp \ - external/glslang/glslang/OSDependent/Unix/*.cpp - - -SLANG_TEST_SOURCES := tools/slang-test/*.cpp -SLANG_TEST_HEADERS := tools/slang-test/*.h -# -SLANG_TEST_SOURCES += $(CORE_SOURCES) -SLANG_TEST_HEADERS += $(CORE_HEADERS) - -# -# Each project will have a variable that is an alias for -# the binary it should produce. -# -SLANG := $(OUTPUTDIR)$(SHARED_LIB_PREFIX)slang$(SHARED_LIB_SUFFIX) -SLANGC := $(OUTPUTDIR)slangc$(BIN_SUFFIX) -SLANG_GLSLANG := $(OUTPUTDIR)$(SHARED_LIB_PREFIX)slang-glslang$(SHARED_LIB_SUFFIX) -SLANG_TEST := $(OUTPUTDIR)slang-test$(BIN_SUFFIX) -SLANG_REFLECTION_TEST := $(OUTPUTDIR)slang-reflection-test$(BIN_SUFFIX) - -# By default, when the user invokes `make`, we will build the -# `slang` shared library, and the `slangc` front-end application. -all: slang slang-glslang slangc slang-test slang-reflection-test - -mkdirs: $(OUTPUTDIR) - -# Project-specific targets depend on making theappropriate binary. -slang: mkdirs $(SLANG) -slangc: mkdirs $(SLANGC) -slang-glslang: mkdirs $(SLANG_GLSLANG) -slang-test: mkdirs $(SLANG_TEST) -slang-reflection-test: mkdirs $(SLANG_REFLECTION_TEST) - -$(SLANG): $(SLANG_SOURCES) $(SLANG_HEADERS) - $(CXX) $(SHARED_LIB_LDFLAGS) -o $@ -DSLANG_DYNAMIC_EXPORT $(SHARED_LIB_CFLAGS) $(SLANG_SOURCES) -ldl $(RELATIVE_RPATH_INCANTATION) - -$(SLANGC): $(SLANGC_SOURCES) $(SLANGC_HEADERS) $(SLANG) - $(CXX) $(LDFLAGS) -o $@ $(CFLAGS) $(SLANGC_SOURCES) -ldl $(RELATIVE_RPATH_INCANTATION) -lslang - -$(SLANG_GLSLANG): $(SLANG_GLSLANG_SOURCES) $(SLANG_GLSLANG_HEADERS) - $(CXX) $(SHARED_LIB_LDFLAGS) -pthread -o $@ -Iexternal/glslang/ $(SHARED_LIB_CFLAGS) -DAMD_EXTENSIONS -DNV_EXTENSIONS $(SLANG_GLSLANG_SOURCES) - -$(SLANG_TEST): $(SLANG_TEST_SOURCES) $(SLANG_TEST_HEADERS) $(SLANG) - $(CXX) $(LDFLAGS) -o $@ $(CFLAGS) $(SLANG_TEST_SOURCES) -ldl $(RELATIVE_RPATH_INCANTATION) -lslang - -$(SLANG_REFLECTION_TEST): $(SLANG_REFLECTION_TEST_SOURCES) $(SLANG) - $(CXX) $(LDFLAGS) -o $@ $(CFLAGS) $(SLANG_REFLECTION_TEST_SOURCES) $(RELATIVE_RPATH_INCANTATION) -lslang - -$(OUTPUTDIR): - mkdir -p $(OUTPUTDIR) - -test: $(SLANG_TEST) $(SLANG_REFLECTION_TEST) - $(SLANG_TEST) -bindir $(OUTPUTDIR) -travis -category $(SLANG_TEST_CATEGORY) $(SLANG_TEST_FLAGS) - -clean: - rm -rf $(OUTPUTDIR) diff --git a/docs/building.md b/docs/building.md index 68e57ec2d..32e4a1156 100644 --- a/docs/building.md +++ b/docs/building.md @@ -6,13 +6,81 @@ Clone [this](https://github.com/shader-slang/slang) repository, and then run: git submodule update --init -The submodule update step is required to pull in the copy of the `glslang` compiler that we currently use for generating SPIR-V. +The submodule update step is required to pull in dependencies used for testing infrastructure as well as the `glslang` compiler that we currently use for generating SPIR-V. -## Using Visual Studio +## Windows Using Visual Studio Building from source is really only well supported for Windows users with Visual Studio 2015 or later. -If you are on Windows, then open `slang.sln` and build your desired platform/configuration. +If you are on Windows, then open `slang.sln` and build your desired platform/configuration. +The Visual Studio solution in the project is actually just generated using [`premake5`](https://premake.github.io/). See instructions in premake section below for further explanation. + ## Linux -For Linux, we include a simple `Makefile`, but it is not designed to be used for active development (e.g., dependency tracking is not handled). +For building on Linux it is first necessary to generate the `Makefile` for the project - we use [`premake5`](https://premake.github.io/) as the tool used for generating projects from the premake5.lua script found in the root of the project. The section below describes how to use premake on Linux. + +## Premake + +Slang uses the tool [`premake5`](https://premake.github.io/) in order to generate projects that can be built on different targets. On Linux premake will generate Makefile/s and on windows it will generate a Visual Studio solution. Information on invoking premake for different kinds of targets can be found [here](https://github.com/premake/premake-core/wiki/Using-Premake). You can also run with `--help` to see available command line options + +``` +% premake5 --help +``` + +### Windows + +First download and install [`premake5`](https://premake.github.io/) on your build system. Open up a command line and go to the root directory of the slang source tree (ie the directory containing `slang.h`). + +Assuming premake5 is in your `PATH`, you can create a Visual Studio 2015 project for Slang with the following command line + +``` +% premake5 vs2015 +``` + +For Visual Studio 2017 use + +``` +% premake5 vs2017 +``` + +These should create a slang.sln in the same directory and which you can then open in the appropriate Visual Studio. Building will build all of slang and it's test infrastructure. + +### Linux + +First download and install [`premake5`](https://premake.github.io/) on your build system. In the terminal go to the root directory of the slang source tree (ie the directory containing `slang.h`). Assuming premake5 is in your `PATH` use + +``` +% premake5 gmake +``` + +You can vary the compiler to use via the --cc option with 'gcc' or 'clang' for example + +``` +% premake5 gmake --cc=clang +``` + +To actually build using make use one of the following + +``` +% make config=release_x64 +% make config=debug_x64 +% make config=release_x86 +% make config=debug_x86 +% make config=release_aarch64 +% make config=debug_aarch64 +``` + +## Testing + +When slang is built from source it also builds tools to be able to test the Slang compiler. Testing is achieved using the `slang-test` tool. The binaries are placed in the appropriate directory underneath `bin`. To run the tests on a release x64 build from the command line, in the root directory of slang source tree you can use... + +``` +% bin\windows-x64\release\slang-test +``` + +Note that on windows if you want to run all of the tests from inside visual studio, it is necessary to set the `Working Directory` under "slang-test project" > "Configuration Properties" > "Debugging" > "Working Directory" to the root directory of the slang source tree. You can do this by setting it to `$(ProjectDir)/../..` for all configurations. + +If you only see 'unit-tests' being run (unit tests are prefixed with 'unit-tests/') then the working directory is not correctly set. Most tests are text files describing the test held in the `tests` directory in the root of the slang project. + +See the [documentation on testing](../tools/slang-test/README.md) for more information. + diff --git a/source/core/slang-io.cpp b/source/core/slang-io.cpp index cf099aa04..6d2320cd0 100644 --- a/source/core/slang-io.cpp +++ b/source/core/slang-io.cpp @@ -9,6 +9,18 @@ #ifdef _WIN32 # include + +# define WIN32_LEAN_AND_MEAN +# define VC_EXTRALEAN +# include +#endif + +#if defined(__linux__) || defined(__CYGWIN__) +# include +#endif + +#if SLANG_APPLE_FAMILY +# include #endif #include /* PATH_MAX */ @@ -316,6 +328,121 @@ namespace Slang #endif } + /// Gets the path to the executable that was invoked that led to the current threads execution + /// If run from a shared library/dll will be the path of the executable that loaded said library + /// @param outPath Pointer to buffer to hold the path. + /// @param ioPathSize Size of the buffer to hold the path (including zero terminator). + /// @return SLANG_OK on success, SLANG_E_BUFFER_TOO_SMALL if buffer is too small. If ioPathSize is changed it will be the required size + static SlangResult _calcExectuablePath(char* outPath, size_t* ioSize) + { + SLANG_ASSERT(ioSize); + const size_t bufferSize = *ioSize; + SLANG_ASSERT(bufferSize > 0); + +#if SLANG_WINDOWS_FAMILY + // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-getmodulefilenamea + + DWORD res = ::GetModuleFileNameA(::GetModuleHandle(nullptr), outPath, DWORD(bufferSize)); + // If it fits it's the size not including terminator. So must be less than bufferSize + if (res < bufferSize) + { + return SLANG_OK; + } + return SLANG_E_BUFFER_TOO_SMALL; +#elif SLANG_LINUX_FAMILY + +# if defined(__linux__) || defined(__CYGWIN__) + // https://linux.die.net/man/2/readlink + // Mark last byte with 0, so can check overrun + ssize_t resSize = ::readlink("/proc/self/exe", outPath, bufferSize); + if (resSize < 0) + { + return SLANG_FAIL; + } + if (resSize >= bufferSize) + { + return SLANG_E_BUFFER_TOO_SMALL; + } + // Zero terminate + outPath[resSize - 1] = 0; + return SLANG_OK; +# else + String text = Slang::File::ReadAllText("/proc/self/maps"); + UInt startIndex = text.IndexOf('/'); + if (startIndex == UInt(-1)) + { + return SLANG_FAIL; + } + UInt endIndex = text.IndexOf("\n", startIndex); + endIndex = (endIndex == UInt(-1)) ? text.Length() : endIndex; + + auto path = text.SubString(startIndex, endIndex - startIndex); + + if (path.getLength() < bufferSize) + { + ::memcpy(outPath, path.begin(), path.getLength()); + outPath[path.getLength()] = 0; + return SLANG_OK; + } + + *ioSize = path.getLength() + 1; + return SLANG_E_BUFFER_TOO_SMALL; +# endif + +#elif SLANG_APPLE_FAMILY + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dyld.3.html + uint32_t size = uint32_t(*ioSize); + switch (_NSGetExecutablePath(outPath, &size)) + { + case 0: return SLANG_OK; + case -1: + { + *ioSize = size; + return SLANG_E_BUFFER_TOO_SMALL; + } + default: break; + } + return SLANG_FAIL; +#else + return SLANG_E_NOT_IMPLEMENTED; +#endif + } + + static String _getExecutablePath() + { + List buffer; + // Guess an initial buffer size + buffer.SetSize(1024); + + while (true) + { + const size_t size = buffer.Count(); + size_t bufferSize = size; + SlangResult res = _calcExectuablePath(buffer.Buffer(), &bufferSize); + + if (SLANG_SUCCEEDED(res)) + { + return String(buffer.Buffer()); + } + + if (res != SLANG_E_BUFFER_TOO_SMALL) + { + // Couldn't determine the executable string + return String(); + } + + // If bufferSize changed it should be the exact fit size, else we just make the buffer bigger by a guess (50% bigger) + bufferSize = (bufferSize > size) ? bufferSize : (bufferSize + bufferSize / 2); + buffer.SetSize(bufferSize); + } + } + + /* static */String Path::GetExecutablePath() + { + static String executablePath = _getExecutablePath(); + return executablePath; + } + Slang::String File::ReadAllText(const Slang::String & fileName) { StreamReader reader(new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite)); diff --git a/source/core/slang-io.h b/source/core/slang-io.h index 177671442..654a88d1a 100644 --- a/source/core/slang-io.h +++ b/source/core/slang-io.h @@ -50,6 +50,11 @@ namespace Slang static SlangResult GetPathType(const String & path, SlangPathType* pathTypeOut); static SlangResult GetCanonical(const String & path, String& canonicalPathOut); + + /// Returns the executable path + /// @return The path in platform native format. Returns empty string if failed. + static String GetExecutablePath(); + }; } diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index bb4aa66ff..128b0b481 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -225,7 +225,6 @@ - diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 18530d11a..b88fbcc25 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -42,6 +42,9 @@ Header Files + + Header Files + Header Files @@ -171,9 +174,6 @@ Header Files - - Header Files - Header Files @@ -195,9 +195,6 @@ Header Files - - Header Files - diff --git a/tools/slang-test/options.cpp b/tools/slang-test/options.cpp index 2b9ec6c06..693d1939c 100644 --- a/tools/slang-test/options.cpp +++ b/tools/slang-test/options.cpp @@ -291,5 +291,15 @@ static bool _isSubCommand(const char* arg) return SLANG_FAIL; } + if (optionsOut->binDir.Length() == 0) + { + // If the binDir isn't set try using the path to the executable + String exePath = Path::GetExecutablePath(); + if (exePath.Length()) + { + optionsOut->binDir = Path::GetDirectoryName(exePath); + } + } + return SLANG_OK; } diff --git a/tools/slang-test/options.h b/tools/slang-test/options.h index 72dc66035..48c06463c 100644 --- a/tools/slang-test/options.h +++ b/tools/slang-test/options.h @@ -23,7 +23,7 @@ struct TestCategorySet { public: /// Find a category with the specified name. Returns nullptr if not found - TestCategory * find(Slang::String const& name); + TestCategory* find(Slang::String const& name); /// Adds a category with the specified name, and parent. Returns the category object. /// Parent can be nullptr TestCategory* add(Slang::String const& name, TestCategory* parent); @@ -40,8 +40,8 @@ struct Options { char const* appName = "slang-test"; - // Directory to use when looking for binaries to run - char const* binDir = ""; + // Directory to use when looking for binaries to run. If empty it's not set. + Slang::String binDir; // only run test cases with names that have this prefix. char const* testPrefix = nullptr; diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index e475d4b5a..232ec5d53 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -413,7 +413,7 @@ OSError spawnAndWaitSharedLibrary(TestContext* context, const String& testPath, builder << "slang-test"; - if (options.binDir) + if (options.binDir.Length()) { builder << " -bindir " << options.binDir; } @@ -430,7 +430,7 @@ OSError spawnAndWaitSharedLibrary(TestContext* context, const String& testPath, context->reporter->messageFormat(TestMessageType::Info, "%s\n", builder.begin()); } - auto func = context->getInnerMainFunc(String(context->options.binDir), exeName); + auto func = context->getInnerMainFunc(context->options.binDir, exeName); if (func) { StringBuilder stdErrorString; @@ -765,7 +765,7 @@ static RenderApiFlags _getAvailableRenderApiFlags(TestContext* context) { // Try starting up the device OSProcessSpawner spawner; - spawner.pushExecutablePath(String(context->options.binDir) + "render-test" + osGetExecutableSuffix()); + spawner.pushExecutablePath(Path::Combine(context->options.binDir, String("render-test") + osGetExecutableSuffix())); _addRenderTestOptions(context->options, spawner); // We just want to see if the device can be started up spawner.pushArgument("-only-startup"); @@ -895,7 +895,7 @@ String findExpectedPath(const TestInput& input, const char* postFix) static void _initSlangCompiler(TestContext* context, OSProcessSpawner& spawnerOut) { - spawnerOut.pushExecutablePath(String(context->options.binDir) + "slangc" + osGetExecutableSuffix()); + spawnerOut.pushExecutablePath(Path::Combine(context->options.binDir, String("slangc") + osGetExecutableSuffix())); if (context->options.verbosePaths) { @@ -1007,7 +1007,7 @@ TestResult runReflectionTest(TestContext* context, TestInput& input) OSProcessSpawner spawner; - spawner.pushExecutablePath(String(options.binDir) + "slang-reflection-test" + osGetExecutableSuffix()); + spawner.pushExecutablePath(Path::Combine(options.binDir, String("slang-reflection-test") + osGetExecutableSuffix())); spawner.pushArgument(filePath); for( auto arg : input.testOptions->args ) @@ -1471,7 +1471,7 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons OSProcessSpawner spawner; - spawner.pushExecutablePath(String(context->options.binDir) + "render-test" + osGetExecutableSuffix()); + spawner.pushExecutablePath(Path::Combine(context->options.binDir, String("render-test") + osGetExecutableSuffix())); spawner.pushArgument(filePath999); _addRenderTestOptions(context->options, spawner); @@ -1589,7 +1589,7 @@ TestResult doRenderComparisonTestRun(TestContext* context, TestInput& input, cha OSProcessSpawner spawner; - spawner.pushExecutablePath(String(context->options.binDir) + "render-test" + osGetExecutableSuffix()); + spawner.pushExecutablePath(Path::Combine(context->options.binDir, String("render-test") + osGetExecutableSuffix())); spawner.pushArgument(filePath); _addRenderTestOptions(context->options, spawner); -- cgit v1.2.3