diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-05-11 16:34:19 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-11 16:34:19 -0700 |
| commit | e2c2c220c642cc5f1c622f909d0ddfd22e6c04d4 (patch) | |
| tree | 2f8b2faa3ff61d07e106d4f049aa600b14ad8cf8 /premake5.lua | |
| parent | 34ecdb71c04232fba4b097f04fc358c57e704e26 (diff) | |
Generate Visual Studio projects using Premake (#557)
* Generate Visual Studio projects using Premake
This change adds a `premake5.lua` file that allows us to generate our Visual Studio solution using Premake 5 (https://premake.github.io/).
The existing Visual Studio solution/projects are now replaced with the Premake-generated ones, and project contributors will be expected to update these by running premake after adding/removing files.
I have *not* changed the Linux `Makefile` build at all, because that file is also used for things like running our tests, so that clobbering it with a premake-generated `Makefile` would break our continuous testing.
Hopefully future changes can switch to a generated `Makefile` and perhaps even add an XCode project as well.
Notes:
* The `build/slang-build.props` file is no longer needed/used, so it has been removed.
* The `slang-eval-test` test fixture wasn't following our naming conventions for its directory path, so it was updated to streamline the Premake build configuration work. This required changes to the `Makefile` as well
* Some seemingly unncessary preprocessor definitions that were specified for `core` and `slang-glslang` have been dropped. We will see if anything breaks from that.
* Possible fixup for Premake vpath issue
Premake's `vpath` feature seems to be nondeterministic about the order it applies filters (because Lua isn't deterministic about the order of entries in a key/value table), and as a result we can end up in a weird case where it decides that a `foo.cpp.h` file matches the `**.cpp` filter (I'm not sure why) before it tests against the `**.h` filter.
This change uses an (undocumented) Premake facility to set `vpath` using a list of singleton tables, which seems to fix the order in which things get tested.
* Remove support for "single-file" build of Slang
The `hello` example was the only bit of code that uses the "single-file" way of building Slang, and this had already run up against limitations of the Visual Studio compilers in its Debug|x64 build.
Rather than mess with Premake to make it pass through the `/bigobj` linker flag that is needed to work around the issue, it makes more sense just to stop using/supporting the feature since we wouldn't want users to depend on it anyway (our documentation no longer refers to it).
While I was at it I went ahead and made sure that the `SLANG_DYNAMIC` flag doesn't need to be set manually, so that instead there is a non-default `SLANG_STATIC` option (not that we have a static-library build of Slang at the moment).
Diffstat (limited to 'premake5.lua')
| -rw-r--r-- | premake5.lua | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/premake5.lua b/premake5.lua new file mode 100644 index 000000000..390d67a92 --- /dev/null +++ b/premake5.lua @@ -0,0 +1,539 @@ +-- premake5.lua + +-- This file describes the build configuration for Slang so +-- that premake can generate platform-specific build files +-- using Premake 5 (https://premake.github.io/). +-- +-- To update the build files that are checked in to the Slang repository, +-- run a `premake5` binary and specify the appropriate action, e.g.: +-- +-- premake5.exe --os=windows vs2015 +-- +-- If you are trying to build Slang on another platform, then you +-- can try invoking `premake5` for your desired OS and build format +-- and see what happens. +-- +-- If you are going to modify this file to change/customize the Slang +-- buidl, then you may need to read up on Premake's approach and +-- how it uses/abuses Lua syntax. A few important things to note: +-- +-- * Everything that *looks* like a declarative (e.g., `kind "SharedLib"`) +-- is actually a Lua function call (e.g., `kind("SharedLib")`) that +-- modifies the behind-the-scenes state that describes the build. +-- +-- * Many of these function calls are "sticky" and affect subsequent +-- calls, so ordering matters a *lot*. This file uses indentation to +-- represent some of the flow of state, but it is important to recognize +-- that the indentation is not semantically significant. +-- +-- * Because the configuration logic is just executable Lua code, we +-- can capture and re-use bits of configuration logic in ordinary +-- Lua subroutines. +-- +-- Now let's move on to the actual build: + +-- The "workspace" represents the overall build (the "solution" in +-- Visual Studio terms). It sets up basic build settings that will +-- apply across all projects. +-- +workspace "slang" + -- We will support debug/release configuration and x86/x64 builds. + configurations { "Debug", "Release" } + platforms { "x86", "x64" } + + -- The output binary directory will be derived from the OS + -- and configuration options, e.g. `bin/windows-x64/debug/` + targetdir "bin/%{cfg.system}-%{cfg.platform:lower()}/%{cfg.buildcfg:lower()}" + + -- The intermediate ("object") directory will use a similar + -- naming scheme to the output directory, but will also use + -- the project name to avoid cases where multiple projects + -- have source files with the same name. + objdir "intermediate/%{cfg.system}-%{cfg.platform:lower()}/%{cfg.buildcfg:lower()}/%{prj.name}" + + -- Statically link to the C/C++ runtime rather than create a DLL dependency. + flags { "StaticRuntime" } + + -- Once we've set up the common settings, we will make some tweaks + -- that only apply in a subset of cases. Each call to `filter()` + -- changes the "active" filter for subsequent commands. In + -- effect, those commands iwll be ignored when the conditions of + -- the filter aren't satisfied. + + -- Our `x64` platform should (obviously) target the x64 + -- architecture and similarly for x86. + filter { "platforms:x64" } + architecture "x64" + filter { "platforms:x86" } + architecture "x86" + + -- When compiling the debug configuration, we want to turn + -- optimization off, make sure debug symbols are output, + -- and add the same preprocessor definition that VS + -- would add by default. + filter { "configurations:debug" } + optimize "Off" + symbols "On" + defines { "_DEBUG" } + + -- For the release configuration we will turn optimizations on + -- (we do not yet micro-manage the optimization settings) + -- and set the preprocessor definition that VS would add by default. + filter { "configurations:release" } + optimize "On" + defines { "NDEBUG" } + +-- +-- We are now going to start defining the projects, where +-- each project builds some binary artifact (an executable, +-- library, etc.). +-- +-- All of our projects follow a common structure, so rather +-- than reiterate a bunch of build settings, we define +-- some subroutines that make the configuration as concise +-- as possible. +-- +-- First, we will define a helper routine for adding all +-- the relevant files from a given directory path: +-- +function addSourceDir(path) + files + { + path .. "/*.cpp", -- C++ source files + path .. "/*.slang", -- Slang files (for our stdlib) + path .. "/*.h", -- Header files + path .. "/*.hpp", -- C++ style headers (for glslang) + path .. "/*.natvis", -- Visual Studio debugger visualization files + } +end +-- +-- Next we will define a helper routine that all of our +-- projects will bottleneck through. Here `name` is +-- the name for the project (and the base name for +-- whatever output file it produces), while `baseDir` +-- is the parent directory of the project's directory. +-- +-- E.g., for the `slangc` project, the source code +-- is nested in `source/`, so we'd (indirectly) call: +-- +-- baseSlangProject("slangc", "source") +-- +function baseSlangProject(name, baseDir) + + -- The project directory will be nested inside + -- the base directory using the project's name. + -- + local projectDir = baseDir .. "/" .. name + + -- Start a new project in premake. This switches + -- the "current" project over to the newly created + -- one, so that subsequent commands affect this project. + -- + project(name) + + -- Set the location where the project file will be placed. + -- We set the project files to reside in their source + -- directory, because in Visual Studio the default + -- working directory when launching a project in the + -- debugger is its project directory. This ensures that + -- examples will work as expected for VS users. + -- + -- TODO: consider only setting this for examples, since + -- it is less relevant to other projects. + -- + location(projectDir) + + -- All of our projects are written in C++. + -- + language "C++" + + -- Since we know the project directory, we can go ahead + -- and add any source files locate there. + -- + -- Note that we do *not* recurse into subdirectories, + -- so projects that spread their source over multiple + -- directories will need to take more steps. + -- + addSourceDir(projectDir) + + -- By default, Premake generates VS project files that + -- reflect the directory structure of the source code. + -- While this is nice in principle, it creates messy + -- results in practice for our projects. + -- + -- Instead, we will use the `vpaths` feature to imitate + -- the default VS behavior of grouping files into + -- virtual subdirectories (VS calls them "filters") for + -- header and source files respectively. + -- + -- Note: We are setting `vpaths` using a list of key/value + -- tables instead of just a key/value table, since this + -- appears to be an (undocumented) way to fix the order + -- in which the filters are tested. Otherwise we have + -- issues where premake will nondeterministically decide + -- the check something against the `**.cpp` filter first, + -- and decide that a `foo.cpp.h` file should go into + -- the `"Source Files"` vpath. That behavior seems buggy, + -- but at least we appear to have a workaround. + -- + vpaths { + { ["Header Files"] = { "**.h", "**.hpp"} }, + { ["Source Files"] = { "**.cpp", "**.slang", "**.natvis" } }, + } +end + +-- We can now use the `baseSlangProject()` subroutine to +-- define helpers for the different categories of project +-- in our source tree. +-- +-- For example, the Slang project has several tools that +-- are used during building/testing, but don't need to +-- be distributed. These always have their source code in +-- `tools/<project-name>/`. +-- +function tool(name) + -- We use the `group` command here to specify that the + -- next project we create shold be placed into a group + -- named "tools" in a generated IDE solution/workspace. + -- + -- This is used in the generated Visual Studio solution + -- to group all the tools projects together in a logical + -- sub-directory of the solution. + -- + group "tools" + + -- Now we invoke our shared project configuration logic, + -- specifying that the project lives under the `tools/` path. + -- + baseSlangProject(name, "tools") + + -- Finally, we set the project "kind" to produce a console + -- application. This is a reasonable default for tools, + -- and it can be overriden because Premake is stateful, + -- and a subsequent call to `kind()` would overwrite this + -- default. + -- + kind "ConsoleApp" +end + +-- "Standard" projects will be those that go to make the binary +-- packages for slang: the shared libraries and executables. +-- +function standardProject(name) + -- Because Premake is stateful, any `group()` call by another + -- project would still be in effect when we create a project + -- here (e.g., if somebody had called `tool()` before + -- `standardProject()`), so we are careful here to set the + -- group to an emptry string, which Premake treats as "no group." + -- + group "" + + -- A standard project has its code under `source/` + -- + baseSlangProject(name, "source") +end + +-- Finally we have the example programs that show how to use Slang. +-- +function example(name) + -- Example programs go into an "example" group + group "examples" + + -- They have their source code under `examples/<project-name>/` + baseSlangProject(name, "examples") + + -- By default, all of our examples are GUI applications. One some + -- platforms there is no meaningful distinction between GUI and + -- command-line applications, but it is significant on Windows and MacOS + -- + kind "WindowedApp" + + -- Every example needs to be able to include the `slang.h` header + -- if it is going to use Slang, so we might as well set up a suitable + -- include path here rather than make each example do it. + -- + includedirs { "." } + + -- The examples also need to link against the slang library, + -- so we specify that here rather than in each example. + links { "slang" } +end + +-- +-- With all of these helper routines defined, we can now define the +-- actual projects quite simply. For example, here is the entire +-- declaration of the "Hello, World" example project: +-- +example "hello" + uuid "E6385042-1649-4803-9EBD-168F8B7EF131" +-- +-- Note how we are calling our custom `example()` subroutine with +-- the same syntax sugar that Premake usually advocates for their +-- `project()` function. This allows us to treat `example` as +-- a kind of specialized "subclass" of `project` +-- +-- The call to `uuid()` in the definition of `hello` establishes +-- the UUID/GUID that will be used for the project in generated +-- formats that use these as unique identifiers (e.g., Visual +-- Studio solutions). Without this call, Premake will generate +-- a fresh UUID for a project each time its generation logic +-- runs, which can create spurious diffs. +-- + +-- Most of the other projects have more interesting configuration going +-- on, so let's walk through them in order of increasing complexity. +-- +-- The `core` project is a static library that has all the basic types +-- and routines that get shared across both the Slang compiler/runtime +-- and the various tool projects. It's build is pretty simple: +-- + +standardProject "core" + uuid "F9BE7957-8399-899E-0C49-E714FDDD4B65" + kind "StaticLib" + + -- For our core implementation, we want to use the most + -- aggressive warning level supported by the target, and + -- to treat every warning as an error to make sure we + -- keep our code free of warnings. + -- + warnings "Extra" + flags { "FatalWarnings" } + + +-- +-- `slang-generate` is a tool we use for source code generation on +-- the compiler. It depends on the `core` library, so we need to +-- declare that: +-- + +tool "slang-generate" + uuid "66174227-8541-41FC-A6DF-4764FC66F78E" + links { "core" } + + +-- +-- The `slang-test` test driver also uses the `core` library, and it +-- currently relies on include paths being set up so that it can find +-- the core headers: +-- + +tool "slang-test" + uuid "0C768A18-1D25-4000-9F37-DA5FE99E3B64" + includedirs { "." } + links { "core" } + +-- +-- The reflection test harness `slang-reflection-test` is pretty +-- simple, in that it only needs to link against the slang library +-- to do its job: +-- + +tool "slang-reflection-test" + uuid "22C45F4F-FB6B-4535-BED1-D3F5D0C71047" + includedirs { "." } + links { "slang" } + +-- +-- `slang-eval-test` is similarly easy to build: +-- +-- Note: `slang-eval-test` will probably be deprecated and its functionality +-- folded into `render-test`, but we aren't ready for that just yet. +-- + +tool "slang-eval-test" + uuid "205FCAB9-A13F-4980-86FA-F6221A7095EE" + includedirs { "." } + links { "core", "slang" } + +-- +-- The most complex testing tool we have is `render-test`, but from +-- a build perspective the most interesting thing about it is that for +-- our Windows build it requires a Windows 10 SDK. +-- +-- TODO: Try to make the build not require a fixed version of the Windows SDK. +-- Ideally we should just specify a *minimum* version. +-- +-- This test also requires Vulkan headers which we've placed in the +-- `external/` directory, and it also includes files from the `core` +-- library in ways that require us to set up `source/` as an include path. +-- +-- TODO: Fix that requirement. +-- + +tool "render-test" + uuid "96610759-07B9-4EEB-A974-5C634A2E742B" + includedirs { ".", "external", "source" } + links { "core", "slang" } + filter { "system:windows" } + systemversion "10.0.14393.0" + +-- +-- The `slangc` command-line application is just a very thin wrapper +-- around the Slang dynamic library, so its build is extermely simple. +-- One windows `slangc` uses the the `core` library for some UTF-16 +-- to UTF-8 string conversion before calling into `slang.dll`, so +-- it also depends on `core`: +-- + +standardProject "slangc" + uuid "D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7" + kind "ConsoleApp" + links { "core", "slang" } + +-- +-- TODO: Slang's current `Makefile` build does some careful incantations +-- to make sure that the binaries it generates use a "relative `RPATH`" +-- for loading shared libraries, so that Slang is not dependent on +-- being installed to a fixed path on end-user machines. Before we +-- can use Premake for the Linux build (or evenatually MacOS) we would +-- need to figure out how to replicate this incantation in premake. +-- + +-- +-- Now that we've gotten all the simple projects out of the way, it is time +-- to get into the more serious build steps. +-- +-- First up is the `slang` dynamic library project: +-- + +standardProject "slang" + uuid "DB00DA62-0533-4AFD-B59F-A67D5B3A0808" + kind "SharedLib" + links { "core" } + warnings "Extra" + flags { "FatalWarnings" } + + -- The way that we currently configure things through `slang.h`, + -- we need to set a preprocessor definitions to ensure that + -- we declare the Slang API functions for *export* and not *import*. + -- + defines { "SLANG_DYNAMIC_EXPORT" } + + -- The `standardProject` operation already added all the code in + -- `source/slang/*`, but we also want to incldue the umbrella + -- `slang.h` header in this prject, so we do that manually here. + files { "slang.h" } + + -- The most challenging part of building `slang` is that we need + -- to invoke the `slang-generate` tool to generate the version + -- of the Slang standard library that we embed into the compiler. + -- + -- First, we need to ensure that `slang-generate` gets built + -- before `slang`, so we declare a non-linking dependency between + -- the projects here: + -- + dependson { "slang-generate" } + + -- Next, we want to add a custom build rule for each of the + -- files that makes up the standard library. Those are + -- always named `*.meta.slang`, so we can select for them + -- using a `filter` and then use Premake's support for + -- defining custom build commands: + -- + filter "files:**.meta.slang" + -- Specify the "friendly" message that should print to the build log for the action + buildmessage "slang-generate %{file.relpath}" + + -- Specify the actual command to run for this action. + -- + -- Note that we use a single-quoted Lua string and wrap the path + -- to the `slang-generate` command in double quotes to avoid + -- confusing the Windows shell. It seems that Premake outputs that + -- path with forward slashes, which confused the shell if we don't + -- quote the executable path. + -- + buildcommands { '"%{cfg.targetdir}/slang-generate" %{file.relpath}' } + + -- Given `foo.meta.slang` we woutput `foo.meta.slang.h`. + -- This needs to be specified because the custom action will only + -- run when this file needs to be generated. + -- + buildoutputs { "%{file.relpath}.h" } + + -- We will specify an additional build input dependency on the `slang-generate` + -- tool itself, so that changes to the code for the tool cause the generation + -- step to be re-run. + -- + -- In order to get the file name right, we need to know the executable suffix + -- that the target platform will use. Premake might have a built-in way to + -- query this, but I couldn't find it, so I am just winging it for now: + -- + local executableSuffix = ""; + if(os.target() == "windows") then + executableSuffix = ".exe"; + end + -- + buildinputs { "%{cfg.targetdir}/slang-generate" .. executableSuffix } + +-- +-- The single most complicated part of our build is our custom version of glslang. +-- Is not really set up to produce a shared library with a usable API, so we have +-- our own custom shim API around it to invoke GLSL->SPIRV compilation. +-- +-- Glslang normally relies on a CMake-based build process, and its code is spread +-- across multiple directories with implicit dependencies on certain command-line +-- definitions. +-- +-- The following is a tailored build of glslang that pulls in the pieces we care +-- about whle trying to leave out the rest: +-- +standardProject "slang-glslang" + uuid "C495878A-832C-485B-B347-0998A90CC936" + kind "SharedLib" + includedirs { "external/glslang" } + + defines + { + -- `ENABLE_OPT` must be defined (to either zero or one) for glslang to compile at all + "ENABLE_OPT=0", + + -- We want to build a version of glslang that supports every feature possible, + -- so we will enable all of the supported vendor-specific extensions so + -- that they can be used in Slang-generated GLSL code when required. + -- + "AMD_EXTENSIONS", + "NV_EXTENSIONS", + } + + -- We will add source code from every directory that is required to get a + -- minimal GLSL->SPIR-V compilation path working. + addSourceDir("external/glslang/glslang/GenericCodeGen") + addSourceDir("external/glslang/glslang/MachineIndependent") + addSourceDir("external/glslang/glslang/MachineIndependent/preprocessor") + addSourceDir("external/glslang/glslang/OSDependent") + addSourceDir("external/glslang/OGLCompilersDLL") + addSourceDir("external/glslang/SPIRV") + addSourceDir("external/glslang/StandAlone") + + -- Unfortunately, blindly adding files like that also pulled in a declaration + -- of a main entry point that we do *not* want, so we will specifically + -- exclude that file from our build. + removefiles { "external/glslang/StandAlone/StandAlone.cpp" } + + -- Glslang includes some platform-specific code around DLL setup/teardown + -- and handling of thread-local storage for its multi-threaded mode. We + -- don't really care about *any* of that, but we can't remove it from the + -- build so we need to include the appropriate platform-specific sources. + + filter { "system:windows" } + -- On Windows we need to add the platform-specific sources and then + -- remove the `main.cpp` file since it tries to define a `DllMain` + -- and we don't want the default glslang one. + addSourceDir( "external/glslang/glslang/OSDependent/Windows" ) + removefiles { "external/glslang/glslang/OSDependent/Windows/main.cpp" } + + filter { "system:linux" } + addSourceDir("external/glslang/glslang/OSDependent/Unix") + +-- +-- With glslang's build out of the way, we've now covered everything we have +-- to build to get Slang and its tools/examples built. +-- +-- What is not included in this file yet is support for any custom `make` +-- targets for: +-- +-- * Invoking the test runner +-- * Packaging up binaries +-- * "Installing" Slang on a user's machine +--
\ No newline at end of file |
