Skip to content

Better handling of build preset targets #295

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

arntanguy
Copy link

In the current implementation, the targets field of CMakePresets.json is ignored. This leads to inconsistencies between what cmake-tools.nvim does and displays and what the user expects to happen.

Here is an example buildPresets showcasing the 4 possible cases:

  1. One target specified as a string
  2. One target specified as an array with a single string
  3. Several targets specified
  4. No target specified
  "buildPresets": [
    {
      "name": "build-one-string",
      "configurePreset": "release",
      "configuration": "Release",
      "targets": "target1"
    },
    {
      "name": "build-one-array",
      "configurePreset": "release",
      "configuration": "Release",
      "targets": ["target1"]
    },
    {
      "name": "build-two",
      "configurePreset": "release",
      "configuration": "Release",
      "targets": ["target1", "target2"]
    }
    {
      "name": "build-notarget",
      "configurePreset": "release",
      "configuration": "Release",
    }
  ]

One expects that :

  • when calling :CMakeSelectBuildPreset the internal build_target to be updated. This ensures that calling get_build_target() returns the target specified in the preset (for lualine for instance).
  • when calling :CMakeBuild the command executed to be cmake --preset <preset_name> without additional --target arguments

The current behaviour priour to this PR is to:

  • Disregard the targets field of the CMakePresets.json
  • Run cmake --preset <preset_name> --target <whatever the previously specified target was>

For a more concrete use-case, consider embeded development with a build-preset preset with a build-target target and and a flash-preset preset with a flash-target target that flashes the program onto the device.

  "buildPresets": [
    {
      "name": "build-preset",
      "configurePreset": "release",
      "configuration": "Release",
      "targets": "build-target"
    },
    {
      "name": "flash-preset",
      "configurePreset": "release",
      "configuration": "Release",
      "targets": ["flash-target"]
    },

Currently, the following would happen:

  • :CMakeSelectBuildPreset build-preset
    • the build-preset is indeed selected
    • build_target: none => we expected build-target instead
  • :CMakeBuild will prompt for a target, we select build-target, and cmake --preset build-preset --target build-target is run
  • :CMakeSelectBuildPreset flash-preset
    • the flash-preset is indeed selected
    • build_target: build-target => we expected flash-target
    • :CMakeBuild runs cmake --preset flash-preset --target build-target => we expected to run the flash-target

You get the gist. This PR fixes this behaviour in the following way:

  • When a new build preset is selected, the build_target is changed to what's specified in that preset with one caveat: multiple targets targets: ["target1", "target2"]. Since this case is not handled anywhere else in the code, I simply put the build_target = nil
  • The user only gets asked for which target to select if none where defined in the selected preset
  • The build command is always cmake --preset <selected_preset> if at least one target is specified in the preset, otherwise it is cmake --preset <selected_preset> --target <user selected target>

It would probably be better to have a proper way of handle the multiple-targets case. However this seemed too big of a change, for little benefit for me to tackle right now.

if opt.target == nil and config.build_target == nil then
local presets = Presets:parse(config.cwd)
local build_preset = presets:get_build_preset(config.build_preset)
if (not config.build_preset or config.build_preset and not build_preset.targets) and opt.target == nil and config.build_target == nil then
Copy link
Collaborator

Choose a reason for hiding this comment

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

build_preset could be nil. You will run into an index into nill error on build_preset.targets in this case. Maybe check for if (not build_preset or not build_preset.targets) ... ?

Comment on lines +358 to +368
if not build_preset or build_preset and not build_preset.targets then
if opt.target ~= nil then
vim.list_extend(args, { "--target", opt.target })
vim.list_extend(args, fargs)
elseif config.build_target == "all" then
vim.list_extend(args, { "--target", "all" })
vim.list_extend(args, fargs)
else
vim.list_extend(args, { "--target", config.build_target })
vim.list_extend(args, fargs)
end
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you want the build_preset.targets to take precedence, you can check for if build_preset and build_presets.targets first and have elseif opt.target second, right?

end

if config.build_type ~= nil then
vim.list_extend(args, { "--config", config.build_type })
if not build_preset then
Copy link
Collaborator

Choose a reason for hiding this comment

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

why this additional if?

end
end
else -- build preset does not have a target, ask
config.build_target = nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

if the build_preset does not have a target, the currently selected one should be kept.

else
-- XXX: Array targets are not supported in other code paths
-- As a workaround, unset the current build_target if any (it is not used when using build presets)
config.build_target = nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

as we do not have a handling for multiple build targets currently, you could either keep the currently selected one (if it is part of the array) or always select the first one from the list. The user can still change the target, if needed.
Resetting the selected target is not desirable, I think

I think a refactoring to support multiple targets might be helpful. But this is beyond the scope of this MR for sure.

@lceWolf
Copy link
Collaborator

lceWolf commented Feb 22, 2025

I appreciate the PR and think it is a step in the right direction for sure. Very nice write-up of the motivation and your approach!
Thanks for taking care of that topic.

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.

2 participants