Skip to content
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

[question] Better way to compose configurations #17608

Open
1 task
vasama opened this issue Jan 22, 2025 · 3 comments
Open
1 task

[question] Better way to compose configurations #17608

vasama opened this issue Jan 22, 2025 · 3 comments
Assignees

Comments

@vasama
Copy link

vasama commented Jan 22, 2025

What is your question?

Inspired by this comment: conan-io/conan-center-index#26391 (comment)

Profiles sometimes need to specify configuration options for building packages, e.g:

[conf]
tools.build:compiler_executables={"c": "clang-cl", "cpp": "clang-cl"}

However sometimes specific packages must be built using a different configuration:

[settings]
openssl/*:compiler=msvc
openssl/*:compiler.version=194
openssl/*:compiler.runtime=dynamic
openssl/*:compiler.runtime_type=Release
openssl/*:compiler.cppstd=17

OpenSSL cannot be built using Clang-CL, but MSVC and Clang-CL are binary compatible, so this works out fine.

In order to actually change the compiler being used, the compiler_executables option must be made conditional:

[conf]
!openssl*:tools.build:compiler_executables={"c": "clang-cl", "cpp": "clang-cl"}

All of this is pretty unwieldy and seems to break down as soon as more than one dependency should use a different configuration. Is there a better way to compose configurations to achieve this?

Have you read the CONTRIBUTING guide?

  • I've read the CONTRIBUTING guide
@memsharded memsharded self-assigned this Jan 22, 2025
@memsharded
Copy link
Member

Hi @vasama

Thanks for your question.

Maybe instead of doing the negation, it would be easier to do the definition, so something like:

openssl*:tools.build:compiler_executables={"c": "cl", "cpp": "cl"}

could work nicely.

That means, that if defining a profile for several packages, it could be convenient to define a jinja template with a for loop that iterates over a list of packages to the define the specific settings, and then the [conf] section will contain exactly the same iteration for the compiler_executables?

Note that if you want to fully undefine the conf, I think it might be possible with the ! operation:

[conf]
{% for pkg in my_msvc_pkg_list %}
{{pkg}}/*:tools.build.compiler_executables=!
{% endfor %}

@vasama
Copy link
Author

vasama commented Jan 22, 2025

Maybe instead of doing the negation, it would be easier to do the definition

That happens to work for this particular case. Like in the referenced issue, there are other options that make this more difficult:

tools.build:cxxflags+=["/clang:-march=nehalem"]
tools.build:exelinkflags+=["clang_rt.builtins-x86_64.lib"]
tools.build:sharedlinkflags+=["clang_rt.builtins-x86_64.lib"]

The template might work, but it looks horribly complicated. I would hope for a more easily maintained solution. Something like being able to override the entire profile for a specific dependency might solve this rather nicely:

openssl*:profile=msvc

@memsharded
Copy link
Member

memsharded commented Jan 24, 2025

openssl*:profile=msvc

I know this is not as built-in as that, but the jinja templates allow implementing the same concept, see the following test:

  • A couple of macros allow defining the exact logic to apply to the desired argument
  • The logic can be instantiated in one line: {{set_windows_settings("mypkg")}}, so it can be reused for different packages
  • The macros can actually be in another file if desired

Furthermore the application of a full profile to a per-package pattern is not always that well defined. Profiles have some fields, like [replace_requires] that won't have a definition per package. The conf itself can be appended/prepended or replaced, and the semantics are not always clear. While a explicit approach with jinja syntax allow users to define exactly the behavior they want, and reuse the logic to not repeat it for different packages.

def test_profile_macro_per_package():
    client = TestClient()
    tpl1 = textwrap.dedent("""
        {% macro set_windows_settings(lib) %}
        {{lib}}/*:os=Windows
        {{lib}}/*:arch=x86
        {% endmacro %}
        {% macro set_windows_conf(lib) %}
        {{lib}}/*:user.conf:key = 2
        {% endmacro %}

        [settings]
        os = Linux
        {{set_windows_settings("mypkg")}}
        [conf]
        user.conf:key = 1
        {{set_windows_conf("mypkg")}}
        """)
    conanfile = textwrap.dedent("""
        from conan import ConanFile
        class Pkg(ConanFile):
            name = "mypkg"
            version = "0.1"
            settings = "os", "arch"
            def generate(self):
                value = self.conf.get("user.conf:key")
                self.output.info(f"os={self.settings.os}!!")
                self.output.info(f"arch={self.settings.arch}!!")
                self.output.info(f"user.conf:key={value}!!!!")
        """)
    client.save({"conanfile.py": conanfile,
                 "profile1": tpl1})
    client.run("install . -pr=profile1")
    assert "conanfile.py (mypkg/0.1): user.conf:key=2!!!!" in client.out
    assert "conanfile.py (mypkg/0.1): os=Windows!!" in client.out
    assert "conanfile.py (mypkg/0.1): arch=x86!!" in client.out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants