- Which command best expresses this requirement after
parser_demo has been created? Answer: target_compile_features(parser_demo PRIVATE cxx_std_20)
When working with CMake build targets, the key distinction to understand is target-scoped vs. directory-scoped commands, and feature abstraction vs. raw flags.
CMake offers two ways to set compiler behavior: you can specify abstract features (letting CMake pick the right compiler flag), or you can hard-code raw flags like -std=c++20. The question asks for something that applies only to parser_demo and lets CMake choose the compiler-specific option — that's exactly what target_compile_features(parser_demo PRIVATE cxx_std_20) does. The PRIVATE keyword scopes the requirement to parser_demo alone, and cxx_std_20 is a CMake-managed feature name, so CMake translates it to whatever flag your compiler needs. This is option C, and it's the correct answer.
Option A uses set(CMAKE_CXX_FLAGS ...), which modifies a global variable affecting every target in the directory — the opposite of what the passage requires. Option B, add_compile_options(-std=c++20), also applies directory-wide to all targets created afterward, not just parser_demo, and it hard-codes a GCC/Clang-style flag that won't work on MSVC. Option D, target_link_options, controls the linker command, not the compiler — passing -std=c++20 there has no effect on compilation and is simply the wrong command for the job.
A useful pattern to remember: whenever a CMake question mentions "only this target" and "let CMake choose the flag," reach for target_compile_features with PRIVATE. The word features signals abstraction; the word target_ signals per-target scope.
- What should happen when CMake configures this project in an out-of-source build directory?
Answer: Configuration succeeds because main.cpp is resolved relative to the src source directory.
When working with CMake's directory model, the key concept to understand is that **source file paths are always resolved relative to the CMakeLists.txt file that declares them**, not relative to the project root.
In this scenario, src/CMakeLists.txt calls add_executable(analyzer main.cpp). Because this command lives in src/CMakeLists.txt, CMake interprets main.cpp as src/main.cpp. Since that file exists, configuration succeeds — making A the correct answer. The add_subdirectory(src) call in the root simply tells CMake to process that subdirectory, and once it does, the subdirectory's CMakeLists.txt becomes the active context for resolving paths.
B is wrong because it describes a common misconception — that all paths resolve from the project root. CMake actually tracks the "current source directory" (CMAKE_CURRENT_SOURCE_DIR) as it descends into subdirectories, and uses that as the base for relative paths.
C is wrong because out-of-source builds separate build artifacts from source files, but source file resolution is entirely unaffected by the build directory's contents. You never need to copy source files anywhere.
D is wrong because project() is only needed once at the top level (or optionally in sub-projects for naming purposes). It has nothing to do with how source file paths are resolved within subdirectory CMakeLists.txt files.
A useful rule of thumb: whenever you see add_executable or add_library with relative paths, mentally prepend CMAKE_CURRENT_SOURCE_DIR — the directory containing that specific CMakeLists.txt file.
- Which action most directly disables the option while preserving the existing build directory?
Answer: Run cmake -S . -B build -DBUILD_TOOLS=OFF to update the cached value during reconfiguration.
When working with CMake, you need to understand the difference between a cache variable and a default value. Once CMake configures a build directory, variable values are stored in CMakeCache.txt. On subsequent runs, CMake reads that cached value — not the default in CMakeLists.txt. This is why changing ON to OFF in the source file has no effect on an already-configured build directory: the cache entry wins.
The most direct fix is A: passing -DBUILD_TOOLS=OFF on the command line explicitly overwrites the cached value during reconfiguration. CMake updates CMakeCache.txt with the new value, and the tools are disabled immediately without losing any other configuration in the existing build directory.
B is wrong because cmake --build invokes the underlying build system (like Make or Ninja) and has no mechanism for setting CMake cache variables. The --target flag specifies a build target, not a CMake option — this command would simply error out or build the wrong thing.
C (-UBUILD_TOOLS) removes the cache entry entirely rather than setting it to OFF. After removal, the new default of OFF would take effect — so this could eventually disable the tools, but it's indirect and relies on the changed default, making A more direct and explicit.
D is wrong because --clean-first only deletes compiled artifacts before rebuilding. It does not touch CMakeCache.txt or re-evaluate option defaults.
Study tip: Remember that CMake's cache is persistent — when you need to change a cached variable, always use -DVAR=VALUE explicitly rather than relying on source-level defaults.
- Which workflow most reliably selects the new compiler for a separate configuration?
Answer: Configure a new directory with cmake -S . -B build-alt -DCMAKE_CXX_COMPILER=/path/to/compiler.
When working with CMake, it's important to understand that compiler selection happens at configuration time, not build time — and once a build directory is initialized, its compiler choice is locked in via the cache.
The cleanest way to evaluate a different compiler without touching your existing build is exactly what option B describes: run cmake -S . -B build-alt -DCMAKE_CXX_COMPILER=/path/to/compiler. This creates a fresh, isolated build directory (build-alt) configured from scratch with your chosen compiler. CMake records this in the new directory's CMakeCache.txt, and the original build/ directory remains completely untouched. You get a true side-by-side comparison.
Option A is fundamentally flawed because cmake --build invokes the build tool (like make or ninja) — it does not re-run configuration. Passing -DCMAKE_CXX_COMPILER at build time has no effect; CMake variables are configuration-phase concepts and are silently ignored here.
Option C is tempting but dangerous. Manually editing CMakeCache.txt mid-project can leave the cache in an inconsistent state — other cached paths, flags, and feature-detection results were computed assuming the old compiler. CMake won't automatically re-run its compiler checks, so you risk subtle build failures or silent misconfiguration.
Option D is nonsensical. CMake reads CMakeLists.txt as a build-system generator — you don't "run a compiler on it." Invoking the compiler directly on CMakeLists.txt produces nothing useful.
Study tip: Whenever a CMake question involves changing a fundamental setting like the compiler, think "new configuration = new build directory." The -B flag is your friend for clean, parallel setups.
- Which sequence provides the required configuration information for both generator categories?
Answer: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release and then cmake --build build --config Release
When working with CMake, you need to understand that single-configuration generators (like Unix Makefiles) and multi-configuration generators (like Visual Studio) handle build type selection differently — and a robust command sequence must satisfy both.
Single-config generators require CMAKE_BUILD_TYPE to be set at configure time (the cmake -S . -B build step), because they generate only one build system for a single configuration. Multi-config generators, on the other hand, ignore CMAKE_BUILD_TYPE entirely and instead rely on the --config flag passed at build time (the cmake --build step) to select among multiple available configurations.
This makes D the correct answer: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release satisfies the single-config generator by embedding the build type into the cache, while cmake --build build --config Release satisfies the multi-config generator by specifying the configuration at build time. Both generators receive the information they need.
Answer A fails immediately because it omits CMAKE_BUILD_TYPE from the configure step, leaving single-config generators with no build type defined — they'll typically default to an empty or Debug configuration.
Answer B reverses the logic: --config is not a valid configure-step flag and has no effect there, while passing -DCMAKE_BUILD_TYPE=Release to cmake --build is ignored because cache variables aren't accepted at build time.
Answer C only sets CMAKE_BUILD_TYPE, which works for single-config generators but gives multi-config generators no guidance, since they require --config at build time.
A useful rule of thumb: configure-time for single-config, build-time for multi-config — you need both flags together to write a generator-agnostic CMake workflow.
- Which CMake change most directly supplies the missing target-level dependency and linker input?
Answer: Add target_link_libraries(app PRIVATE helper) after both targets have been created.
When you see a CMake linking error about unresolved symbols, your first instinct should be to ask: "Does the linker actually know to include this library?" Compiling and linking are separate steps, and CMake manages them through explicit target relationships — not implicit file proximity.
The fix here is C: target_link_libraries(app PRIVATE helper). This single command does two things simultaneously — it tells the linker to include the helper library when building app, and it establishes a build dependency so CMake ensures helper is compiled before app links. That's exactly what "target-level dependency and linker input" means in the question.
A is a workaround, not a solution to the described architecture. Adding helper.cpp directly to app via target_sources sidesteps the library entirely — you'd now be compiling the same translation unit twice (once in the library, once in the executable), which defeats the purpose of having a separate library target and can cause duplicate symbol problems.
B reverses the dependency direction entirely. add_dependencies(helper app) makes helper wait for app to finish — the opposite of what you need. Even if the direction were correct, add_dependencies only controls build ordering, not linker inputs. The unresolved symbol would remain.
D uses the old, discouraged link_directories approach, which adds a filesystem search path for raw library files. It doesn't reference CMake's helper target at all, so CMake still won't pass the library to the linker correctly.
A reliable rule of thumb: in modern CMake, every linker relationship should flow through target_link_libraries. If you're seeing undefined symbol errors, that's almost always the first place to look.
- Which command sequence follows the intended CMake and CTest workflow?
Answer: cmake -S . -B build, then cmake --build build, then ctest --test-dir build
When working with CMake and CTest, always think in three distinct phases: configure, build, then test. Each phase depends on the previous one completing successfully, and skipping steps or reordering them breaks the workflow entirely.
The correct sequence is B. You first run cmake -S . -B build to configure the project — this reads your CMakeLists.txt, resolves dependencies, and generates build system files inside the build/ directory. Next, cmake --build build compiles the source files and produces the app executable inside that same build directory. Finally, ctest --test-dir build tells CTest exactly where to find the test registry and the executable, so it doesn't depend on your shell's current working directory. This is precisely what the question asks for.
A is backwards and nonsensical — you can't build before configuring, and specifying --test-dir . points CTest at the source root rather than the build directory where the executable actually lives. C attempts to run tests before the project is even configured or built, which means CTest has no test registry to read and no executable to invoke — it would simply fail immediately. D mixes ctest --build-and-test incorrectly; that flag is a specialized mode for automated testing pipelines with different syntax, and the final cmake --build . points to the source root rather than the build directory, making it invalid.
A useful mental shortcut: Configure → Build → Test, always in that order, and always point each tool at the build directory, not the source root.
- Which command sequence correctly configures and then builds the project without first changing directories?
Answer: cmake -S demo -B demo/build followed by cmake --build demo/build
When working with CMake, you need to understand two distinct phases: configuration (where CMake reads your CMakeLists.txt and generates build files) and building (where the actual compilation happens). The flags -S and -B control configuration, while --build triggers the build phase.
The -S flag specifies the source directory — where your CMakeLists.txt lives. The -B flag specifies the build directory — where generated files should go. So to configure a project whose source is in demo/ and whose build files should land in demo/build/, you write cmake -S demo -B demo/build. Then, to compile using those generated files, you point --build at that same build directory: cmake --build demo/build. That's exactly what A does, making it correct.
B swaps the -S and -B arguments entirely — it tells CMake the source is in demo/build (which doesn't exist yet and has no CMakeLists.txt) and the build output goes into demo. This is logically reversed and will fail.
C mixes the phases nonsensically by passing -S to cmake --build, which doesn't accept source flags, and then tries to run a lone -B configuration command. Neither command is valid as written.
D is a subtle trap: the configuration step is correct (cmake -S demo -B demo/build), but the build step says cmake --build demo instead of cmake --build demo/build. This points to the source directory rather than the build directory, so CMake won't find the generated build system.
A handy memory rule: **--build always takes the build directory**, the same path you gave to -B during configuration.
- Which response is the safest way to keep the existing Makefiles build while also creating a Ninja build?
Answer: Configure Ninja in a different directory, such as cmake -S . -B build-ninja -G Ninja.
When working with CMake, each build directory stores its configuration — including the chosen generator — inside CMakeCache.txt. Once a generator is written to a cache, CMake treats it as locked. You cannot switch generators in the same directory without a full clean reconfigure, and even then, mixing generator artifacts risks corrupted or inconsistent builds. The safest mental model: one build directory = one generator.
Configuring Ninja into a separate directory with cmake -S . -B build-ninja -G Ninja (answer D) is the correct approach. Your original build/ directory remains untouched with its Unix Makefiles setup, while build-ninja/ gets a clean cache initialized for Ninja. Both coexist independently, sourcing the same CMakeLists.txt, with zero risk of interference.
Answer A is tempting but dangerous — deleting only CMakeCache.txt leaves behind generator-specific files (e.g., Makefiles) that are incompatible with Ninja. CMake may error out or produce a broken build tree when it encounters those stale artifacts.
Answer B reflects a fundamental misunderstanding: cmake --build invokes the already-configured generator to compile; it does not reconfigure or replace the stored generator. The -G flag is meaningless at the build step.
Answer C similarly misapplies the CMAKE_GENERATOR variable. Setting it as an environment variable or cache variable does not override an already-locked cache entry — you'd still face the same mismatch error without a clean directory.
Study tip: Whenever a CMake question involves switching generators or isolating configurations, your first instinct should be "use a separate build directory." CMake is designed to support multiple build trees from one source tree — lean into that design.
- Which addition most accurately models the header directory as a usage requirement of
mathlib? Answer: target_include_directories(mathlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
When working with CMake targets, always ask two questions: who needs this? and should that need be inherited? The target_include_directories command accepts visibility keywords — PRIVATE, PUBLIC, and INTERFACE — that control exactly this.
Here, math/add.hpp is consumed by two translation units: add.cpp (inside mathlib itself) and main.cpp (inside calculator). The include path include/ is therefore a requirement both for building mathlib and for any target that links against it. That's precisely what PUBLIC expresses — it populates both mathlib's own include paths and its usage requirements, so CMake automatically propagates the directory to calculator when you call target_link_libraries(calculator mathlib). Option A is correct for this reason.
Option B uses PRIVATE, which tells CMake the include path is needed only to compile mathlib itself. That breaks main.cpp, because calculator would never receive the path and #include <math/add.hpp> would fail to resolve — a classic under-propagation bug. Option C manually adds the include path to calculator directly. This "works" in isolation but defeats the purpose of usage requirements entirely: you've scattered dependency knowledge into the consumer instead of encoding it in the library. If you add a third target later, you'd have to repeat this manually. Option D uses link_directories, which affects linker search paths for .so/.lib files, not header lookup — it's completely the wrong tool for include directories.
As a study tip, memorize the three visibility keywords as a mental triangle: PRIVATE = me only, INTERFACE = consumers only, PUBLIC = both. Most library headers need PUBLIC precisely because consumers #include them too.