Skip to content

Instantly share code, notes, and snippets.

@lucasmeijer
Created May 30, 2018 07:39
Show Gist options
  • Save lucasmeijer/bb8c4e9cd08285a1e6bdb6e9e96ddd01 to your computer and use it in GitHub Desktop.
Save lucasmeijer/bb8c4e9cd08285a1e6bdb6e9e96ddd01 to your computer and use it in GitHub Desktop.
/* Visual Studio project generation is quite a topic. Grab a coffee.
A visual studio _project_ always has a "Configuration" string, and a "Platform" string.
You're free to choose whatever configuration string you want, but the platform string has
to be from a finite set of known values. ("Win32","x64","ARM", "ORBIS","CRT"}.
Some visual studio features (I'm not sure exactly which, but at minimum debugging, and some console things),
require that the platform value is set correctly. You cannnot debug an orbis executable without having its
platform set to ORBIS. Platforms that don't have any fancy VS features (like emscripten) are free to just set Platform to x64.
So far so good, all very reasonable.
Now a solution has multiple projects. Some of them might share similar configuration strings, some might not.
A solution also has a list of Configurations, as well as a list of "SolutionPlatforms". both of these lists
can contrain arbitrary values. In the .sln file, each combination of SolutionConfiguration/SolutionPlatform needs
to be mapped for each project in the solution, to which ProjectConfiguration/ProjectPlatform combo it should map.
The two dropdowns that you see in the visual studio user interface, they control the SolutionConfiguration, and the
SolutionPlatform. The mapping of solutioncombo to per-project combo is stored in the .sln file, and can be visually
inspected using the configuration manager UI.
Traditional projects use the ProjectPlatform to switch between different architectures. Win32/x64/ARM for instance.
Bee's project generation chooses to not do that, and instead to only use the configuration string. A ProjectConfiguration string
for a NativeProgramConfiguration would look like "Debug_UWP_ARMv7_Lump".
The only thing we do with the projectplatform string, is to ensure it's set to the value that VS requires it to be for a project
to properly function. On the solution level, we only setup a single "Default" SolutionPlatform, and have that map to the only
valid value for each project.
Different NativeProgramConfigurations that have different architectures are "encoded" by making multiple ProjectConfigurations for them.
This choice is made like this for 1) simplicity, 2) it makes it much harder to select through the UI a combination that
does not exist. (traditional setup allows you to choose ARM platform for a PS4 configuration, just because there is another
program in the solution that does have ARM as a valid platform. 3) for symmetry wih other IDE's who tend to have
a simpler setup that has one list of configs.
Ok, so we have a bunch of projects that each have a list of valid ProjectConfigurations. (ala "Debug_UWP_ARMv7_Lump)
Now we need to make a list of SolutionConfigurations. The strings we use for solution configurations have some more constraints,
because it's the list that shows up in the UI. The UI they show up in is very narrow, so really short names help a lot.
The list is also used to choose what is the default SolutionConfiguration, so we want to make sure we setup a reasonable default.
The UI is consumed by humans, so it would be nice if it's not 200 items long, and it would be nice if popular configs are near the top.
All ProjectConfiguration/ProjectPlatform combos for all projects need to be "selectable" by selecting a SolutionConfiguration.
If a NativeProgram only has one NativeProgramConfiguration, there is no need for that NativeProgram to cause an extra SolutionConfiguration
to be created. Any SolutionConfiguration will map the the only ProjectConfiguration.
We always make a "Debug" and a "Release" solution configuration. If a NativeProgram only has 2 NativeProgramConfigurations, that only
differ in CodeGen, we make Debug solutionconfig map to the CodeGen.Debug one, and Release solution config map to the CodeGen.Release one.
In this case there's also no reason for this NativeProgram to cause an additional SolutionConfiguration to be added.
If a NativeProgram has these NativeProgramConfigurations, we would create these additional solutionconfigurations:
(CodeGen.Debug, WindowsPlatform, x86, Lump) Win_x86
(CodeGen.Release, WindowsPlatform, x86, Lump) Win_x86_Release
(CodeGen.Debug, WindowsPlatform, x64, Lump) Win
(CodeGen.Debug, WindowsPlatform, x64,NoLump) Win_NoLump
(CodeGen.Release, WindowsPlatform, x64, Lump) Win_Release
(CodeGen.Debug, UniversalWindowsPlatform, x64, Lump) UWP
(CodeGen.Release, UniversalWindowsPlatform, x64, Lump) UWP_Release
(CodeGen.Debug, UniversalWindowsPlatform, ARMv7, Lump) UWP_ARMv7
(CodeGen.Release, UniversalWindowsPlatform, ARMv7, Lump) UAP_ARMv7_Release
(CodeGen.Release, EmsciprtenPlatform, AsmJs, Lump) Emscripten
Notice how we always start with the platform name. And then only encode the architecture if there are 1) multiple NativeProgramConfigurations in the solution
with that specific platform, that have different architectures, 2) _and_ the architecture is different from what is the "most natural" architecture for this
platform. Same logic is applied to codegen and lumping. Most natural codegen is always debug, most natural lump setting is lumping. We then sort the list
by making entries that have more of their values set to the "natural" value to be at the top, so the sorted list ends up being:
Debug
Release
Win
Win_x86
UWP
UWP_ARMv7
Emscripten
Win_Relase
Win_x86_Release
UWP_Release
UWP_ARMv7_Release
//TODO: Talk to aras about "ehm, can we even sort?!"
The reason we always make a Debug and Release config, is that it's often desirable to have a solution config that just makes all projects map to their "most natural platform,
most natural architecture.".
Now that we have our list of SolutionConfigurations, the only thing left to do is decide for each project, which ProjectConfiguration this SolutionConfiguration maps to.
For many programs there will not be a perfect match. Like when my project is not setup for Emscripten, but the SolutionConfiguration is set to "Emscripten" anyway. We do the
mapping process like this:
- we need to choose a NativeProgramConfiguration this solutionconfiguration maps to, from our finite set of valid NativeProgramConfiguration.
- our candidate list starts with all valid NativeProgramConfigurations for this NativeProgram.
- if any NativeProgramConfigurations match the Platform of the chosen SolutionConfiguration, our candidate list narrows to those. If none match, candidate list stays the same.
- If any of the remaining candidates have a matching architecture as the chosen SolutionConfiguration, our candidatelist narrows to those. If none match, candidate list stays the same.
- if any of the remaining candidates have a matching codegen as the chosen SolutionConfiguration, our candidate list narrows to those. If none match, candidate list stays the same.
- If any of the remaining candidates have a matching lumpsetting as the chosen SolutionConfiguration, our candidateslist narrows to those. If none match, cadidate list stays the same.
- we choose the first of the remaining candidates.
- If NativeProgramConfiguration is subclassed and more settings have been addded (UnityPlayerConfiguration adds ScriptingBackend and DevelopmentPlayer settings), those get processed the same way.
The order in which all settings are filtered should be in "most important settings first, least important settings last"
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment