terminal/build/rules/GenerateFeatureFlags.proj

104 lines
4.5 KiB
Plaintext
Raw Normal View History

Add support for branch- and branding-based feature flagging (#10361) This pull request implements a "feature flagging" system that will let us turn Terminal and conhost features on/off by branch, "release" status or branding (Dev, Preview, etc.). It's loosely modelled after the Windows OS concept of "Velocity," but only insofar as it is driven by an XML document and there's a tool that emits a header file for you to include. It only supports toggling features at compile time, and the feature flag evaluators are intended to be fully constant expressions. Features are added to `src\features.xml` and marked with a "stage". For now, the only stages available are `AlwaysDisabled` and `AlwaysEnabled`. Features can be toggled to different states using branch and branding tokens, as documented in the included feature flag docs. For a given feature Feature_XYZ, we will emit two fixtures visible to the compiler: 1. A preprocessor define `TIL_FEATURE_XYZ_ENABLED` (usable from MIDL, C++ and C) 2. A feature class type `Feature_XYZ` with a static constexpr member `IsEnabled()` (usable from C++, designed for `if constexpr()`). Like Velocity, we rely on the compiler to eliminate dead code caused by things that compile down to `if constexpr (false)`. :) Michael suggested that we could use `WindowsInbox` as a branding to determine when we were being built inside Windows to supplant our use of the `__INSIDE_WINDOWS` preprocessor token. It was brilliant. Design Decisions ---------------- * Emitting the header as part of an MSBuild project * WHY: This allows the MSBuild engine to ensure that the build is only run once, even in a parallel build situation. * Only having one feature flag document for the entire project * WHY: Ease. * Forcibly including `TilFeatureStaging` with `/FI` for all CL compiler invocations. * WHY: If this is a project-wide feature system, we should make it as easy as possible to use. * Emitting preprocessor definitions instead of constexpr/consteval * WHY: Removing entire functions/includes is impossible with `if constexpr`. * WHY: MIDL cannot use a `static constexpr bool`, but it can rely on the C preprocessor to remove text. * Using MSBuild to emit the text instead of PowerShell * WHY: This allows us to leverage MSBuild's `WriteOnlyWhenDifferent` task parameter to avoid changing the file's modification time when it would have resulted in the same contents. This lets us use the same FeatureStaging header across multiple builds and multiple branches and brandings _assuming that they do not result in a feature flag change_. * The risk in using a force-include is always that it, for some reason, determines that the entire project is out of date. We've gone to great lengths to make sure that it only does so if the features _actually materially changed_.
2021-06-11 01:09:52 +02:00
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- THIS PROJECT CANNOT BE LOADED INTO THE SOLUTION. -->
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Any CPU">
<Configuration>Release</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Fuzzing|Any CPU">
<Configuration>Fuzzing</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="AuditMode|Any CPU">
<Configuration>AuditMode</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Any CPU">
<Configuration>Debug</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>d97c3c61-53cd-4e72-919b-9a0940e038f9</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<IntermediateOutputPath>$(SolutionDir)obj\$(Configuration)\GenerateFeatureFlags\</IntermediateOutputPath>
<OpenConsoleCommonOutDir>$(SolutionDir)bin\$(Configuration)\</OpenConsoleCommonOutDir>
<_WTBrandingName Condition="'$(WindowsTerminalBranding)'=='Preview'">Preview</_WTBrandingName>
<_WTBrandingName Condition="'$(WindowsTerminalBranding)'=='Release'">Release</_WTBrandingName>
<_WTBrandingName Condition="'$(_WTBrandingName)'==''">Dev</_WTBrandingName>
</PropertyGroup>
<Target Name="_GenerateBranchAndBrandingCache">
<Exec Command="git.exe rev-parse --abbrev-ref HEAD"
CustomWarningRegularExpression="^fatal:.*"
ConsoleToMsBuild="true"
IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" ItemName="_GitBranchLines" />
</Exec>
<ItemGroup>
<_BrandingLines Include="$(_WTBrandingName)" />
</ItemGroup>
<WriteLinesToFile File="$(IntermediateOutputPath)branch_branding_cache.txt"
Lines="@(_GitBranchLines);@(_BrandingLines)"
Overwrite="true"
WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)branch_branding_cache.txt" />
<_BranchBrandingCacheFiles Include="$(IntermediateOutputPath)branch_branding_cache.txt" />
</ItemGroup>
</Target>
<Target Name="_RunFeatureFlagScript"
Inputs="@(FeatureFlagFile);@(_BranchBrandingCacheFiles)"
Outputs="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h"
DependsOnTargets="_GenerateBranchAndBrandingCache">
<MakeDir Directories="$(OpenConsoleCommonOutDir)\inc" />
<!-- This commandline is escaped like:
powershell -Command "&'$(SolutionDir)\tools\Generate-FeatureStagingHeader.ps1' -Path '%(FeatureFlagFile.FullPath)'' -Branding $(_WTBrandingName)"
which was the only way I could find to get it to obey spaces in the SolutionDir
-->
Add support for branch- and branding-based feature flagging (#10361) This pull request implements a "feature flagging" system that will let us turn Terminal and conhost features on/off by branch, "release" status or branding (Dev, Preview, etc.). It's loosely modelled after the Windows OS concept of "Velocity," but only insofar as it is driven by an XML document and there's a tool that emits a header file for you to include. It only supports toggling features at compile time, and the feature flag evaluators are intended to be fully constant expressions. Features are added to `src\features.xml` and marked with a "stage". For now, the only stages available are `AlwaysDisabled` and `AlwaysEnabled`. Features can be toggled to different states using branch and branding tokens, as documented in the included feature flag docs. For a given feature Feature_XYZ, we will emit two fixtures visible to the compiler: 1. A preprocessor define `TIL_FEATURE_XYZ_ENABLED` (usable from MIDL, C++ and C) 2. A feature class type `Feature_XYZ` with a static constexpr member `IsEnabled()` (usable from C++, designed for `if constexpr()`). Like Velocity, we rely on the compiler to eliminate dead code caused by things that compile down to `if constexpr (false)`. :) Michael suggested that we could use `WindowsInbox` as a branding to determine when we were being built inside Windows to supplant our use of the `__INSIDE_WINDOWS` preprocessor token. It was brilliant. Design Decisions ---------------- * Emitting the header as part of an MSBuild project * WHY: This allows the MSBuild engine to ensure that the build is only run once, even in a parallel build situation. * Only having one feature flag document for the entire project * WHY: Ease. * Forcibly including `TilFeatureStaging` with `/FI` for all CL compiler invocations. * WHY: If this is a project-wide feature system, we should make it as easy as possible to use. * Emitting preprocessor definitions instead of constexpr/consteval * WHY: Removing entire functions/includes is impossible with `if constexpr`. * WHY: MIDL cannot use a `static constexpr bool`, but it can rely on the C preprocessor to remove text. * Using MSBuild to emit the text instead of PowerShell * WHY: This allows us to leverage MSBuild's `WriteOnlyWhenDifferent` task parameter to avoid changing the file's modification time when it would have resulted in the same contents. This lets us use the same FeatureStaging header across multiple builds and multiple branches and brandings _assuming that they do not result in a feature flag change_. * The risk in using a force-include is always that it, for some reason, determines that the entire project is out of date. We've gone to great lengths to make sure that it only does so if the features _actually materially changed_.
2021-06-11 01:09:52 +02:00
<Exec
Command="powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy ByPass -Command &quot;&amp;&apos;$(SolutionDir)\tools\Generate-FeatureStagingHeader.ps1&apos; -Path &apos;%(FeatureFlagFile.FullPath)&apos; -Branding $(_WTBrandingName)&quot;"
Add support for branch- and branding-based feature flagging (#10361) This pull request implements a "feature flagging" system that will let us turn Terminal and conhost features on/off by branch, "release" status or branding (Dev, Preview, etc.). It's loosely modelled after the Windows OS concept of "Velocity," but only insofar as it is driven by an XML document and there's a tool that emits a header file for you to include. It only supports toggling features at compile time, and the feature flag evaluators are intended to be fully constant expressions. Features are added to `src\features.xml` and marked with a "stage". For now, the only stages available are `AlwaysDisabled` and `AlwaysEnabled`. Features can be toggled to different states using branch and branding tokens, as documented in the included feature flag docs. For a given feature Feature_XYZ, we will emit two fixtures visible to the compiler: 1. A preprocessor define `TIL_FEATURE_XYZ_ENABLED` (usable from MIDL, C++ and C) 2. A feature class type `Feature_XYZ` with a static constexpr member `IsEnabled()` (usable from C++, designed for `if constexpr()`). Like Velocity, we rely on the compiler to eliminate dead code caused by things that compile down to `if constexpr (false)`. :) Michael suggested that we could use `WindowsInbox` as a branding to determine when we were being built inside Windows to supplant our use of the `__INSIDE_WINDOWS` preprocessor token. It was brilliant. Design Decisions ---------------- * Emitting the header as part of an MSBuild project * WHY: This allows the MSBuild engine to ensure that the build is only run once, even in a parallel build situation. * Only having one feature flag document for the entire project * WHY: Ease. * Forcibly including `TilFeatureStaging` with `/FI` for all CL compiler invocations. * WHY: If this is a project-wide feature system, we should make it as easy as possible to use. * Emitting preprocessor definitions instead of constexpr/consteval * WHY: Removing entire functions/includes is impossible with `if constexpr`. * WHY: MIDL cannot use a `static constexpr bool`, but it can rely on the C preprocessor to remove text. * Using MSBuild to emit the text instead of PowerShell * WHY: This allows us to leverage MSBuild's `WriteOnlyWhenDifferent` task parameter to avoid changing the file's modification time when it would have resulted in the same contents. This lets us use the same FeatureStaging header across multiple builds and multiple branches and brandings _assuming that they do not result in a feature flag change_. * The risk in using a force-include is always that it, for some reason, determines that the entire project is out of date. We've gone to great lengths to make sure that it only does so if the features _actually materially changed_.
2021-06-11 01:09:52 +02:00
ConsoleToMsBuild="true"
StandardOutputImportance="low">
<Output TaskParameter="ConsoleOutput" ItemName="_FeatureFlagFileLines" />
</Exec>
<!--
We gather the feature flag output in MSBuild and emit the file so that we can take advantage of
WriteOnlyWhenDifferent. Doing this ensures that we don't rebuild the world when the branch changes
(if it results in a new TilFeatureStaging.h that would have had the same content/features as the previous one)
-->
<WriteLinesToFile File="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h"
Lines="@(_FeatureFlagFileLines)"
Overwrite="true"
WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h" />
</ItemGroup>
</Target>
<Target Name="Build" DependsOnTargets="_RunFeatureFlagScript" />
<Target Name="Clean">
<Delete Files="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h" />
</Target>
<ItemGroup>
<FeatureFlagFile Include="$(SolutionDir)\src\features.xml" />
</ItemGroup>
</Project>