Repository: conan-io/conan
Pull request #11605 (2022-07-10 23:25:44)

test for multiple python_require reuse
Changelog: Omit
Docs: Omit

Close https://github.com/conan-io/conan/issues/11589


Patch:
diff --git a/conan/tools/apple/xcodedeps.py b/conan/tools/apple/xcodedeps.py
index e972423d2..a15c533d1 100644
--- a/conan/tools/apple/xcodedeps.py
+++ b/conan/tools/apple/xcodedeps.py
@@ -137,7 +137,7 @@ class XcodeDeps(object):
             'lib_dirs': " ".join('"{}"'.format(p) for p in _merged_vars("libdirs")),
             'libs': " ".join("-l{}".format(lib) for lib in _merged_vars("libs")),
             'system_libs': " ".join("-l{}".format(sys_lib) for sys_lib in _merged_vars("system_libs")),
-            'frameworksdirs': " ".join('"{}"'.format(p) for p in _merged_vars("frameworkdirs")),
+            'frameworkdirs': " ".join('"{}"'.format(p) for p in _merged_vars("frameworkdirs")),
             'frameworks': " ".join("-framework {}".format(framework) for framework in _merged_vars("frameworks")),
             'definitions': " ".join('"{}"'.format(p.replace('"', '\\"')) for p in _merged_vars("defines")),
             'c_compiler_flags': " ".join('"{}"'.format(p.replace('"', '\\"')) for p in _merged_vars("cflags")),
diff --git a/conan/tools/cmake/cmake.py b/conan/tools/cmake/cmake.py
index 0805f7092..d30e4274f 100644
--- a/conan/tools/cmake/cmake.py
+++ b/conan/tools/cmake/cmake.py
@@ -1,11 +1,9 @@
 import os
-import platform
 
 from conan.tools.build import build_jobs
 from conan.tools.cmake.presets import load_cmake_presets, get_configure_preset
 from conan.tools.cmake.utils import is_multi_configuration
 from conan.tools.files import chdir, mkdir
-from conan.tools.files.files import load_toolchain_args
 from conan.tools.microsoft.msbuild import msbuild_verbosity_cmd_line_arg
 from conans.client.tools.oss import args_to_string
 from conans.errors import ConanException
diff --git a/conan/tools/cmake/cmakedeps/templates/config.py b/conan/tools/cmake/cmakedeps/templates/config.py
index bb7cdd3b8..5892a4160 100644
--- a/conan/tools/cmake/cmakedeps/templates/config.py
+++ b/conan/tools/cmake/cmakedeps/templates/config.py
@@ -66,7 +66,7 @@ class ConfigTemplate(CMakeDepsFileTemplate):
 
         # 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}'")
+            message(STATUS "Conan: Including build module from '${_BUILD_MODULE}'")
             include({{ '${_BUILD_MODULE}' }})
         endforeach()
 
@@ -76,9 +76,9 @@ class ConfigTemplate(CMakeDepsFileTemplate):
         if({{ file_name }}_FIND_COMPONENTS)
             foreach(_FIND_COMPONENT {{ '${'+file_name+'_FIND_COMPONENTS}' }})
                 if (TARGET ${_FIND_COMPONENT})
-                    conan_message(STATUS "Conan: Component '${_FIND_COMPONENT}' found in package '{{ pkg_name }}'")
+                    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 }}'")
+                    message(FATAL_ERROR "Conan: Component '${_FIND_COMPONENT}' NOT found in package '{{ pkg_name }}'")
                 endif()
             endforeach()
         endif()
diff --git a/conan/tools/cmake/cmakedeps/templates/macros.py b/conan/tools/cmake/cmakedeps/templates/macros.py
index 8888535a0..9d3965c0e 100644
--- a/conan/tools/cmake/cmakedeps/templates/macros.py
+++ b/conan/tools/cmake/cmakedeps/templates/macros.py
@@ -26,12 +26,6 @@ class MacrosTemplate(CMakeDepsFileTemplate):
     @property
     def template(self):
         return textwrap.dedent("""
-        function(conan_message MESSAGE_TYPE MESSAGE_CONTENT)
-            if(NOT CONAN_CMAKE_SILENT_OUTPUT)
-                message(${MESSAGE_TYPE} "${MESSAGE_CONTENT}")
-            endif()
-        endfunction()
-
        macro(conan_find_apple_frameworks FRAMEWORKS_FOUND FRAMEWORKS FRAMEWORKS_DIRS)
            if(APPLE)
                foreach(_FRAMEWORK ${FRAMEWORKS})
@@ -39,9 +33,9 @@ class MacrosTemplate(CMakeDepsFileTemplate):
                    find_library(CONAN_FRAMEWORK_${_FRAMEWORK}_FOUND NAMES ${_FRAMEWORK} PATHS ${FRAMEWORKS_DIRS} CMAKE_FIND_ROOT_PATH_BOTH)
                    if(CONAN_FRAMEWORK_${_FRAMEWORK}_FOUND)
                        list(APPEND ${FRAMEWORKS_FOUND} ${CONAN_FRAMEWORK_${_FRAMEWORK}_FOUND})
-                       conan_message(DEBUG "Framework found! ${FRAMEWORKS_FOUND}")
+                       message(VERBOSE "Framework found! ${FRAMEWORKS_FOUND}")
                    else()
-                       conan_message(FATAL_ERROR "Framework library ${_FRAMEWORK} not found in paths: ${FRAMEWORKS_DIRS}")
+                       message(FATAL_ERROR "Framework library ${_FRAMEWORK} not found in paths: ${FRAMEWORKS_DIRS}")
                    endif()
                endforeach()
            endif()
@@ -56,7 +50,7 @@ class MacrosTemplate(CMakeDepsFileTemplate):
                find_library(CONAN_FOUND_LIBRARY NAMES ${_LIBRARY_NAME} PATHS ${package_libdir}
                             NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
                if(CONAN_FOUND_LIBRARY)
-                   conan_message(DEBUG "Library ${_LIBRARY_NAME} found ${CONAN_FOUND_LIBRARY}")
+                   message(VERBOSE "Conan: Library ${_LIBRARY_NAME} found ${CONAN_FOUND_LIBRARY}")
                    list(APPEND _out_libraries ${CONAN_FOUND_LIBRARY})
 
                    # Create a micro-target for each lib/a found
@@ -69,12 +63,12 @@ class MacrosTemplate(CMakeDepsFileTemplate):
                        set_target_properties(${_LIB_NAME} PROPERTIES IMPORTED_LOCATION ${CONAN_FOUND_LIBRARY})
                        list(APPEND _CONAN_ACTUAL_TARGETS ${_LIB_NAME})
                    else()
-                       conan_message(STATUS "Skipping already existing target: ${_LIB_NAME}")
+                       message(VERBOSE "Conan: Skipping already existing target: ${_LIB_NAME}")
                    endif()
                    list(APPEND _out_libraries_target ${_LIB_NAME})
-                   conan_message(DEBUG "Found: ${CONAN_FOUND_LIBRARY}")
+                   message(VERBOSE "Conan: Found: ${CONAN_FOUND_LIBRARY}")
                else()
-                   conan_message(FATAL_ERROR "Library '${_LIBRARY_NAME}' not found in package. If '${_LIBRARY_NAME}' is a system library, declare it with 'cpp_info.system_libs' property")
+                   message(FATAL_ERROR "Library '${_LIBRARY_NAME}' not found in package. If '${_LIBRARY_NAME}' is a system library, declare it with 'cpp_info.system_libs' property")
                endif()
                unset(CONAN_FOUND_LIBRARY CACHE)
            endforeach()
diff --git a/conan/tools/cmake/cmakedeps/templates/targets.py b/conan/tools/cmake/cmakedeps/templates/targets.py
index 8096b9712..c3cea116d 100644
--- a/conan/tools/cmake/cmakedeps/templates/targets.py
+++ b/conan/tools/cmake/cmakedeps/templates/targets.py
@@ -66,13 +66,13 @@ class TargetsTemplate(CMakeDepsFileTemplate):
         foreach(_COMPONENT {{ '${' + pkg_name + '_COMPONENT_NAMES' + '}' }} )
             if(NOT TARGET ${_COMPONENT})
                 add_library(${_COMPONENT} INTERFACE IMPORTED)
-                conan_message(STATUS "Conan: Component target declared '${_COMPONENT}'")
+                message(STATUS "Conan: Component target declared '${_COMPONENT}'")
             endif()
         endforeach()
 
         if(NOT TARGET {{ root_target_name }})
             add_library({{ root_target_name }} INTERFACE IMPORTED)
-            conan_message(STATUS "Conan: Target declared '{{ root_target_name }}'")
+            message(STATUS "Conan: Target declared '{{ root_target_name }}'")
         endif()
 
         {%- for alias, target in cmake_target_aliases.items() %}
diff --git a/conan/tools/cmake/layout.py b/conan/tools/cmake/layout.py
index 18f78c5e3..6a464f5c3 100644
--- a/conan/tools/cmake/layout.py
+++ b/conan/tools/cmake/layout.py
@@ -14,13 +14,14 @@ def cmake_layout(conanfile, generator=None, src_folder="."):
         else:
             multi = False
 
-    conanfile.folders.source = src_folder
+    subproject = conanfile.folders.subproject
+    conanfile.folders.source = src_folder if not subproject else os.path.join(subproject, src_folder)
     try:
         build_type = str(conanfile.settings.build_type)
     except ConanException:
         raise ConanException("'build_type' setting not defined, it is necessary for cmake_layout()")
 
-    build_folder = "build"
+    build_folder = "build" if not subproject else os.path.join(subproject, "build")
     custom_conf = get_build_folder_custom_vars(conanfile)
     if custom_conf:
         build_folder = "{}/{}".format(build_folder, custom_conf)
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index 1f0d0a198..5db4af04e 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -854,13 +854,21 @@ class OutputDirsBlock(Block):
 
         return textwrap.dedent("""
            set(CMAKE_INSTALL_PREFIX "{{package_folder}}")
+           {% if default_bin %}
            set(CMAKE_INSTALL_BINDIR "{{default_bin}}")
            set(CMAKE_INSTALL_SBINDIR "{{default_bin}}")
            set(CMAKE_INSTALL_LIBEXECDIR "{{default_bin}}")
+           {% endif %}
+           {% if default_lib %}
            set(CMAKE_INSTALL_LIBDIR "{{default_lib}}")
+           {% endif %}
+           {% if default_include %}
            set(CMAKE_INSTALL_INCLUDEDIR "{{default_include}}")
            set(CMAKE_INSTALL_OLDINCLUDEDIR "{{default_include}}")
+           {% endif %}
+           {% if default_res %}
            set(CMAKE_INSTALL_DATAROOTDIR "{{default_res}}")
+           {% endif %}
         """)
 
     def _get_cpp_info_value(self, name):
diff --git a/conan/tools/gnu/gnudeps_flags.py b/conan/tools/gnu/gnudeps_flags.py
index 734254329..2546c7831 100644
--- a/conan/tools/gnu/gnudeps_flags.py
+++ b/conan/tools/gnu/gnudeps_flags.py
@@ -48,7 +48,6 @@ class GnuDepsFlags(object):
         os_ = self._conanfile.settings.get_safe("os")
         if not frameworks or not is_apple_os(os_):
             return []
-        # FIXME: Missing support for subsystems
         compiler = self._conanfile.settings.get_safe("compiler")
         if str(compiler) not in self._GCC_LIKE:
             return []
@@ -60,15 +59,14 @@ class GnuDepsFlags(object):
     def _format_include_paths(self, include_paths):
         if not include_paths:
             return []
-        # FIXME: Missing support for subsystems
-        return ["-I%s" % (self._adjust_path(include_path))
+        pattern = "/I%s" if is_msvc(self._conanfile) else "-I%s"
+        return [pattern % (self._adjust_path(include_path))
                 for include_path in include_paths if include_path]
 
     def _format_library_paths(self, library_paths):
         if not library_paths:
             return []
-        # FIXME: Missing support for subsystems
-        pattern = "-LIBPATH:%s" if is_msvc(self._conanfile) else "-L%s"
+        pattern = "/LIBPATH:%s" if is_msvc(self._conanfile) else "-L%s"
         return [pattern % self._adjust_path(library_path)
                 for library_path in library_paths if library_path]
 
diff --git a/conan/tools/layout/__init__.py b/conan/tools/layout/__init__.py
index 9cd971712..8f64e14e7 100644
--- a/conan/tools/layout/__init__.py
+++ b/conan/tools/layout/__init__.py
@@ -6,10 +6,12 @@ from conan.tools.google import bazel_layout
 
 
 def basic_layout(conanfile, src_folder="."):
-    conanfile.folders.build = "build"
+    subproject = conanfile.folders.subproject
+
+    conanfile.folders.source = src_folder if not subproject else os.path.join(subproject, src_folder)
+    conanfile.folders.build = "build" if not subproject else os.path.join(subproject, "build")
     if conanfile.settings.get_safe("build_type"):
         conanfile.folders.build += "-{}".format(str(conanfile.settings.build_type).lower())
     conanfile.folders.generators = os.path.join(conanfile.folders.build, "conan")
     conanfile.cpp.build.bindirs = ["."]
     conanfile.cpp.build.libdirs = ["."]
-    conanfile.folders.source = src_folder
diff --git a/conan/tools/meson/__init__.py b/conan/tools/meson/__init__.py
index 9f317238a..b1fe7d9b9 100644
--- a/conan/tools/meson/__init__.py
+++ b/conan/tools/meson/__init__.py
@@ -1,3 +1,4 @@
-from conan.tools.meson.toolchain import MesonToolchain
 from conan.tools.meson.meson import Meson
+from conan.tools.meson.mesondeps import MesonDeps
+from conan.tools.meson.toolchain import MesonToolchain
 
diff --git a/conan/tools/meson/meson.py b/conan/tools/meson/meson.py
index 1285bab47..1b467ba35 100644
--- a/conan/tools/meson/meson.py
+++ b/conan/tools/meson/meson.py
@@ -1,7 +1,8 @@
 import os
 
 from conan.tools.build import build_jobs
-from conan.tools.meson import MesonToolchain
+from conan.tools.meson.toolchain import MesonToolchain
+from conan.tools.meson.mesondeps import MesonDeps
 
 
 class Meson(object):
@@ -15,10 +16,18 @@ class Meson(object):
         generators_folder = self._conanfile.generators_folder
         cross = os.path.join(generators_folder, MesonToolchain.cross_filename)
         native = os.path.join(generators_folder, MesonToolchain.native_filename)
+        deps_flags = os.path.join(generators_folder, MesonDeps.filename)  # extra machine files layer
+        has_deps_flags = os.path.exists(deps_flags)
+
         if os.path.exists(cross):
             cmd += ' --cross-file "{}"'.format(cross)
+            if has_deps_flags:
+                cmd += ' --cross-file "{}"'.format(deps_flags)
         else:
             cmd += ' --native-file "{}"'.format(native)
+            if has_deps_flags:
+                cmd += ' --native-file "{}"'.format(deps_flags)
+
         cmd += ' "{}" "{}"'.format(build_folder, source_folder)
         if self._conanfile.package_folder:
             cmd += ' -Dprefix="{}"'.format(self._conanfile.package_folder)
diff --git a/conan/tools/meson/mesondeps.py b/conan/tools/meson/mesondeps.py
new file mode 100644
index 000000000..84ec1e582
--- /dev/null
+++ b/conan/tools/meson/mesondeps.py
@@ -0,0 +1,105 @@
+import textwrap
+
+from jinja2 import Template
+
+from conan.tools.gnu.gnudeps_flags import GnuDepsFlags
+from conan.tools.meson.helpers import to_meson_value
+from conans.model.new_build_info import NewCppInfo
+from conans.util.files import save
+
+
+class MesonDeps(object):
+    """Generator that manages all the GNU flags from all the dependencies"""
+
+    filename = "conan_meson_deps_flags.ini"
+
+    _meson_file_template = textwrap.dedent("""
+    [constants]
+    deps_c_args = {{c_args}}
+    deps_c_link_args = {{c_link_args}}
+    deps_cpp_args = {{cpp_args}}
+    deps_cpp_link_args = {{cpp_link_args}}
+    """)
+
+    def __init__(self, conanfile):
+        self._conanfile = conanfile
+        self._ordered_deps = []
+        # constants
+        self.c_args = []
+        self.c_link_args = []
+        self.cpp_args = []
+        self.cpp_link_args = []
+
+    # TODO: Add all this logic to GnuDepsFlags? Distinguish between GnuFlags and GnuDepsFlags?
+    @property
+    def ordered_deps(self):
+        if not self._ordered_deps:
+            deps = self._conanfile.dependencies.host.topological_sort
+            self._ordered_deps = [dep for dep in reversed(deps.values())]
+        return self._ordered_deps
+
+    def _get_cpp_info(self):
+        ret = NewCppInfo()
+        for dep in self.ordered_deps:
+            dep_cppinfo = dep.cpp_info.aggregated_components()
+            # In case we have components, aggregate them, we do not support isolated
+            # "targets" with autotools
+            ret.merge(dep_cppinfo)
+        return ret
+
+    def _rpaths_flags(self):
+        flags = []
+        for dep in self.ordered_deps:
+            flags.extend(["-Wl,-rpath -Wl,{}".format(libdir) for libdir in dep.cpp_info.libdirs
+                          if dep.options.get_safe("shared", False)])
+        return flags
+
+    def get_gnu_flags(self):
+        flags = GnuDepsFlags(self._conanfile, self._get_cpp_info())
+
+        # cpp_flags
+        cpp_flags = []
+        cpp_flags.extend(flags.include_paths)
+        cpp_flags.extend(flags.defines)
+
+        # Ldflags
+        ldflags = flags.sharedlinkflags
+        ldflags.extend(flags.exelinkflags)
+        ldflags.extend(flags.frameworks)
+        ldflags.extend(flags.framework_paths)
+        ldflags.extend(flags.lib_paths)
+
+        # set the rpath in Macos so that the library are found in the configure step
+        if self._conanfile.settings.get_safe("os") == "Macos":
+            ldflags.extend(self._rpaths_flags())
+
+        # libs
+        libs = flags.libs
+        libs.extend(flags.system_libs)
+
+        # cflags
+        cflags = flags.cflags
+        cxxflags = flags.cxxflags
+        return cflags, cxxflags, cpp_flags, ldflags, libs
+
+    def _context(self):
+        cflags, cxxflags, cpp_flags, ldflags, _ = self.get_gnu_flags()
+        self.c_args.extend(cflags + cpp_flags)
+        self.cpp_args.extend(cxxflags + cpp_flags)
+        self.c_link_args.extend(ldflags)
+        self.cpp_link_args.extend(ldflags)
+
+        return {
+            "c_args": to_meson_value(self.c_args),
+            "c_link_args": to_meson_value(self.c_link_args),
+            "cpp_args": to_meson_value(self.cpp_args),
+            "cpp_link_args": to_meson_value(self.cpp_link_args),
+        }
+
+    def _content(self):
+        context = self._context()
+        content = Template(self._meson_file_template).render(context)
+        return content
+
+    def generate(self):
+        save(self.filename, self._content())
diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py
index cbedd32c2..f095c8679 100644
--- a/conan/tools/meson/toolchain.py
+++ b/conan/tools/meson/toolchain.py
@@ -26,6 +26,11 @@ class MesonToolchain(object):
     [constants]
     preprocessor_definitions = [{% for it, value in preprocessor_definitions.items() -%}
     '-D{{ it }}="{{ value}}"'{%- if not loop.last %}, {% endif %}{% endfor %}]
+    # Constants to be overridden by conan_meson_deps_flags.ini (if exists)
+    deps_c_args = []
+    deps_c_link_args = []
+    deps_cpp_args = []
+    deps_cpp_link_args = []
 
     [project options]
     {% for it, value in project_options.items() -%}
@@ -52,10 +57,10 @@ class MesonToolchain(object):
     {% if b_staticpic %}b_staticpic = {{b_staticpic}}{% endif %}
     {% if cpp_std %}cpp_std = '{{cpp_std}}' {% endif %}
     {% if backend %}backend = '{{backend}}' {% endif %}
-    c_args = {{c_args}} + preprocessor_definitions
-    c_link_args = {{c_link_args}}
-    cpp_args = {{cpp_args}} + preprocessor_definitions
-    cpp_link_args = {{cpp_link_args}}
+    c_args = {{c_args}} + preprocessor_definitions + deps_c_args
+    c_link_args = {{c_link_args}} + deps_c_link_args
+    cpp_args = {{cpp_args}} + preprocessor_definitions + deps_cpp_args
+    cpp_link_args = {{cpp_link_args}} + deps_cpp_link_args
     {% if pkg_config_path %}pkg_config_path = '{{pkg_config_path}}'{% endif %}
 
     {% for context, values in cross_build.items() %}
diff --git a/conan/tools/microsoft/layout.py b/conan/tools/microsoft/layout.py
index 9d3a045e0..4da792179 100644
--- a/conan/tools/microsoft/layout.py
+++ b/conan/tools/microsoft/layout.py
@@ -15,13 +15,14 @@ def vs_layout(conanfile):
         if not arch:
             raise ConanException("The 'vs_layout' doesn't "
                                  "work with the arch '{}'".format(conanfile.settings.arch))
-        base = os.path.join(arch, str(conanfile.settings.build_type))
+        bindirs = os.path.join(arch, str(conanfile.settings.build_type))
     else:
-        base = str(conanfile.settings.build_type)
+        bindirs = str(conanfile.settings.build_type)
 
-    conanfile.folders.build = "."
-    conanfile.folders.generators = "conan"
-    conanfile.folders.source = "."
-    conanfile.cpp.build.libdirs = [base]
-    conanfile.cpp.build.bindirs = [base]
+    subproject = conanfile.folders.subproject
+    conanfile.folders.build = subproject or "."
+    conanfile.folders.generators = os.path.join(subproject, "conan") if subproject else "conan"
+    conanfile.folders.source = subproject or "."
+    conanfile.cpp.build.libdirs = [bindirs]
+    conanfile.cpp.build.bindirs = [bindirs]
     conanfile.cpp.source.includedirs = ["include"]
diff --git a/conans/client/conan_command_output.py b/conans/client/conan_command_output.py
index 588b48240..91a6fff6a 100644
--- a/conans/client/conan_command_output.py
+++ b/conans/client/conan_command_output.py
@@ -140,6 +140,10 @@ class CommandOutputer(object):
             item_data["build_id"] = build_id(conanfile)
             item_data["context"] = conanfile.context
 
+            item_data["invalid_build"] = node.cant_build is not False
+            if node.cant_build:
+                item_data["invalid_build_reason"] = node.cant_build
+
             python_requires = getattr(conanfile, "python_requires", None)
             if python_requires and not isinstance(python_requires, dict):  # no old python requires
                 item_data["python_requires"] = [repr(r)
diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py
index 9c76dd41b..246af0a51 100644
--- a/conans/client/generators/__init__.py
+++ b/conans/client/generators/__init__.py
@@ -72,7 +72,8 @@ class GeneratorManager(object):
                                 "MesonToolchain", "MSBuildDeps", "QbsToolchain", "msbuild",
                                 "VirtualRunEnv", "VirtualBuildEnv", "AutotoolsDeps",
                                 "AutotoolsToolchain", "BazelDeps", "BazelToolchain", "PkgConfigDeps",
-                                "VCVars", "IntelCC", "XcodeDeps", "PremakeDeps", "XcodeToolchain"]
+                                "VCVars", "IntelCC", "XcodeDeps", "PremakeDeps", "XcodeToolchain",
+                                "MesonDeps"]
 
     def add(self, name, generator_class, custom=False):
         if name not in self._generators or custom:
@@ -113,6 +114,9 @@ class GeneratorManager(object):
         elif generator_name == "MesonToolchain":
             from conan.tools.meson import MesonToolchain
             return MesonToolchain
+        elif generator_name == "MesonDeps":
+            from conan.tools.meson import MesonDeps
+            return MesonDeps
         elif generator_name in ("MSBuildDeps", "msbuild"):
             from conan.tools.microsoft import MSBuildDeps
             return MSBuildDeps
diff --git a/conans/client/graph/graph.py b/conans/client/graph/graph.py
index dbfc5ea5c..fbcfac5c5 100644
--- a/conans/client/graph/graph.py
+++ b/conans/client/graph/graph.py
@@ -91,6 +91,8 @@ class Node(object):
         self.id_direct_prefs = None
         self.id_indirect_prefs = None
 
+        self.cant_build = False  # It will set to a str with a reason if the validate_build() fails
+
     @property
     def id(self):
         return self._id
diff --git a/conans/client/graph/graph_binaries.py b/conans/client/graph/graph_binaries.py
index 103832b2a..4977e2d68 100644
--- a/conans/client/graph/graph_binaries.py
+++ b/conans/client/graph/graph_binaries.py
@@ -48,7 +48,10 @@ class GraphBinariesAnalyzer(object):
                     break
         if build_mode.forced(conanfile, ref, with_deps_to_build):
             conanfile.output.info('Forced build from source')
-            node.binary = BINARY_BUILD
+            if node.cant_build:
+                node.binary = BINARY_INVALID
+            else:
+                node.binary = BINARY_BUILD
             node.prev = None
             return True
 
@@ -178,7 +181,10 @@ class GraphBinariesAnalyzer(object):
             pref = PackageReference(locked.ref, locked.package_id, locked.prev)  # Keep locked PREV
             self._process_node(node, pref, build_mode, update, remotes)
             if node.binary == BINARY_MISSING and build_mode.allowed(node.conanfile):
-                node.binary = BINARY_BUILD
+                if node.cant_build:
+                    node.binary = BINARY_INVALID
+                else:
+                    node.binary = BINARY_BUILD
             if node.binary == BINARY_BUILD:
                 locked.unlock_prev()
 
@@ -231,7 +237,10 @@ class GraphBinariesAnalyzer(object):
                     if node.binary == BINARY_MISSING and node.package_id == PACKAGE_ID_INVALID:
                         node.binary = BINARY_INVALID
                 if node.binary == BINARY_MISSING and build_mode.allowed(node.conanfile):
-                    node.binary = BINARY_BUILD
+                    if node.cant_build:
+                        node.binary = BINARY_INVALID
+                    else:
+                        node.binary = BINARY_BUILD
 
             if locked:
                 # package_id was not locked, this means a base lockfile that is being completed
@@ -253,6 +262,8 @@ class GraphBinariesAnalyzer(object):
             node.binary = BINARY_INVALID
             return
 
+
+
         if self._evaluate_build(node, build_mode):
             return
 
@@ -293,7 +304,10 @@ class GraphBinariesAnalyzer(object):
                 local_recipe_hash = package_layout.recipe_manifest().summary_hash
                 if local_recipe_hash != recipe_hash:
                     conanfile.output.info("Outdated package!")
-                    node.binary = BINARY_BUILD
+                    if node.cant_build:
+                        node.binary = BINARY_INVALID
+                    else:
+                        node.binary = BINARY_BUILD
                     node.prev = None
                 else:
                     conanfile.output.info("Package is up to date")
@@ -400,6 +414,14 @@ class GraphBinariesAnalyzer(object):
                 except ConanInvalidConfiguration as e:
                     conanfile.info.invalid = str(e)
 
+        if hasattr(conanfile, "validate_build") and callable(conanfile.validate_build):
+            with conanfile_exception_formatter(str(conanfile), "validate_build"):
+                try:
+                    conanfile.validate_build()
+                except ConanInvalidConfiguration as e:
+                    # This 'cant_build' will be ignored if we don't have to build the node.
+                    node.cant_build = str(e)
+
         info = conanfile.info
         node.package_id = info.package_id()
 
diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py
index 9f9d3baa6..05b7ef1df 100644
--- a/conans/client/graph/graph_builder.py
+++ b/conans/client/graph/graph_builder.py
@@ -3,7 +3,7 @@ import time
 from conans.client.conanfile.configure import run_configure_method
 from conans.client.graph.graph import DepsGraph, Node, RECIPE_EDITABLE, CONTEXT_HOST, CONTEXT_BUILD
 from conans.errors import (ConanException, ConanExceptionInUserConanfileMethod,
-                           conanfile_exception_formatter)
+                           conanfile_exception_formatter, ConanInvalidConfiguration)
 from conans.model.conan_file import get_env_context_manager
 from conans.model.ref import ConanFileReference
 from conans.model.requires import Requirements, Requirement
@@ -65,7 +65,9 @@ class DepsGraphBuilder(object):
         t1 = time.time()
         self._expand_node(root_node, dep_graph, Requirements(), None, None, check_updates,
                           update, remotes, profile_host, profile_build, graph_lock)
+
         logger.debug("GRAPH: Time to load deps %s" % (time.time() - t1))
+
         return dep_graph
 
     def extend_build_requires(self, graph, node, build_requires_refs, check_updates, update,
@@ -471,4 +473,5 @@ class DepsGraphBuilder(object):
 
         dep_graph.add_node(new_node)
         dep_graph.add_edge(current_node, new_node, requirement)
+
         return new_node
diff --git a/conans/client/graph/graph_manager.py b/conans/client/graph/graph_manager.py
index 3568566e7..10a75d463 100644
--- a/conans/client/graph/graph_manager.py
+++ b/conans/client/graph/graph_manager.py
@@ -124,6 +124,7 @@ class GraphManager(object):
         root_node = self._load_root_node(reference, create_reference, profile_host, graph_lock,
                                          root_ref, lockfile_node_id, is_build_require,
                                          require_overrides)
+
         deps_graph = self._resolve_graph(root_node, profile_host, profile_build, graph_lock,
                                          build_mode, check_updates, update, remotes, recorder,
                                          apply_build_requires=apply_build_requires)
diff --git a/conans/client/installer.py b/conans/client/installer.py
index 65649b942..260c7dcdd 100644
--- a/conans/client/installer.py
+++ b/conans/client/installer.py
@@ -423,7 +423,13 @@ class BinaryInstaller(object):
         if invalid:
             msg = ["There are invalid packages (packages that cannot exist for this configuration):"]
             for node in invalid:
-                msg.append("{}: Invalid ID: {}".format(node.conanfile, node.conanfile.info.invalid))
+                if node.cant_build:
+                    msg.append("{}: Cannot build "
+                               "for this configuration: {}".format(node.conanfile,
+                                                                   node.cant_build))
+                else:
+                    msg.append("{}: Invalid ID: {}".format(node.conanfile,
+                                                           node.conanfile.info.invalid))
             raise ConanInvalidConfiguration("\n".join(msg))
         self._raise_missing(missing)
         processed_package_refs = {}
diff --git a/conans/client/rest/rest_client.py b/conans/client/rest/rest_client.py
index ab7549083..aa4c74ad3 100644
--- a/conans/client/rest/rest_client.py
+++ b/conans/client/rest/rest_client.py
@@ -111,7 +111,8 @@ class RestApiClient(object):
 
     def authenticate(self, user, password):
         api_v1 = RestV1Methods(self._remote_url, self._token, self._custom_headers, self._output,
-                               self._requester, self._verify_ssl, self._artifacts_properties)
+                               self._requester, self._config, self._verify_ssl,
+                               self._artifacts_properties)
 
         if self._refresh_token and self._token:
             token, refresh_token = api_v1.refresh_token(self._token, self._refresh_token)
diff --git a/conans/model/layout.py b/conans/model/layout.py
index cce15ddd5..d3c4828cb 100644
--- a/conans/model/layout.py
+++ b/conans/model/layout.py
@@ -31,6 +31,10 @@ class Folders(object):
         # Relative location of the project root, if the conanfile is not in that project root, but
         # in a subfolder: e.g: If the conanfile is in a subfolder then self.root = ".."
         self.root = None
+        # The relative location with respect to the project root of the subproject containing the
+        # conanfile.py, that makes most of the output folders defined in layouts (cmake_layout, etc)
+        # start from the subproject again
+        self.subproject = None
 
     def __repr__(self):
         return str(self.__dict__)


Test patch:
diff --git a/conans/test/functional/layout/test_in_subfolder.py b/conans/test/functional/layout/test_in_subfolder.py
index 33925fa67..af3f58293 100644
--- a/conans/test/functional/layout/test_in_subfolder.py
+++ b/conans/test/functional/layout/test_in_subfolder.py
@@ -1,5 +1,9 @@
 import textwrap
 
+import pytest
+
+from conans.test.assets.cmake import gen_cmakelists
+from conans.test.assets.sources import gen_function_cpp
 from conans.test.utils.tools import TestClient
 
 
@@ -54,3 +58,113 @@ def test_exports_sources_own_code_in_subfolder():
     c.run("build conan")
     assert "conanfile.py (pkg/0.1): MYCMAKE-BUILD: mycmake!" in c.out
     assert c.load("build/mylib.a") == "mylib"
+
+
+def test_exports_sources_common_code():
+    """ very similar to the above, but intended for a multi-package project sharing some
+    common code
+    """
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        import os
+        from conan import ConanFile
+        from conan.tools.files import load, copy, save
+        class Pkg(ConanFile):
+            name = "pkg"
+            version = "0.1"
+
+            def layout(self):
+                self.folders.root = ".."
+                self.folders.source = "pkg"
+                self.folders.build = "build"
+
+            def export_sources(self):
+                source_folder = os.path.join(self.recipe_folder, "..")
+                copy(self, "*.txt", source_folder, self.export_sources_folder)
+                copy(self, "*.cmake", source_folder, self.export_sources_folder)
+
+            def source(self):
+                cmake = load(self, "CMakeLists.txt")
+                self.output.info("MYCMAKE-SRC: {}".format(cmake))
+
+            def build(self):
+                path = os.path.join(self.source_folder, "CMakeLists.txt")
+                cmake = load(self, path)
+                self.output.info("MYCMAKE-BUILD: {}".format(cmake))
+
+                path = os.path.join(self.export_sources_folder, "common", "myutils.cmake")
+                cmake = load(self, path)
+                self.output.info("MYUTILS-BUILD: {}".format(cmake))
+            """)
+    c.save({"pkg/conanfile.py": conanfile,
+            "pkg/CMakeLists.txt": "mycmake!",
+            "common/myutils.cmake": "myutils!"})
+    c.run("create pkg")
+    assert "pkg/0.1: MYCMAKE-SRC: mycmake!" in c.out
+    assert "pkg/0.1: MYCMAKE-BUILD: mycmake!" in c.out
+    assert "pkg/0.1: MYUTILS-BUILD: myutils!" in c.out
+
+    # Local flow
+    c.run("install pkg")
+    # SOURCE NOT CALLED! It doesnt make sense (will fail due to local exports)
+    c.run("build pkg")
+    assert "conanfile.py (pkg/0.1): MYCMAKE-BUILD: mycmake!" in c.out
+    assert "conanfile.py (pkg/0.1): MYUTILS-BUILD: myutils!" in c.out
+
+
+@pytest.mark.tool_cmake
+def test_exports_sources_common_code_layout():
+    """ Equal to the previous test, but actually building and using cmake_layout
+    """
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        import os
+        from conan import ConanFile
+        from conan.tools.cmake import cmake_layout, CMake
+        from conan.tools.files import load, copy, save
+        class Pkg(ConanFile):
+            name = "pkg"
+            version = "0.1"
+            settings = "os", "compiler", "build_type", "arch"
+            generators = "CMakeToolchain"
+
+            def layout(self):
+                self.folders.root = ".."
+                self.folders.subproject = "pkg"
+                cmake_layout(self)
+
+            def export_sources(self):
+                source_folder = os.path.join(self.recipe_folder, "..")
+                copy(self, "*", source_folder, self.export_sources_folder)
+
+            def build(self):
+                cmake = CMake(self)
+                cmake.configure()
+                cmake.build()
+                self.run(os.path.join(self.cpp.build.bindirs[0], "myapp"))
+            """)
+    cmake_include = "include(${CMAKE_CURRENT_LIST_DIR}/../common/myutils.cmake)"
+    c.save({"pkg/conanfile.py": conanfile,
+            "pkg/app.cpp": gen_function_cpp(name="main", includes=["../common/myheader"],
+                                            preprocessor=["MYDEFINE"]),
+            "pkg/CMakeLists.txt": gen_cmakelists(appsources=["app.cpp"],
+                                                 custom_content=cmake_include),
+            "common/myutils.cmake": 'message(STATUS "MYUTILS.CMAKE!")',
+            "common/myheader.h": '#define MYDEFINE "MYDEFINEVALUE"'})
+    c.run("create pkg")
+    assert "MYUTILS.CMAKE!" in c.out
+    assert "main: Release!" in c.out
+    assert "MYDEFINE: MYDEFINEVALUE" in c.out
+
+    # Local flow
+    c.run("install pkg")
+    c.run("build pkg")
+    assert "MYUTILS.CMAKE!" in c.out
+    assert "main: Release!" in c.out
+    assert "MYDEFINE: MYDEFINEVALUE" in c.out
+
+    c.run("install pkg -s build_type=Debug")
+    c.run("build pkg")
+    assert "MYUTILS.CMAKE!" in c.out
+    assert "main: Debug!" in c.out
+    assert "MYDEFINE: MYDEFINEVALUE" in c.out
diff --git a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
index c7101504c..095ae5c61 100644
--- a/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
+++ b/conans/test/functional/toolchains/cmake/test_cmake_toolchain.py
@@ -918,3 +918,94 @@ def test_cmake_presets_forbidden_build_type():
     client.run("install . {}".format(settings_layout), assert_error=True)
     assert "Error, don't include 'settings.build_type' in the " \
            "'tools.cmake.cmake_layout:build_folder_vars' conf" in client.out
+
+
+def test_resdirs_cmake_install():
+    """If resdirs is declared, the CMAKE_INSTALL_DATAROOTDIR folder is set"""
+
+    client = TestClient(path_with_spaces=False)
+
+    conanfile = textwrap.dedent("""
+            from conan import ConanFile
+            from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
+
+            class AppConan(ConanFile):
+                settings = "os", "compiler", "build_type", "arch"
+                exports_sources = "CMakeLists.txt", "my_license"
+                name = "foo"
+                version = "1.0"
+
+                def generate(self):
+                    tc = CMakeToolchain(self)
+                    tc.generate()
+
+                def layout(self):
+                    self.cpp.package.resdirs = ["res"]
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure()
+                    cmake.build()
+
+                def package(self):
+                    cmake = CMake(self)
+                    cmake.install()
+            """)
+
+    cmake = """
+    cmake_minimum_required(VERSION 3.15)
+    set(CMAKE_CXX_COMPILER_WORKS 1)
+    project(foo)
+    if(NOT CMAKE_INSTALL_DATAROOTDIR)
+        message(FATAL_ERROR "Cannot install stuff")
+    endif()
+    install(FILES my_license DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/licenses)
+    """
+
+    client.save({"conanfile.py": conanfile, "CMakeLists.txt": cmake, "my_license": "MIT"})
+    client.run("create .")
+    assert "/res/licenses/my_license" in client.out
+    assert "Packaged 1 file: my_license" in client.out
+
+
+def test_resdirs_none_cmake_install():
+    """If no resdirs are declared, the CMAKE_INSTALL_DATAROOTDIR folder is not set"""
+
+    client = TestClient(path_with_spaces=False)
+
+    conanfile = textwrap.dedent("""
+            from conan import ConanFile
+            from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
+
+            class AppConan(ConanFile):
+                settings = "os", "compiler", "build_type", "arch"
+                exports_sources = "CMakeLists.txt", "my_license"
+                name = "foo"
+                version = "1.0"
+
+                def generate(self):
+                    tc = CMakeToolchain(self)
+                    tc.generate()
+
+                def build(self):
+                    cmake = CMake(self)
+                    cmake.configure()
+                    cmake.build()
+
+                def package(self):
+                    cmake = CMake(self)
+                    cmake.install()
+            """)
+
+    cmake = """
+    cmake_minimum_required(VERSION 3.15)
+    set(CMAKE_CXX_COMPILER_WORKS 1)
+    project(foo)
+    if(NOT CMAKE_INSTALL_DATAROOTDIR)
+        message(FATAL_ERROR "Cannot install stuff")
+    endif()
+    """
+
+    client.save({"conanfile.py": conanfile, "CMakeLists.txt": cmake, "my_license": "MIT"})
+    client.run("create .", assert_error=True)
+    assert "Cannot install stuff" in client.out
diff --git a/conans/test/functional/toolchains/meson/test_meson_and_gnu_deps_flags.py b/conans/test/functional/toolchains/meson/test_meson_and_gnu_deps_flags.py
new file mode 100644
index 000000000..8c3d0c018
--- /dev/null
+++ b/conans/test/functional/toolchains/meson/test_meson_and_gnu_deps_flags.py
@@ -0,0 +1,146 @@
+import os
+import platform
+import textwrap
+
+from conan.tools.files import load
+from conans.test.assets.sources import gen_function_cpp
+from conans.test.functional.toolchains.meson._base import TestMesonBase
+from conans.test.utils.tools import TestClient
+
+
+class TestMesonToolchainAndGnuFlags(TestMesonBase):
+
+    def test_mesondeps(self):
+        client = TestClient(path_with_spaces=False)
+        client.run("new hello/0.1 -s")
+        client.run("create . hello/0.1@ %s" % self._settings_str)
+        app = gen_function_cpp(name="main", includes=["hello"], calls=["hello"])
+
+        conanfile_py = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.meson import Meson
+
+        class App(ConanFile):
+            settings = "os", "arch", "compiler", "build_type"
+            requires = "hello/0.1"
+            generators = "MesonDeps", "MesonToolchain"
+
+            def layout(self):
+                self.folders.build = "build"
+
+            def build(self):
+                meson = Meson(self)
+                meson.configure()
+                meson.build()
+        """)
+
+        meson_build = textwrap.dedent("""
+        project('tutorial', 'cpp')
+        cxx = meson.get_compiler('cpp')
+        hello = cxx.find_library('hello', required: true)
+        executable('demo', 'main.cpp', dependencies: hello)
+        """)
+
+        client.save({"conanfile.py": conanfile_py,
+                     "meson.build": meson_build,
+                     "main.cpp": app},
+                    clean_first=True)
+
+        client.run("install . %s" % self._settings_str)
+        client.run("build .")
+        assert "[2/2] Linking target demo" in client.out
+
+    def test_mesondeps_flags_are_being_appended_and_not_replacing_toolchain_ones(self):
+        """
+        Test MesonDeps and MesonToolchain are keeping all the flags/definitions defined
+        from both generators and nothing is being messed up.
+        """
+        client = TestClient(path_with_spaces=False)
+        if platform.system() == "Windows":
+            deps_flags = '"/GA", "/analyze:quiet"'
+            flags = '"/Wall", "/W4"'
+        else:
+            deps_flags = '"-Wpedantic", "-Werror"'
+            flags = '"-Wall", "-finline-functions"'
+        # Dependency - hello/0.1
+        conanfile_py = textwrap.dedent("""
+        from conan import ConanFile
+
+        class HelloConan(ConanFile):
+            name = "hello"
+            version = "0.1"
+
+            def package_info(self):
+                self.cpp_info.libs = ["hello"]
+                self.cpp_info.cxxflags = [{}]
+                self.cpp_info.defines = ['DEF1=one_string', 'DEF2=other_string']
+        """.format(deps_flags))
+        client.save({"conanfile.py": conanfile_py})
+        client.run("create . %s" % self._settings_str)
+        # Dependency - other/0.1
+        conanfile_py = textwrap.dedent("""
+        from conan import ConanFile
+
+        class OtherConan(ConanFile):
+            name = "other"
+            version = "0.1"
+
+            def package_info(self):
+                self.cpp_info.libs = ["other"]
+                self.cpp_info.defines = ['DEF3=simple_string']
+        """)
+        client.save({"conanfile.py": conanfile_py}, clean_first=True)
+        client.run("create . %s" % self._settings_str)
+
+        # Consumer using MesonDeps and MesonToolchain
+        conanfile_py = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.meson import Meson, MesonDeps, MesonToolchain
+
+        class App(ConanFile):
+            settings = "os", "arch", "compiler", "build_type"
+            requires = "hello/0.1", "other/0.1"
+
+            def layout(self):
+                self.folders.build = "build"
+
+            def generate(self):
+                tc = MesonDeps(self)
+                tc.generate()
+                tc = MesonToolchain(self)
+                tc.preprocessor_definitions["VAR"] = "VALUE"
+                tc.preprocessor_definitions["VAR2"] = "VALUE2"
+                tc.generate()
+
+            def build(self):
+                meson = Meson(self)
+                meson.configure()
+                meson.build()
+        """)
+
+        meson_build = textwrap.dedent("""
+            project('tutorial', 'cpp')
+            cxx = meson.get_compiler('cpp')
+            # It's not needed to declare "hello/0.1" as a dependency, only interested in flags
+            executable('demo', 'main.cpp')
+        """)
+        client.save({"conanfile.py": conanfile_py,
+                     "meson.build": meson_build,
+                     "main.cpp": "int main()\n{return 0;}\n"},
+                    clean_first=True)
+
+        client.run("install . %s -c 'tools.build:cxxflags=[%s]'" % (self._settings_str, flags))
+        client.run("build .")
+        deps_flags = deps_flags.replace('"', "").replace(",", "")
+        flags = flags.replace('"', "").replace(",", "")
+        meson_log_path = os.path.join(client.current_folder, "build", "meson-logs", "meson-log.txt")
+        meson_log = load(None, meson_log_path)
+        meson_log = meson_log.replace("\\", "/")
+        assert "Build Options: " \
+               "'--native-file {folder}/conan_meson_native.ini' " \
+               "'--native-file {folder}/conan_meson_deps_flags.ini'" \
+               "".format(folder=client.current_folder.replace("\\", "/")) in meson_log
+        # Flags/Defines from deps and consumer are appearing in meson-log.txt as part
+        # of the command-line
+        assert '%s -DVAR="VALUE" -DVAR2="VALUE2" %s' % (flags, deps_flags) in meson_log
+        assert '-DDEF3=simple_string -DDEF1=one_string -DDEF2=other_string' in meson_log
diff --git a/conans/test/integration/graph/test_validate_build.py b/conans/test/integration/graph/test_validate_build.py
new file mode 100644
index 000000000..65ab33011
--- /dev/null
+++ b/conans/test/integration/graph/test_validate_build.py
@@ -0,0 +1,83 @@
+import json
+import textwrap
+
+from conans.test.assets.genconanfile import GenConanfile
+from conans.test.utils.tools import TestClient
+
+
+def test_basic_validate_build_test():
+
+    t = TestClient()
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+    from conans.errors import ConanInvalidConfiguration
+
+    class myConan(ConanFile):
+        name = "foo"
+        version = "1.0"
+        settings = "os", "arch", "compiler"
+
+        def validate_build(self):
+            if self.settings.compiler == "gcc":
+                raise ConanInvalidConfiguration("This doesn't build in GCC")
+
+        def package_id(self):
+            del self.info.settings.compiler
+    """)
+
+    settings_gcc = "-s compiler=gcc -s compiler.libcxx=libstdc++11 -s compiler.version=11"
+    settings_clang = "-s compiler=clang -s compiler.libcxx=libc++ -s compiler.version=8"
+
+    t.save({"conanfile.py": conanfile})
+    t.run(f"create . {settings_gcc}", assert_error=True)
+
+    assert "foo/1.0: Cannot build for this configuration: This doesn't build in GCC" in t.out
+
+    t.run(f"create . {settings_clang}")
+
+    # Now with GCC again, but now we have the binary, we don't need to build, so it doesn't fail
+    t.run(f"create . {settings_gcc} --build missing")
+    assert "foo/1.0: Already installed!" in t.out
+
+    # But if I force the build... it will fail
+    t.run(f"create . {settings_gcc} ", assert_error=True)
+    assert "foo/1.0: Cannot build for this configuration: This doesn't build in GCC" in t.out
+
+    # What happens with a conan info?
+    t.run(f"info foo/1.0@ {settings_gcc} --json=myjson")
+    myjson = json.loads(t.load("myjson"))
+    assert myjson[0]["invalid_build"] is True
+    assert myjson[0]["invalid_build_reason"] == "This doesn't build in GCC"
+
+    t.run(f"info foo/1.0@ {settings_clang} --json=myjson")
+    myjson = json.loads(t.load("myjson"))
+    assert myjson[0]["invalid_build"] is False
+    assert "invalid_build_reason" not in myjson[0]
+
+
+def test_with_options_validate_build_test():
+    t = TestClient()
+    conanfile = textwrap.dedent("""
+    from conan import ConanFile
+    from conans.errors import ConanInvalidConfiguration
+
+    class myConan(ConanFile):
+        name = "foo"
+        version = "1.0"
+        options = {"my_option": [True, False]}
+        default_options = {"my_option": True}
+
+        def validate_build(self):
+            if not self.options.my_option:
+                raise ConanInvalidConfiguration("This doesn't build with False option")
+
+    """)
+    t.save({"conanfile.py": conanfile})
+    t.run("export .")
+    consumer = GenConanfile().with_require("foo/1.0").with_name("consumer").with_version("1.0")
+    t.save({"consumer.py": consumer})
+    t.run("create consumer.py --build missing -o foo/*:my_option=False", assert_error=True)
+    assert "foo/1.0: Cannot build for this configuration: This doesn't build " \
+           "with False option" in t.out
+
+    t.run("create consumer.py --build missing -o foo/*:my_option=True")
diff --git a/conans/test/integration/layout/__init__.py b/conans/test/integration/layout/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/conans/test/integration/py_requires/python_requires_test.py b/conans/test/integration/py_requires/python_requires_test.py
index be3aa71db..c2d5bab40 100644
--- a/conans/test/integration/py_requires/python_requires_test.py
+++ b/conans/test/integration/py_requires/python_requires_test.py
@@ -969,3 +969,54 @@ def test_transitive_diamond_python_requires():
            client.out
     assert "consumer/1.0@user/channel: Calling package()\nconsumer/1.0@user/channel: 222, 234" in \
            client.out
+
+
+def test_multiple_reuse():
+    """ test how to enable the multiple code reuse for custom user generators
+        # https://github.com/conan-io/conan/issues/11589
+    """
+
+    c = TestClient()
+    common = textwrap.dedent("""
+        from conan import ConanFile
+        def mycommon():
+            return 42
+        class Common(ConanFile):
+            name = "common"
+            version = "0.1"
+        """)
+    tool = textwrap.dedent("""
+        from conan import ConanFile
+
+        class MyGenerator:
+            common = None
+            def __init__(self, conanfile):
+                self.conanfile = conanfile
+            def generate(self):
+                self.conanfile.output.info("VALUE TOOL: {}!!!".format(MyGenerator.common.mycommon()))
+
+        class Tool(ConanFile):
+            name = "tool"
+            version = "0.1"
+            python_requires = "common/0.1"
+            def init(self):
+                MyGenerator.common = self.python_requires["common"].module
+        """)
+    consumer = textwrap.dedent("""
+        from conan import ConanFile
+        class Consumer(ConanFile):
+            python_requires = "tool/0.1", "common/0.1"
+            def generate(self):
+                mycommon = self.python_requires["common"].module.mycommon
+                self.output.info("VALUE COMMON: {}!!!".format(mycommon()))
+                mygenerator = self.python_requires["tool"].module.MyGenerator(self)
+                mygenerator.generate()
+        """)
+    c.save({"common/conanfile.py": common,
+            "tool/conanfile.py": tool,
+            "consumer/conanfile.py": consumer})
+    c.run("export common")
+    c.run("export tool")
+    c.run("install consumer")
+    assert "VALUE COMMON: 42!!!" in c.out
+    assert "VALUE TOOL: 42!!!" in c.out
diff --git a/conans/test/integration/toolchains/apple/test_xcodedeps.py b/conans/test/integration/toolchains/apple/test_xcodedeps.py
index c60ea523a..3467b7da2 100644
--- a/conans/test/integration/toolchains/apple/test_xcodedeps.py
+++ b/conans/test/integration/toolchains/apple/test_xcodedeps.py
@@ -174,3 +174,109 @@ def test_xcodedeps_aggregate_components():
 
     # folders are aggregated
     assert "mylibdir" in component4_vars
+
+
+@pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS")
+def test_xcodedeps_frameworkdirs():
+    client = TestClient()
+
+    conanfile_py = textwrap.dedent("""
+        from conan import ConanFile
+        class LibConan(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            def package_info(self):
+                self.cpp_info.frameworkdirs = ["lib_a_frameworkdir"]
+        """)
+
+    client.save({"conanfile.py": conanfile_py})
+    client.run("create conanfile.py lib_a/1.0@")
+
+    client.run("install lib_a/1.0@ -g XcodeDeps")
+
+    lib_a_xcconfig = client.load("conan_lib_a_lib_a_release_x86_64.xcconfig")
+
+    assert "lib_a_frameworkdir" in lib_a_xcconfig
+
+
+@pytest.mark.skipif(platform.system() != "Darwin", reason="Only for MacOS")
+def test_xcodedeps_cppinfo_requires():
+
+    """
+    lib_a: has four components cmp1, cmp2, cmp3, cmp4
+    lib_b --> uses libA cmp1 so cpp_info.requires = ["lib_a::cmp1"]
+    lib_c --> uses libA cmp2 so cpp_info.requires = ["lib_a::cmp2"]
+    consumer --> libB, libC
+    """
+    client = TestClient()
+    lib_a = textwrap.dedent("""
+        from conan import ConanFile
+        class lib_aConan(ConanFile):
+            name = "lib_a"
+            version = "1.0"
+            settings = "os", "compiler", "build_type", "arch"
+            def package_info(self):
+                self.cpp_info.components["cmp1"].includedirs = ["include"]
+                self.cpp_info.components["cmp2"].includedirs = ["include"]
+                self.cpp_info.components["cmp3"].includedirs = ["include"]
+                self.cpp_info.components["cmp4"].includedirs = ["include"]
+        """)
+
+    lib = textwrap.dedent("""
+        from conan import ConanFile
+        class lib_{name}Conan(ConanFile):
+            name = "lib_{name}"
+            version = "1.0"
+            settings = "os", "compiler", "build_type", "arch"
+            def requirements(self):
+                self.requires("lib_a/1.0")
+            def package_info(self):
+                self.cpp_info.requires = {cppinfo_comps}
+        """)
+
+    consumer = textwrap.dedent("""
+    from conan import ConanFile
+    class ConsumerConan(ConanFile):
+        name = "consumer"
+        version = "1.0"
+        settings = "os", "compiler", "build_type", "arch"
+        generators = "XcodeDeps"
+        def requirements(self):
+            self.requires("lib_b/1.0")
+            self.requires("lib_c/1.0")
+    """)
+
+    client.save({
+        'lib_a/conanfile.py': lib_a,
+        'lib_b/conanfile.py': lib.format(name="b", cppinfo_comps='["lib_a::cmp1"]'),
+        'lib_c/conanfile.py': lib.format(name="c", cppinfo_comps='["lib_a::cmp2"]'),
+        'consumer/conanfile.py': consumer,
+    })
+
+    client.run("create lib_a")
+
+    client.run("create lib_b")
+
+    client.run("create lib_c")
+
+    client.run("install consumer")
+
+    """
+    Check that the generated lib_b and lib_c xcconfig only use the cmp1 and cmp2 components
+    So we will only link against the components specified in the cpp_info.requires of lib_b and lib_c
+    """
+
+    lib_b = client.load("conan_lib_b_lib_b.xcconfig")
+
+    # check that nothing from other components than the specified in the cpp_info.requires
+    # from lib_b and lib_c exist in the xcconfig that adds the includes from components
+    assert "cmp1" in lib_b
+    assert "cmp2" not in lib_b
+    assert "cmp3" not in lib_b
+    assert "cmp4" not in lib_b
+
+    lib_c = client.load("conan_lib_c_lib_c.xcconfig")
+
+    assert "cmp1" not in lib_c
+    assert "cmp2" in lib_c
+    assert "cmp3" not in lib_c
+    assert "cmp4" not in lib_c
diff --git a/conans/test/integration/toolchains/gnu/test_basic_layout.py b/conans/test/integration/toolchains/gnu/test_basic_layout.py
new file mode 100644
index 000000000..19cd9ebb6
--- /dev/null
+++ b/conans/test/integration/toolchains/gnu/test_basic_layout.py
@@ -0,0 +1,25 @@
+import os
+import platform
+import textwrap
+
+from conans.test.utils.tools import TestClient
+
+
+def test_basic_layout_subproject():
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.layout import basic_layout
+        class Pkg(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            generators = "AutotoolsToolchain"
+            def layout(self):
+                self.folders.root = ".."
+                self.folders.subproject = "pkg"
+                basic_layout(self)
+        """)
+    c.save({"pkg/conanfile.py": conanfile})
+    c.run("install pkg")
+    ext = "sh" if platform.system() != "Windows" else "bat"
+    assert os.path.isfile(os.path.join(c.current_folder, "pkg", "build-release", "conan",
+                                       "conanautotoolstoolchain.{}".format(ext)))
diff --git a/conans/test/integration/toolchains/microsoft/test_vs_layout.py b/conans/test/integration/toolchains/microsoft/test_vs_layout.py
new file mode 100644
index 000000000..5a446da73
--- /dev/null
+++ b/conans/test/integration/toolchains/microsoft/test_vs_layout.py
@@ -0,0 +1,22 @@
+import os
+import textwrap
+
+from conans.test.utils.tools import TestClient
+
+
+def test_vs_layout_subproject():
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.microsoft import vs_layout
+        class Pkg(ConanFile):
+            settings = "os", "compiler", "build_type", "arch"
+            generators = "MSBuildToolchain"
+            def layout(self):
+                self.folders.root = ".."
+                self.folders.subproject = "pkg"
+                vs_layout(self)
+        """)
+    c.save({"pkg/conanfile.py": conanfile})
+    c.run("install pkg")
+    assert os.path.isfile(os.path.join(c.current_folder, "pkg", "conan", "conantoolchain.props"))