Skip to content

Conversation

@sharder996
Copy link
Collaborator

@sharder996 sharder996 commented Dec 6, 2025

A simple first approach for adding feature flags in Multipass based on defining CMake variables through various CMake Presets. This PR is based on #4542 to showcase its use in conditionally enabling the integration of the Apple Virtualization Framework.

The general idea is to have one main preset; feature-flags-enabled that you can apply on any platform. Depending on your current platform, all common and relevant platform specific CMake variables will be defined. For example, on macOS, MULTIPASS_BACKENDS will be defined to apple, but on Linux and Windows, it will remain undefined.

CMake Presets does not support inheritance of conditional presets (see https://gitlab.kitware.com/cmake/cmake/-/issues/23283), so we are forced to have three presets, one for each platform. Features that are common to all platforms, i.e. availability zones, can still be added to a common preset, common-feature-flags, that all platform specific presets inherit from.

For CI, I rely on other developers to manually set ["vendor"]["enabled-in-ci"] in CMakePresets.json to signify it should be compiled in CI rather than try to parse the json file and evaluate whether or not there is any meaningful difference in "Edge" vs "Release" builds. This metadata is used to conditionally add another runner(s) to do a Debug build with the appropriate preset applied.

The rest is just making sure that Release and Edge; the name I've decided to give to builds with feature flags turned on, stay logically separated.

Example #4555 that has all feature flags turned on
Example #4557 that demonstrates a build failure within code that is enabled by a feature flag

As demonstrated, my philosophy was that a feature flag enabled build should fail the entire workflow and effectively block development in main. Ultimately, the development of a feature should be managed by an engineer, but the rest of team should not have to maintain it throughout its development cycle. For example, an engineer refactoring virtual_machine.h should not have to be responsible for also modifying all the virtual machine code for apple_virtual_machine.h.

Closes #4555
Closes #4557


MULTI-2401

@sharder996 sharder996 force-pushed the feature-flags branch 2 times, most recently from 19c7842 to a33ee1e Compare December 6, 2025 08:06
@sharder996 sharder996 changed the base branch from main to feature/apple-vz-boilerplate December 6, 2025 08:07
sharder996 and others added 7 commits December 6, 2025 02:33
We wrap all calls into the Virtualization Framework in a mockable
singleton for testing purposes
Co-authored-by: Mustafa Kemal Gılor <mustafa.gilor@canonical.com>
Signed-off-by: ScottH <59572507+sharder996@users.noreply.github.com>
@sharder996 sharder996 force-pushed the feature/apple-vz-boilerplate branch from 5450a6f to d87a67b Compare December 6, 2025 08:33
@codecov
Copy link

codecov bot commented Dec 6, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.20%. Comparing base (72edbd7) to head (5631adb).
⚠️ Report is 136 commits behind head on feature/apple-vz-boilerplate.

Additional details and impacted files
@@                       Coverage Diff                        @@
##           feature/apple-vz-boilerplate    #4552      +/-   ##
================================================================
+ Coverage                         87.19%   87.20%   +0.01%     
================================================================
  Files                               246      246              
  Lines                             14134    14134              
================================================================
+ Hits                              12323    12324       +1     
+ Misses                             1811     1810       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sharder996 sharder996 marked this pull request as ready for review December 7, 2025 15:34
@sharder996 sharder996 marked this pull request as draft December 7, 2025 15:34
@sharder996 sharder996 closed this Dec 7, 2025
@sharder996 sharder996 force-pushed the feature-flags branch 3 times, most recently from c3798fc to 2abd7fb Compare December 8, 2025 21:15
CMake Preset doesn't support conditionally inheriting over presets.
@sharder996 sharder996 marked this pull request as ready for review December 9, 2025 05:20
with:
script: |
const matrix = { include: [{
"build-type": "Debug",
Copy link
Member

@xmkg xmkg Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really related to this pr but we can get rid of the build-type now since build type is now defined by the preset.

"inherits": "common"
},
{
"name": "macOS-feature-flags",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presets are not a way to "declare" the option flags -- this still has to be done in a CMake file. I'd say it'd be better to declare all feature flags as options in a dedicated CMake module and include it as the first thing in the project. cmake_dependent_option() would be a good way to declare feature options with prerequisites, such as:

include(CMakeDependentOption)

# Foo is the prerequisite. Assume it's an implicitly defined platform variable (e.g. APPLE)
cmake_dependent_option(FOO_BAR_ENABLED "Enable foo bar" OFF FOO OFF)

if(FOO_BAR_ENABLED)
    message(STATUS "foo bar is enabled"*)
endif()

The message "foo bar is enabled" would only print when FOO is set. Otherwise, it's forced to be OFF regardless of what the user actually sets at configure time:

cmake -S . -B build -DFOO:BOOL=TRUE -DFOO_BAR_ENABLED:BOOL=TRUE # would print
cmake -S . -B build -DFOO:BOOL=FALSE -DFOO_BAR_ENABLED:BOOL=TRUE # would not print the message

That means it wouldn't matter either the preset or the configure parameters are setting a flag to true/false unless the precondition has met (e.g. platform or other hard requirements), meaning we can use a single preset for all platforms per configuration.

As for the use of the presets, they should be really about defining a specific way that the project is going to be built, for example the "CI", or the "public release" build could be the two presets to begin with. The CI preset would enable all the feature options whereas the public release would only enable the features that we intend to ship to the user. That way, the CI code becomes much simpler, and we can also reproduce the CI build locally.

Copy link
Contributor

@jimporter jimporter Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have to take a bit of time to think about how to implement the specifics, but what if we defined a new CMake helper function for our feature flags? That function could keep track of all defined feature flags for us. Then we could do the following:

# CMakeLists.txt

feature_flag(FOO_FEATURE ...)
feature_flag(BAR_FEATURE ...)
$ cmake -S . -B build -DENABLE_ALL_FEATURES=TRUE

That way, when one of us adds a new feature flag, we only have to define the flag in one spot (CMakeLists.txt), and the only other work we need to do is integrating the flag into the build (e.g. with a CMake if block or passing a preprocessor macro to the compiler).

@sharder996 sharder996 force-pushed the feature/apple-vz-boilerplate branch 3 times, most recently from cd9efab to 8fd2e2f Compare December 11, 2025 16:03
@sharder996 sharder996 force-pushed the feature/apple-vz-boilerplate branch 4 times, most recently from d655f8e to 0d4f2d5 Compare January 16, 2026 03:30
@sharder996
Copy link
Collaborator Author

Closing as this PoC is no longer needed and is bloating our open PR count. The CMake side has been implemented in #4552 and the CI side in #4570.

@sharder996 sharder996 closed this Jan 19, 2026
github-merge-queue bot pushed a commit that referenced this pull request Jan 23, 2026
As feature flags will be turned on by default, this PR selectively turns
them off for *new* nightly builds as well as any builds triggered from a
`release/*` branch.

Couple other notes:

- For nightly builds, Mattermost will be notified on a build failure
- For snap builds, an additive preset is used to turn off
`MP_ENABLE_ALL_FEATURES` with an environment variable that is set from
CI
- Unrelated to feature flags, but when a snap is published to the store,
it is deleted from GitHub artifacts

Theoretically, package naming should take care of itself provided the
`-noff` suffix is added through CMake and the Multipass version.

Closes #4552
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants