conan icon indicating copy to clipboard operation
conan copied to clipboard

[feature] `tools.cmake.cmaketoolchain:conan_preset_file`

Open mpusz opened this issue 1 year ago • 19 comments

What is your suggestion?

As described in https://docs.conan.io/2/examples/tools/cmake/cmake_toolchain/extend_own_cmake_presets.html Conan provides the means to specify a custom presets file name that can be later on included by the custom CMakeUserPresets.cmake. This, however, requires having and changing a conanfile.py to provide this functionality.

A current behavior has many limitations:

  • will not work with conanfile.txt
  • a need to change the external project's conanfile.py to get this behavior
  • a need to update several projects in the corporation when we want to achieve the same consistent behavior among projects
  • does not allow customizing per specific repository (the same project in different locations may have different use cases and often, the default behavior would work just fine)

Have you read the CONTRIBUTING guide?

  • [X] I've read the CONTRIBUTING guide

mpusz avatar Feb 10 '24 07:02 mpusz

Please also consider setting "hidden": true for such presets as, in such cases, they are clearly not meant as the final interface to the users.

mpusz avatar Feb 10 '24 07:02 mpusz

Hi @mpusz

This feature is designed this way because things are defined exclusively locally, it depends only on every specific project:

Conan provides the means to specify a custom presets file name that can be later on included by the custom CMakeUserPresets.cmake.

This basically means that the user CMakePresets.json or CMakeUserPresets.json contains an include to the Conan generated presets.

Changing the Conan generated preset filename externally or globally, like via a conf basically breaks projects, because they will have an include to a give filename, and that won't be changed by a Conan conf.

So if a project is using their own presets, the necessary flow includes:

  • Using a conanfile.py. Indeed this is necessary, a conanfile.txt is not enough. This is explained in https://docs.conan.io/2/tutorial/consuming_packages/the_flexibility_of_conanfile_py.html
  • Defining the generated filename in the conanfile.py
  • include that filename in the project presets

Basically, I don't see the customization point, everything in the flow is local and specific to the project, the existing project presets, the include and the Conan generated one are all local and need to be aligned locally. Consistency accross projects cannot be configured globally, it must be implemented locally, because it also depends on existing preset files that Conan do not manage at all.

memsharded avatar Feb 11 '24 22:02 memsharded

The problem with CMakeUserPresets.json is that those should not be project-related. Those should possibly be portable between projects with user-preferred environments. Those should not be versioned in the repo with the project. Each user of a project may want to do something else, but the conanfile.py is one for all the developers and customers of a package. This is why I claim users would benefit a lot from a customization point that does not involve any changes to the conanfile.py, which is a part of the project, so it should not be customized per user needs.

CMakePresets.json are project-related and pushed to the repos. But Conan never generates those files, so a project's preset file can't be broken with any change discussed here.

mpusz avatar Feb 11 '24 22:02 mpusz

The problem with CMakeUserPresets.json is that those should not be project-related.

I think I disagree here. They are very project related, it must live together as sibling with CMakeLists.txt, it cannot even be in another folder, it is in the project folder exclusively. And if it contains an include() to another file, the location and name of the file must be known and fixed. The fact that you can copy it from other projects or from a template doesn't make it a non-local file. It is a project file, that can be exclusively used by that project because the location restricts the usage of the file.

What sounds like a customization point is a breaking point for users that wouldn't have such a templating or automatic copy mechanism for CMakeUserPresets.json, and we will have users complaining why their presets no longer find the Conan generated one even if their conanfile.py specifies a name to generate (because the profile must always have precedence over recipes)

memsharded avatar Feb 11 '24 22:02 memsharded

https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html

CMakePresets.json is meant to specify project-wide build details, while CMakeUserPresets.json is meant for developers to specify their own local build details.

CMakePresets.json may be checked into a version control system, and CMakeUserPresets.json should NOT be checked in. For example, if a project is using Git, CMakePresets.json may be tracked, and CMakeUserPresets.json should be added to the .gitignore.

mpusz avatar Feb 11 '24 22:02 mpusz

we will have users complaining why their presets no longer find the Conan generated one even if their conanfile.py specifies a name to generate (because the profile must always have precedence over recipes)

I do not understand why you think that adding a customization point will break users? The defaults will remain the same and it is up to the user to change it or not.

mpusz avatar Feb 11 '24 22:02 mpusz

Currently when I want to have a customized preset but don't want to change the recipe I need to:

  • backup and remove my preferred CMakeUserPresets.txt file
  • regenerate all configs for all compilers, build types, C++ versions in Conan to get a new conanfile with includes (as I am too lazy to collect all those paths manually)
  • rename the generated file to ConanPresets.json
  • restore my previous file that includes this file

That is quite a lot of workarounds to make it work :-(

mpusz avatar Feb 11 '24 22:02 mpusz

I do not understand why you think that adding a customization point will break users? The defaults will remain the same and it is up to the user to change it or not.

Because I have been there too many times, it is a matter of expectations. Some user adds the customization point (conf configuration) to their profile, then later they move to other project, it breaks that other project and then you have some users filing [bug] tickets because Conan is breaking their previously working builds and requesting that their conanfile.py value should have precedence (which is not possible to do it either, because users expect profiles to have precedence as designed and documented).

So talking more abstractly, adding global configuration for things that are exclusively local, is a common source of misunderstandings and issues.

memsharded avatar Feb 11 '24 23:02 memsharded

Currently when I want to have a customized preset but don't want to change the recipe I need to:

I am afraid that I don't understand the issue. When you are working on a project you have a few files:

  • CMakeLists.txt
  • conanfile.py (it defines to generate for example ConanPresets.json)
  • CMakeUserPresets.json that includes ConanPresets.json

Then if you change something, then you just edit your CMakeUserPresets.json? I don't see what else should be changed.

regenerate all configs for all compilers, build types, C++ versions in Conan to get a new conanfile with includes (as I am too lazy to collect all those paths manually)

Sorry, I don't know what you mean. The conanfile doesn't need to change at all?

memsharded avatar Feb 11 '24 23:02 memsharded

So talking more abstractly, adding global configuration for things that are exclusively local, is a common source of misunderstandings and issues.

That is a pity because I would love to not set it in profiles but pass it as a command line argument to conan install when needed or set it globally in global.conf as a common practice for all my repos.

mpusz avatar Feb 12 '24 05:02 mpusz

When you are working on a project you have a few files:

  • CMakeLists.txt
  • conanfile.py (it defines to generate for example ConanPresets.json)
  • CMakeUserPresets.json that includes ConanPresets.json

I don't have ConanPresets.json because this is not a Conan's-default nor a project's-default to generate one.

Sorry, I don't know what you mean. The conanfile doesn't need to change at all?

Yes, it does. For example, if I want to add an additional build target to my CMakeBuild presets to ensure I always check it while I build the sources, I need a custom CMakeUserPresets.cmake with a copy of all Conan-generated build presets that will inheriting from yet another custom preset that adds such a target to build.

If I modify a Conan-generated CMakeUserPresets.cmake, Conan will not update it anymore with the next conan install commands. I have two options:

  • start from scratch, delete CMakeUserPresets.cmake and regenerate all the conan configs (and I use quite a lot at the same time)
  • temporarily change project's conanfile.py to modify the name of a generated presets file every time I want to do conan install, and revert the change to the project's defaults afterward.

Every user may have different needs and different expectations about the development environment they use. CMakeUserPresets.cmake is exactly for that, to set custom variables and build targets according to the, often temporary, needs.

Currently, doing such customizations is tedious in Conan, as described above. Having a config option to customize the name of the generated files and possibly also hide presets by category (e,g. "none", "config", "build", "test") would be great.

mpusz avatar Feb 12 '24 05:02 mpusz

That is a pity because I would love to not set it in profiles but pass it as a command line argument to conan install when needed or set it globally in global.conf as a common practice for all my repos.

You cannot enforce that, because you still need to locally change the include in the local CMakeUserPresets.json. That is the source of confusion mixing local changes and global configuration

I don't have ConanPresets.json because this is not a Conan's-default nor a project's-default to generate one.

But if you want to provide your own CMakeUserPresets.json, then you need to do it, as explained in https://docs.conan.io/2/examples/tools/cmake/cmake_toolchain/extend_own_cmake_presets.html, so your user presets have a mechanism to load and reuse Conan generated information.

The CMakeUserPresets.json must have an include to a name of a local file, and there is no other way than the conanfile.py to provide that name.

If I modify a Conan-generated CMakeUserPresets.cmake, Conan will not update it anymore with the next conan install commands. I have two options:

No, no, of course modifying a generated file is not desired, it shouldn't be done

temporarily change project's conanfile.py to modify the name of a generated presets file every time I want to do conan install, and revert the change to the project's defaults afterward.

But this is not temporary. If you want to use your CMakeUserPresets.json, then you define the tc.user_presets_path= and include it in your CMakeUserPresets.json. The flows could be:

  • I want in my current project to have my own CMakeUserPresets.json, that reuses Conan one
  • I define in my current project tc.user_presets_path="MyPresets.json"
  • I include MyPresets.json in my CMakeUserPresets.json

Everything is local, directly in my editor, in the same folder, no need to change any conf, command line or anything else. It always work and kept in sync just within the current folder scope

The other flow is:

  • I want in my current project to have my own CMakeUserPresets.json, that reuses Conan one
  • I have to edit in my profile, somewhere else (in the cache for example) tools.cmake.cmaketoolchain:user_presets_path="MyPresets.json" or have to pass this conf every single time in my command line
  • I include MyPresets.json in my CMakeUserPresets.json

This is not self contained, tomorrow I cd to this folder, call conan install with the same commands and then everything is broken because other project changed the tools.cmake.cmaketoolchain:user_presets_path conf elsewhere.

This is what I don't get. At the end of the day, it is all about where to define the custom name of the Conan generated presets file MyPresets.json. If it is local in conanfile tc.user_presets_path= it will always be easily in sync, edited together and in files in the same folder, everything self-contained, cannot be broken by other project, while having a conf that changes requires more user jumps, more typing and more easily breaks user space.

memsharded avatar Feb 13 '24 10:02 memsharded

I am not sure if this helps you but you can change the cmake preset name using the cmake layout:

     def layout(self):
            self.folders.build_folder_vars = [
                "settings.os", "settings.os.api_level",
                "settings.arch", "settings.compiler",
                "settings.compiler.version"
                ]

This allows you to easily switch between different profiles without the need to force the profile name as this can be deduced from the above line. In this case for windows build it will be something like conan-windows-x86_64-msvc-143-debug or ``conan-android-31-arm8-clang-17-release`.

You can pass this also from the command line with the -c option, i.e. -c tools.cmake.cmake_layout:build_folder_vars=['settings.arch'] keep in mind that debug and release will be automatically appended if not specified.

elvisdukaj avatar Feb 13 '24 12:02 elvisdukaj

Currently, doing such customizations is tedious in Conan, as described above. Having a config option to customize the name of the generated files and possibly also hide presets by category (e,g. "none", "config", "build", "test") would be great.

It would be good if you could provide us with an example CMakeUserPresets.json that does this inclusion so that the issue can be better illustrated - and also how this hiding of presets by category would work, and what are the advatanges of hidden.

I can see how some level of customization is useful - but I think I can see @memsharded's points of why the conf mechanism may not be the most adequate for this, given how confs work. They can be set in 3 pleaces: the CLI, the package_info() of a method of a recipe, or a profile.

The "expected" filename for the generated "presets" (generated by CMakeDeps) is tightly coupled with the file that wishes to "include" - so if you want to provide your own CMakeUserPresets.json which includes the ones generated by Conan - you want to minimise the risk of a conf elsewhere changing the name that you've already expressed in CMakeUserPresets.json. A recipe in my dependency graph should not be able to change the name that i'm using to locally build my project!

I have a couple of questions here, circling back to wanting to see an example. The CMakeDeps generator does not generate a CMakeUserPresets.json if one already exist and it was not previously generated by Conan. However, it still generates a CMakePresets.json in the generators subfolder - is this not enough to have a custom , user-provided CMakeUserPresets.json? I appreciate that the subfolder may be different depending on the project, the platform and the layout used.

Conan provides the means to specify a custom presets file name that can be later on included by the custom CMakeUserPresets.cmake

Given that Conan won't generate this file if CMakeUserPresets.cmake already exists and was not previously generated by Conan - what I can think of is changing this behaviour such that if the user is the one providing this file, Conan generates one with a different, predictable filename. It would allow a conanfile.txt user to have their own CMakeUserPresets.cmake, the file (with a given name) would still be generated, and we supress the risk of a conf changing the behaviour across unrelated projects.

jcar87 avatar Feb 13 '24 14:02 jcar87

It would be good if you could provide us with an example CMakeUserPresets.json that does this inclusion so that the issue can be better illustrated

Imagine that I want a build presets that always build all and all_verify_interface_header_sets targets and that I want all the configurations to be provided by Conan. Also, let's assume that I can't modify the conanfile.py for some reason. For example:

  • I am not a member of the Open Source project that I use, and they rejected my PR 😜,
  • every developer in my team has a different favorite list of targets to run,
  • etc.

I also use:

tools.cmake.cmake_layout:build_folder_vars=["settings.compiler", "settings.compiler.version", "settings.compiler.cppstd"]

and have plenty of configurations to test.

How would you approach that? This is one of the possibilities for just two compilers:

{
    "version": 4,
    "include": [
        "/workspace/workshop-tools/echo/build/clang-17-20/generators/CMakePresets.json",
        "/workspace/workshop-tools/echo/build/gcc-13-20/generators/CMakePresets.json"
    ],
    "buildPresets": [
        {
            "name": "build-all",
            "hidden": true,
            "targets": [
                "all",
                "all_verify_interface_header_sets"
            ]
        },
        {
            "name": "gcc-13-20-debug",
            "inherits": [
                "build-all",
                "conan-gcc-13-20-debug"
            ]
        },
        {
            "name": "gcc-13-20-release",
            "inherits": [
                "build-all",
                "conan-gcc-13-20-release"
            ]
        },
        {
            "name": "clang-17-20-debug",
            "inherits": [
                "build-all",
                "conan-clang-17-20-debug"
            ]
        },
        {
            "name": "clang-17-20-release",
            "inherits": [
                "build-all",
                "conan-clang-17-20-release"
            ]
        }
    ]
}

It would be nice to have something like this instead:

{
    "version": 4,
    "include": "ConanPresets.json",
    "buildPresets": [ ... ]
}
  • and also how this hiding of presets by category would work, and what are the advatanges of hidden.

I provided "clang-17-20-release" preset as the one I wanted to use for all my builds. But in the dropdown boxes in the IDE, I also see all other inherited Conan targets. This may lead to accidentally picking and building a different unwanted configuration. If I was able to say that all build presets should be hidden because I plan to overwrite, it would prevent such issues and would keep the IDE cleaner.

Of course, one may argue that I should just manually manage the "include" list with the paths generated by Conan, and I can live with such an answer 😉 as I have to iterate all the Conan-generated build manually presets anyway. But it does not address "hiding" of the presets.

mpusz avatar Feb 13 '24 16:02 mpusz

In general, CMake presets suck when you want to compose something fancier from several presets 😢 There really should be some command that would allow you to combine lists of presets in a more efficient way. But that is not the Conan's issue 😉

mpusz avatar Feb 13 '24 16:02 mpusz

Anyway, this feature is not that important to spend hours discussing it. I thought it might be a simple and nice addition, but if you find it controversial, then you can close this issue.

mpusz avatar Feb 13 '24 17:02 mpusz

Maybe another way around? Maybe there should be a config option similar to tools.cmake.cmaketoolchain:user_toolchain: but for presets? However, this one should be more complicated. It should contain:

  • user presets path
  • optional configure preset name to inherit from
  • optional build preset name to inherit from
  • optional test preset name to inherit from

This would highly simplify all customizations mentioned above. I could provide a file like:

{
    "version": 4,
    "buildPresets": [
        {
            "name": "build-all",
            "hidden": true,
            "targets": [
                "all",
                "all_verify_interface_header_sets"
            ]
        }
    ]
}

and provide -c tools.cmake.cmaketoolchain:user_presets="CustomPresets.json,build: build-all" and get the file that would be equivalent to the one a few comments above.

mpusz avatar Feb 14 '24 09:02 mpusz

Please note that the latest VSCode seems to support workflow presets as well. It would be great if Conan could automatically create a preset like the below:

{
    "version": 6,
    "vendor": {
        "conan": {}
    },
    "cmakeMinimumRequired": {
        "major": 3,
        "minor": 25,
        "patch": 0
    },
    "configurePresets": [
        {
            "name": "conan-clang-18-23",
            "displayName": "'conan-clang-18-23' config",
            "description": "'conan-clang-18-23' configure using 'Ninja Multi-Config' generator",
            "generator": "Ninja Multi-Config",
            "cacheVariables": {
                "CMAKE_POLICY_DEFAULT_CMP0091": "NEW"
            },
            "environment": {
                "PATH": "/home/mpusz/.conan2/p/cmakecf6b18ccaa9f5/p/bin:$penv{PATH}"
            },
            "toolchainFile": "/home/mpusz/repos/units_latest/build/clang-18-23/generators/conan_toolchain.cmake",
            "binaryDir": "/home/mpusz/repos/units_latest/build/clang-18-23"
        }
    ],
    "buildPresets": [
        {
            "name": "conan-clang-18-23-release",
            "configurePreset": "conan-clang-18-23",
            "configuration": "Release"
        },
        {
            "name": "conan-clang-18-23-debug",
            "configurePreset": "conan-clang-18-23",
            "configuration": "Debug"
        }
    ],
    "testPresets": [
        {
            "name": "conan-clang-18-23-release",
            "configurePreset": "conan-clang-18-23",
            "configuration": "Release"
        },
        {
            "name": "conan-clang-18-23-debug",
            "configurePreset": "conan-clang-18-23",
            "configuration": "Debug"
        }
    ],
    "workflowPresets": [
        {
            "name": "conan-clang-18-23",
            "steps": [
                {
                    "type": "configure",
                    "name": "conan-clang-18-23"
                },
                {
                    "type": "build",
                    "name": "conan-clang-18-23-release"
                },
                {
                    "type": "build",
                    "name": "conan-clang-18-23-debug"
                },
                {
                    "type": "test",
                    "name": "conan-clang-18-23-release"
                },
                {
                    "type": "test",
                    "name": "conan-clang-18-23-debug"
                }
            ]
        }
    ]
}

Of course, the possibility of amending the configure, build, and test presets with some custom inherits would be awesome.

Note 1: workflowPresets can't be inherited.

Note 2: Chema version 6 and minimum CMake 3.25 are required to benefit from the workflow presets.

mpusz avatar Feb 16 '24 08:02 mpusz