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

Inject CMake build_modules after targets are created #8130

Merged
merged 19 commits into from
Jan 14, 2021

Conversation

danimtb
Copy link
Member

@danimtb danimtb commented Nov 30, 2020

Changelog: Fix: Inject build modules after CMake targets are created
Docs: conan-io/docs#1944

This allows uisng build modules to generate non-namespaced targets. --

Note: By default this PR will skip the slower tests and will use a limited set of python versions. Check here how to increase the testing level by writing some tags in the current PR body text.

@danimtb danimtb added this to the 1.32 milestone Nov 30, 2020
@danimtb danimtb self-assigned this Nov 30, 2020
@danimtb danimtb changed the title [POC] Use build modules to generate non-namespaced targets Inject CMake build_modules after targets are created Dec 1, 2020
@czoido czoido modified the milestones: 1.32, 1.33 Dec 2, 2020
@danimtb danimtb requested a review from jgsogo January 12, 2021 11:51
Copy link
Member

@memsharded memsharded left a comment

Choose a reason for hiding this comment

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

Looking good, I think it is ready, somethings could be improved now, but I wouldn't oppose if they are fixed later.

Copy link
Contributor

@jgsogo jgsogo left a comment

Choose a reason for hiding this comment

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

This PR is a bit challenging. I'm assuming these expectations:

  • build modules are included after the element they refer to:
    • global build-modules at the very end
    • build-modules per component, after corresponding component is defined (nice to have, but they could go at the bottom too).
  • per-component build-modules are not included as global ones as well.
  • build-modules for a given configuration are added only when that configuration is used (requires some generator expression).

Please, tell me if I'm wrong with any of the previous points.

class TestCMakeFindPackageGenerator:

@pytest.mark.parametrize("use_components", [False, True])
def test_build_modules_alias_target(self, use_components):
Copy link
Contributor

Choose a reason for hiding this comment

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

In this test, when use_components =True, I can see the following:

Findhello.cmake

...
set(namespace_BUILD_MODULES_PATHS "/private/var/folders/fc/6mvcrc952dqcjfhl4c7c11ph0000gn/T/tmphcq2mp5yconans/path with spaces/data/hello/1.0/_/_/package/f83037eff23ab3a94190d7f3f7b37a2d6d522241/share/cmake/target-alias.cmake")
...
set(namespace_comp_BUILD_MODULES_PATHS "/private/var/folders/fc/6mvcrc952dqcjfhl4c7c11ph0000gn/T/tmphcq2mp5yconans/path with spaces/data/hello/1.0/_/_/package/f83037eff23ab3a94190d7f3f7b37a2d6d522241/share/cmake/target-alias.cmake")
...
...
foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS})
    include(${_BUILD_MODULE_PATH})
endforeach()

I would expect the build-module to appear only at the component level... and, of course, a loop calling include for each one of them.

Copy link
Contributor

Choose a reason for hiding this comment

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

Still the same, the same build-module appear at component and global level.

include(${_BUILD_MODULE_PATH})
endforeach()
{%- endfor %}
""")
Copy link
Contributor

Choose a reason for hiding this comment

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

Here we are adding unconditionally all the build-modules. The resulting cmake file contain these lines:

# Build modules
foreach(_BUILD_MODULE_PATH ${hello_BUILD_MODULES_PATHS_DEBUG})
    include(${_BUILD_MODULE_PATH})
endforeach()
foreach(_BUILD_MODULE_PATH ${hello_BUILD_MODULES_PATHS_MINSIZEREL})
    include(${_BUILD_MODULE_PATH})
endforeach()
foreach(_BUILD_MODULE_PATH ${hello_BUILD_MODULES_PATHS_RELWITHDEBINFO})
    include(${_BUILD_MODULE_PATH})
endforeach()
foreach(_BUILD_MODULE_PATH ${hello_BUILD_MODULES_PATHS_RELEASE})
    include(${_BUILD_MODULE_PATH})
endforeach()

All of them are included regardless of the build_type used. We need a generator expression to expand and use only the build-modules for the matching build-type.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have tried modifying this to use generator expressions here, but those only work in target properties as generator expressions are specific for CMake generators. I have also tried using CMAKE_BUILD_TYPE to make the conditionals but it is not available. I am leaving this as it is right now

Copy link
Contributor

Choose a reason for hiding this comment

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

That's right. Generator expressions only work with target-related properties... so for multi-generators this is unsolvable. 🤔 . Maybe something to document.

Then it is clearly a CMake limitation and I'd extend it to non-multi generators as well (for consistency). It means that the list of build-modules to include cannot be different based on the configuration name (typically build_type), recipe authors need to implement the build-module with this circumstance in mind. We'd go crazy trying to protect the user from it given our cpp_info object.

include(${_BUILD_MODULE_PATH})
endforeach()

{%- endfor %}
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above, here we add all the build-modules unconditionally, we need a generator expression to include only the ones for the matching config.

This is current output:

########## BUILD MODULES ####################################################################
#############################################################################################

########## GLOBAL BUILD MODULES #############################################################

foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS_DEBUG})
    include(${_BUILD_MODULE_PATH})
endforeach()

foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS_MINSIZEREL})
    include(${_BUILD_MODULE_PATH})
endforeach()

foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS_RELWITHDEBINFO})
    include(${_BUILD_MODULE_PATH})
endforeach()

foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS_RELEASE})
    include(${_BUILD_MODULE_PATH})
endforeach()

@@ -139,6 +139,105 @@ def build(self):
client.run("create .")
self.assertIn("Printing using a external module!", client.out)

@parameterized.expand([(False,), (True,)])
def test_build_modules_alias_target(self, use_components):
Copy link
Contributor

Choose a reason for hiding this comment

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

Same issue as the one with the cmake_find_package generator:

helloTarget-release.cmake

...
set(namespace_BUILD_MODULES_PATHS_RELEASE "/private/var/folders/fc/6mvcrc952dqcjfhl4c7c11ph0000gn/T/tmpf8fefhlrconans/path with spaces/data/hello/1.0/_/_/package/f83037eff23ab3a94190d7f3f7b37a2d6d522241/share/cmake/target-alias.cmake")
...
set(namespace_comp_BUILD_MODULES_PATHS_RELEASE "/private/var/folders/fc/6mvcrc952dqcjfhl4c7c11ph0000gn/T/tmpf8fefhlrconans/path with spaces/data/hello/1.0/_/_/package/f83037eff23ab3a94190d7f3f7b37a2d6d522241/share/cmake/target-alias.cmake")
...

I expect the build-module to appear listed only under the namespace_comp ones...

hello-config.cmake

...
########## BUILD MODULES ####################################################################
#############################################################################################

########## GLOBAL BUILD MODULES #############################################################

foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS_DEBUG})
    include(${_BUILD_MODULE_PATH})
endforeach()

foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS_MINSIZEREL})
    include(${_BUILD_MODULE_PATH})
endforeach()

foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS_RELWITHDEBINFO})
    include(${_BUILD_MODULE_PATH})
endforeach()

foreach(_BUILD_MODULE_PATH ${namespace_BUILD_MODULES_PATHS_RELEASE})
    include(${_BUILD_MODULE_PATH})
endforeach()
...

Build-modules per component are not included.

Copy link
Contributor

@jgsogo jgsogo left a comment

Choose a reason for hiding this comment

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

Agree we cannot decide which build-modules to include based on the configuration for multi-config generator (the include function cannot use generator expressions). Agree we shouldn't add the feature to non-multi generator for consistency too.

But, I think we can include only build-modules at the level where they are declared.

I'll have a look to the implementation.

class TestCMakeFindPackageGenerator:

@pytest.mark.parametrize("use_components", [False, True])
def test_build_modules_alias_target(self, use_components):
Copy link
Contributor

Choose a reason for hiding this comment

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

Still the same, the same build-module appear at component and global level.

Copy link
Contributor

@jgsogo jgsogo left a comment

Choose a reason for hiding this comment

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

Wow, I'm no longer sure if it is worth fixing/adding that in this PR. After having a look at the sources I can see that we are populating global variables even if we are using components. This is intended and it is good according to the docs where we list the CMake variables that are available for the different generators.

And I see current implementations include only the proper build-modules: global ones or components, depending on the generator.

Good!

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