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

CMakeDeps checks build profile + CMakeDeps check real targets #9206

Merged
merged 6 commits into from
Jul 7, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions conan/tools/_check_build_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

check_msg = "Using the new toolchains and generators without specifying " \
"a build profile (e.g: -pr:b=default) is discouraged and "\
"might cause failures and unexpected behavior"


def check_using_build_profile(conanfile):
"""FIXME: Remove this in Conan 2.0 where the two profiles are always applied"""
if not hasattr(conanfile, "settings_build"):
conanfile.output.warn(check_msg)
11 changes: 11 additions & 0 deletions conan/tools/cmake/cmakedeps/cmakedeps.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os

from conan.tools._check_build_profile import check_using_build_profile
from conan.tools.cmake.cmakedeps.templates.config import ConfigTemplate
from conan.tools.cmake.cmakedeps.templates.config_version import ConfigVersionTemplate
from conan.tools.cmake.cmakedeps.templates.macros import MacrosTemplate
Expand All @@ -26,7 +27,17 @@ def __init__(self, conanfile):
# a suffix. It is necessary in case of same require and build_require and will cause an error
self.build_context_suffix = {}

check_using_build_profile(self._conanfile)

def generate(self):
# FIXME: Remove this in 2.0
if not hasattr(self._conanfile, "settings_build") and \
(self.build_context_activated or self.build_context_build_modules or
self.build_context_suffix):
raise ConanException("The 'build_context_activated' and 'build_context_build_modules' of"
" the CMakeDeps generator cannot be used without specifying a build"
" profile. e.g: -pr:b=default")

# Current directory is the generators_folder
generator_files = self.content
for generator_file, content in generator_files.items():
Expand Down
17 changes: 15 additions & 2 deletions conan/tools/cmake/cmakedeps/templates/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ def filename(self):
def context(self):
return {"file_name": self.file_name,
"pkg_name": self.pkg_name,
"config_suffix": self.config_suffix}
"config_suffix": self.config_suffix,
"target_namespace": self.target_namespace}

@property
def template(self):
Expand All @@ -45,10 +46,22 @@ def template(self):
endif()
endforeach()

# Only the first installed configuration is included to avoid the collission
# Only the first installed configuration is included to avoid the collision
foreach(_BUILD_MODULE {{ '${' + pkg_name + '_BUILD_MODULES_PATHS' + config_suffix + '}' }} )
conan_message(STATUS "Conan: Including build module from '${_BUILD_MODULE}'")
include({{ '${_BUILD_MODULE}' }})
endforeach()

# Check that the specified components in the find_package(Foo COMPONENTS x y z) are there
# This is the variable filled by CMake with the requested components in find_package
if({{ target_namespace }}_FIND_COMPONENTS)
foreach(_FIND_COMPONENT {{ '${'+target_namespace+'_FIND_COMPONENTS}' }})
if (TARGET {{ target_namespace }}::${_FIND_COMPONENT})
conan_message(STATUS "Conan: Component '${_FIND_COMPONENT}' found in package '{{ pkg_name }}'")
else()
conan_message(FATAL_ERROR "Conan: Component '${_FIND_COMPONENT}' NOT found in package '{{ pkg_name }}'")
endif()
endforeach()
endif()

""")
1 change: 0 additions & 1 deletion conan/tools/cmake/cmakedeps/templates/target_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,3 @@ def join_paths_single_var(values):

build_modules = cpp_info.get_property("cmake_build_modules", "CMakeDeps") or []
self.build_modules_paths = join_paths(build_modules)

12 changes: 0 additions & 12 deletions conan/tools/cmake/cmakedeps/templates/targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,4 @@ def template(self):
foreach(f ${CONFIG_FILES})
include(${f})
endforeach()

# This is the variable filled by CMake with the requested components in find_package
if({{ target_namespace }}_FIND_COMPONENTS)
foreach(_FIND_COMPONENT {{ '${'+target_namespace+'_FIND_COMPONENTS}' }})
list(FIND {{ pkg_name }}_COMPONENT_NAMES "${_FIND_COMPONENT}" _index)
if(${_index} EQUAL -1)
conan_message(FATAL_ERROR "Conan: Component '${_FIND_COMPONENT}' NOT found in package '{{ pkg_name }}'")
else()
conan_message(STATUS "Conan: Component '${_FIND_COMPONENT}' found in package '{{ pkg_name }}'")
endif()
endforeach()
endif()
""")
3 changes: 3 additions & 0 deletions conan/tools/cmake/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from jinja2 import Template

from conan.tools import CONAN_TOOLCHAIN_ARGS_FILE
from conan.tools._check_build_profile import check_using_build_profile
from conan.tools._compilers import architecture_flag, use_win_mingw
from conan.tools.cmake.utils import is_multi_configuration, get_file_name
from conan.tools.microsoft.toolchain import write_conanvcvars, vs_ide_version
Expand Down Expand Up @@ -696,6 +697,8 @@ def __init__(self, conanfile, generator=None):
("rpath", SkipRPath),
("shared", SharedLibBock)])

check_using_build_profile(self._conanfile)

def _context(self):
""" Returns dict, the context for the template
"""
Expand Down
2 changes: 2 additions & 0 deletions conan/tools/gnu/autotoolsdeps.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from conan.tools._check_build_profile import check_using_build_profile
from conan.tools.env import Environment
from conan.tools.env.environment import save_script
from conan.tools.gnu.gnudeps_flags import GnuDepsFlags
Expand All @@ -10,6 +11,7 @@ def __init__(self, conanfile):
# alter some value
self._conanfile = conanfile
self._cpp_info = None
check_using_build_profile(self._conanfile)

@property
def cpp_info(self):
Expand Down
3 changes: 3 additions & 0 deletions conan/tools/gnu/autotoolstoolchain.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json

from conan.tools import CONAN_TOOLCHAIN_ARGS_FILE
from conan.tools._check_build_profile import check_using_build_profile
from conan.tools._compilers import architecture_flag, build_type_flags, cppstd_flag
from conan.tools.apple.apple import apple_min_version_flag, to_apple_arch, \
apple_sdk_path
Expand Down Expand Up @@ -63,6 +64,8 @@ def __init__(self, conanfile):
# -isysroot makes all includes for your library relative to the build directory
self.apple_isysroot_flag = "-isysroot {}".format(sdk_path) if sdk_path else None

check_using_build_profile(self._conanfile)

def _cxx11_abi_define(self):
# https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
# The default is libstdc++11, only specify the contrary '_GLIBCXX_USE_CXX11_ABI=0'
Expand Down
2 changes: 2 additions & 0 deletions conan/tools/google/bazeldeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

from jinja2 import Template

from conan.tools._check_build_profile import check_using_build_profile
from conans.util.files import save


class BazelDeps(object):
def __init__(self, conanfile):
self._conanfile = conanfile
check_using_build_profile(self._conanfile)

def generate(self):
local_repositories = []
Expand Down
2 changes: 2 additions & 0 deletions conan/tools/google/toolchain.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import json

from conan.tools import CONAN_TOOLCHAIN_ARGS_FILE
from conan.tools._check_build_profile import check_using_build_profile
from conans.util.files import save


class BazelToolchain(object):

def __init__(self, conanfile):
self._conanfile = conanfile
check_using_build_profile(self._conanfile)

def generate(self):
bazel_config = self._conanfile.conf["tools.google.bazel:config"]
Expand Down
3 changes: 3 additions & 0 deletions conan/tools/meson/toolchain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os

from conan.tools._check_build_profile import check_using_build_profile
from conan.tools.env import VirtualBuildEnv
from conan.tools.microsoft.toolchain import write_conanvcvars
from conans.client.build.cppstd_flags import cppstd_from_settings
Expand Down Expand Up @@ -113,6 +114,8 @@ def from_build_env(name):
self.cpp_link_args = self._to_meson_value(self._env_array('LDFLAGS'))
self.pkg_config_path = "'%s'" % self._conanfile.generators_folder

check_using_build_profile(self._conanfile)

@staticmethod
def _to_meson_value(value):
# https://mesonbuild.com/Machine-files.html#data-types
Expand Down
3 changes: 3 additions & 0 deletions conan/tools/microsoft/msbuilddeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from jinja2 import Template

from conan.tools._check_build_profile import check_using_build_profile
from conans.errors import ConanException
from conans.model.build_info import DepCppInfo
from conans.util.files import load, save
Expand Down Expand Up @@ -109,6 +110,8 @@ def __init__(self, conanfile):
raise ConanException("tools.microsoft.msbuilddeps:exclude_code_analysis must be a"
" list of package names patterns like ['pkga*']")

check_using_build_profile(self._conanfile)

def generate(self):
# TODO: Apply config from command line, something like
# configuration = self.conanfile.config.generators["msbuild"].configuration
Expand Down
5 changes: 3 additions & 2 deletions conan/tools/microsoft/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import textwrap
from xml.dom import minidom

from conan.tools.env.environment import save_script, register_environment_script
from conan.tools._check_build_profile import check_using_build_profile
from conan.tools.env.environment import register_environment_script
from conan.tools.microsoft.visual import vcvars_command, vcvars_arch
from conans.client.tools import intel_compilervars_command
from conans.errors import ConanException
from conans.util.files import save, load


CONAN_VCVARS_FILE = "conanvcvars.bat"


Expand Down Expand Up @@ -70,6 +70,7 @@ def __init__(self, conanfile):
self.runtime_library = self._runtime_library(conanfile.settings)
self.cppstd = conanfile.settings.get_safe("compiler.cppstd")
self.toolset = self._msvs_toolset(conanfile.settings)
check_using_build_profile(self._conanfile)

def _name_condition(self, settings):
props = [("Configuration", self.configuration),
Expand Down
1 change: 1 addition & 0 deletions conans/test/assets/genconanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ def with_cmake_build(self):
self._generators = self._generators or []
self._generators.append("CMakeDeps")
self._generators.append("CMakeToolchain")
self.with_settings("os", "compiler", "arch", "build_type")
self._cmake_build = True
return self

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import pytest

from conans.model.ref import ConanFileReference, PackageReference
from conans.test.assets.cmake import gen_cmakelists
from conans.test.assets.genconanfile import GenConanfile
from conans.test.assets.sources import gen_function_cpp
from conans.test.utils.tools import TestClient, NO_SETTINGS_PACKAGE_ID


Expand Down Expand Up @@ -266,3 +269,60 @@ def test_multi_generator_macos(self):
assert str(t.out).count('>> Build-module is included') == 2 # FIXME: Known bug
assert '>> nonamespace libs: library::library' in t.out
t.run_command('cmake --build . --config Release') # Compiles and links.


def test_targets_declared_in_build_modules():
"""If a require is declaring the component targets in a build_module, CMakeDeps is
fine with it, not needed to locate it as a conan declared component"""

client = TestClient()
conanfile_hello = str(GenConanfile().with_name("hello").with_version("1.0")
.with_exports_sources("*.cmake", "*.h"))
conanfile_hello += """
def package(self):
self.copy("*.h", dst="include")
self.copy("*.cmake", dst="cmake")

def package_info(self):
self.cpp_info.set_property("cmake_build_modules", ["cmake/my_modules.cmake"])
"""
my_modules = textwrap.dedent("""
add_library(cool_component INTERFACE)
target_include_directories(cool_component INTERFACE ${CMAKE_CURRENT_LIST_DIR}/../include/)
add_library(hello::invented ALIAS cool_component)
""")
hello_h = "int cool_header_only=1;"
client.save({"conanfile.py": conanfile_hello,
"my_modules.cmake": my_modules, "hello.h": hello_h})
client.run("create .")

conanfile = GenConanfile().with_cmake_build().with_requires("hello/1.0")\
.with_exports_sources("*.txt", "*.cpp").with_name("app").with_version("1.0")
main_cpp = textwrap.dedent("""
#include <iostream>
#include "hello.h"

int main(){
std::cout << "cool header value: " << cool_header_only;
return 0;
}
""")

cmakelist = textwrap.dedent("""
set(CMAKE_CXX_COMPILER_WORKS 1)
set(CMAKE_CXX_ABI_COMPILED 1)
set(CMAKE_C_COMPILER_WORKS 1)
set(CMAKE_C_ABI_COMPILED 1)
cmake_minimum_required(VERSION 3.15)
project(project CXX)

find_package(hello COMPONENTS invented)
add_executable(myapp main.cpp)
target_link_libraries(myapp hello::invented)
""")
client.save({"conanfile.py": conanfile,
"CMakeLists.txt": cmakelist, "main.cpp": main_cpp})
client.run("create .")
assert "Conan: Including build module" in client.out
assert "my_modules.cmake" in client.out
assert "Conan: Component 'invented' found in package 'hello'" in client.out
64 changes: 64 additions & 0 deletions conans/test/integration/toolchains/test_check_build_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import pytest

from conan.tools._check_build_profile import check_msg
from conans.test.assets.genconanfile import GenConanfile
from conans.test.utils.tools import TestClient


@pytest.mark.parametrize("tool_class", ["CMakeDeps", "CMakeToolchain", "AutotoolsToolchain",
"AutotoolsDeps", "MSBuildToolchain", "MSBuildDeps",
"BazelDeps", "BazelToolchain", "MesonToolchain"])
def test_warning_message(tool_class):
"""FIXME: Remove in Conan 2.0"""
client = TestClient()
r = str(GenConanfile().with_name("lib").with_version("1.0")
.with_settings("os", "arch", "build_type", "compiler")
.with_import("from conan.tools.cmake import CMakeDeps, CMakeToolchain")
.with_import("from conan.tools.gnu import AutotoolsDeps, AutotoolsToolchain")
.with_import("from conan.tools.microsoft import MSBuildDeps, MSBuildToolchain")
.with_import("from conan.tools.google import BazelDeps, BazelToolchain")
.with_import("from conan.tools.meson import MesonToolchain"))

r += """
def generate(self):
{}(self)
""".format(tool_class)

client.save({"conanfile.py": r})

client.run("create . ")
assert check_msg in client.out

client.run("create . -pr:b=default")
assert check_msg not in client.out


@pytest.mark.parametrize("cmake_deps_property", ["build_context_activated",
"build_context_build_modules",
"build_context_suffix",
False])
def test_error_cmake_deps_without_build_profile(cmake_deps_property):
client = TestClient()
r = str(GenConanfile().with_name("lib").with_version("1.0")
.with_settings("os", "arch", "build_type", "compiler")
.with_import("from conan.tools.cmake import CMakeDeps"))

r += """
def generate(self):
deps = CMakeDeps(self)
{}
deps.generate()
""".format("deps.{} = ['foo']".format(cmake_deps_property) if cmake_deps_property else "")

client.save({"conanfile.py": r})

client.run("create . ", assert_error=cmake_deps_property)
if cmake_deps_property:
assert "The 'build_context_activated' and 'build_context_build_modules' of the CMakeDeps " \
"generator cannot be used without specifying a build profile. e.g: -pr:b=default"\
in client.out

client.run("create . -pr:b=default")
assert "The 'build_context_activated' and 'build_context_build_modules' of the CMakeDeps " \
"generator cannot be used without specifying a build profile. e.g: -pr:b=default" \
not in client.out