Repository: conan-io/conan
Pull request #16494 (2024-06-18 21:31:07)

android ndk_version
Changelog: Feature: Add ``os.ndk_version`` for ``Android``
Docs: https://github.com/conan-io/docs/pull/XXXX

Close https://github.com/conan-io/conan/issues/16453

I am still not super convinced about the approach, or if we should use closed versions


Patch:
diff --git a/README.md b/README.md
index 8c76da2cd..7c298f615 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ OK
 To run specific tests, you can specify the test name too, something like:
 
 ```bash
-$ python -m pytest conans/test/functional/command/export_test.py::TestRevisionModeSCM::test_revision_mode_scm -s
+$ python -m pytest test/functional/command/export_test.py::TestRevisionModeSCM::test_revision_mode_scm -s
 ```
 
 The `-s` argument can be useful to see some output that otherwise is captured by *pytest*.
diff --git a/conan/cli/printers/graph.py b/conan/cli/printers/graph.py
index 404ca7c97..5790f8e84 100644
--- a/conan/cli/printers/graph.py
+++ b/conan/cli/printers/graph.py
@@ -1,5 +1,5 @@
 from conan.api.output import ConanOutput, Color, LEVEL_VERBOSE
-from conans.client.graph.graph import RECIPE_CONSUMER, RECIPE_VIRTUAL, CONTEXT_BUILD, BINARY_SKIP,\
+from conans.client.graph.graph import BINARY_INVALID, BINARY_MISSING, RECIPE_CONSUMER, RECIPE_VIRTUAL, CONTEXT_BUILD, BINARY_SKIP,\
     BINARY_PLATFORM
 
 
@@ -122,13 +122,18 @@ def print_graph_packages(graph):
         output.info(title, Color.BRIGHT_YELLOW)
         for pref, (status, remote) in sorted(reqs_to_print.items(), key=repr):
             name = pref.repr_notime() if status != BINARY_PLATFORM else str(pref.ref)
-            msg = f"{tab}{name} - {status}"
-            if remote is not None and status != BINARY_SKIP:
-                msg += f" ({remote.name})"
+            msg = f"{tab}{name} - "
             if status == BINARY_SKIP:
                 skipped_requires.append(str(pref.ref))
-                output.verbose(msg, Color.BRIGHT_CYAN)
+                output.verbose(f"{msg}{status}", Color.BRIGHT_CYAN)
+            elif status == BINARY_MISSING or status == BINARY_INVALID:
+                output.write(msg, Color.BRIGHT_CYAN)
+                output.writeln(status, Color.BRIGHT_RED)
             else:
+                # Support python36
+                msg += status
+                if remote:
+                    msg += f" ({remote.name})"
                 output.info(msg, Color.BRIGHT_CYAN)
 
     _format_requires("Requirements", requires)
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index ae023a009..5d1f34051 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -23,11 +23,12 @@ from conans.model.version import Version
 from conans.util.files import load
 
 
-class Block(object):
-    def __init__(self, conanfile, toolchain):
+class Block:
+    def __init__(self, conanfile, toolchain, name):
         self._conanfile = conanfile
         self._toolchain = toolchain
         self._context_values = None
+        self._name = name
 
     @property
     def values(self):
@@ -44,7 +45,8 @@ class Block(object):
         if context is None:
             return
 
-        template = Template(self.template, trim_blocks=True, lstrip_blocks=True)
+        template = f"########## '{self._name}' block #############\n" + self.template + "\n\n"
+        template = Template(template, trim_blocks=True, lstrip_blocks=True)
         return template.render(**context)
 
     def context(self):
@@ -56,8 +58,10 @@ class Block(object):
 
 
 class VSRuntimeBlock(Block):
-    template = textwrap.dedent("""
-        # Definition of VS runtime, defined from build_type, compiler.runtime, compiler.runtime_type
+    template = textwrap.dedent("""\
+        # Definition of VS runtime CMAKE_MSVC_RUNTIME_LIBRARY, from settings build_type,
+        # compiler.runtime, compiler.runtime_type
+
         {% set genexpr = namespace(str='') %}
         {% for config, value in vs_runtimes.items() %}
             {% set genexpr.str = genexpr.str +
@@ -67,6 +71,7 @@ class VSRuntimeBlock(Block):
         if(NOT "${POLICY_CMP0091}" STREQUAL NEW)
             message(FATAL_ERROR "The CMake policy CMP0091 must be NEW, but is '${POLICY_CMP0091}'")
         endif()
+        message(STATUS "Conan toolchain: Setting CMAKE_MSVC_RUNTIME_LIBRARY={{ genexpr.str  }}")
         set(CMAKE_MSVC_RUNTIME_LIBRARY "{{ genexpr.str }}")
         """)
 
@@ -116,9 +121,11 @@ class VSRuntimeBlock(Block):
 
 
 class VSDebuggerEnvironment(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Definition of CMAKE_VS_DEBUGGER_ENVIRONMENT from "bindirs" folders of dependencies
+        # for execution of applications with shared libraries within the VS IDE
+
         {% if vs_debugger_path %}
-        # Definition of CMAKE_VS_DEBUGGER_ENVIRONMENT
         set(CMAKE_VS_DEBUGGER_ENVIRONMENT "{{ vs_debugger_path }}")
         {% endif %}
         """)
@@ -166,7 +173,9 @@ class VSDebuggerEnvironment(Block):
 
 
 class FPicBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Defining CMAKE_POSITION_INDEPENDENT_CODE for static libraries when necessary
+
         {% if fpic %}
         message(STATUS "Conan toolchain: Setting CMAKE_POSITION_INDEPENDENT_CODE={{ fpic }} (options.fPIC)")
         set(CMAKE_POSITION_INDEPENDENT_CODE {{ fpic }} CACHE BOOL "Position independent code")
@@ -185,11 +194,16 @@ class FPicBlock(Block):
 
 
 class GLibCXXBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Definition of libcxx from 'compiler.libcxx' setting, defining the
+        # right CXX_FLAGS for that libcxx
+
         {% if set_libcxx %}
+        message(STATUS "Conan toolchain: Defining libcxx as C++ flags: {{ set_libcxx }}")
         string(APPEND CONAN_CXX_FLAGS " {{ set_libcxx }}")
         {% endif %}
         {% if glibcxx %}
+        message(STATUS "Conan toolchain: Adding glibcxx compile definition: {{ glibcxx }}")
         add_compile_definitions({{ glibcxx }})
         {% endif %}
         """)
@@ -200,7 +214,9 @@ class GLibCXXBlock(Block):
 
 
 class SkipRPath(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Defining CMAKE_SKIP_RPATH
+
         {% if skip_rpath %}
         set(CMAKE_SKIP_RPATH 1 CACHE BOOL "rpaths" FORCE)
         # Policy CMP0068
@@ -216,7 +232,10 @@ class SkipRPath(Block):
 
 
 class ArchitectureBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Define C++ flags, C flags and linker flags from 'settings.arch'
+
+        message(STATUS "Conan toolchain: Defining architecture flag: {{ arch_flag }}")
         string(APPEND CONAN_CXX_FLAGS " {{ arch_flag }}")
         string(APPEND CONAN_C_FLAGS " {{ arch_flag }}")
         string(APPEND CONAN_SHARED_LINKER_FLAGS " {{ arch_flag }}")
@@ -231,7 +250,10 @@ class ArchitectureBlock(Block):
 
 
 class LinkerScriptsBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Add linker flags from tools.build:linker_scripts conf
+
+        message(STATUS "Conan toolchain: Defining linker script flag: {{ linker_script_flags }}")
         string(APPEND CONAN_EXE_LINKER_FLAGS {{ linker_script_flags }})
         """)
 
@@ -248,7 +270,9 @@ class LinkerScriptsBlock(Block):
 
 
 class CppStdBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Define the C++ and C standards from 'compiler.cppstd' and 'compiler.cstd'
+
         {% if cppstd %}
         message(STATUS "Conan toolchain: C++ Standard {{ cppstd }} with extensions {{ cppstd_extensions }}")
         set(CMAKE_CXX_STANDARD {{ cppstd }})
@@ -285,7 +309,9 @@ class CppStdBlock(Block):
 
 
 class SharedLibBock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Define BUILD_SHARED_LIBS for shared libraries
+
         message(STATUS "Conan toolchain: Setting BUILD_SHARED_LIBS = {{ shared_libs }}")
         set(BUILD_SHARED_LIBS {{ shared_libs }} CACHE BOOL "Build shared libraries")
         """)
@@ -299,7 +325,9 @@ class SharedLibBock(Block):
 
 
 class ParallelBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Define VS paralell build /MP flags
+
         string(APPEND CONAN_CXX_FLAGS " /MP{{ parallel }}")
         string(APPEND CONAN_C_FLAGS " /MP{{ parallel }}")
         """)
@@ -318,12 +346,18 @@ class ParallelBlock(Block):
 
 class AndroidSystemBlock(Block):
 
-    template = textwrap.dedent("""
-        # New toolchain things
+    template = textwrap.dedent("""\
+        # Define Android variables ANDROID_PLATFORM, ANDROID_STL, ANDROID_ABI, etc
+        # and include(.../android.toolchain.cmake) from NDK toolchain file
+
+        # New Android toolchain definitions
+        message(STATUS "Conan toolchain: Setting Android platform: {{ android_platform }}")
         set(ANDROID_PLATFORM {{ android_platform }})
         {% if android_stl %}
+        message(STATUS "Conan toolchain: Setting Android stl: {{ android_stl }}")
         set(ANDROID_STL {{ android_stl }})
         {% endif %}
+        message(STATUS "Conan toolchain: Setting Android abi: {{ android_abi }}")
         set(ANDROID_ABI {{ android_abi }})
         {% if android_use_legacy_toolchain_file %}
         set(ANDROID_USE_LEGACY_TOOLCHAIN_FILE {{ android_use_legacy_toolchain_file }})
@@ -363,7 +397,9 @@ class AndroidSystemBlock(Block):
 
 
 class AppleSystemBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Define Apple architectures, sysroot, deployment target, bitcode, etc
+
         # Set the architectures for which to build.
         set(CMAKE_OSX_ARCHITECTURES {{ cmake_osx_architectures }} CACHE STRING "" FORCE)
         # Setting CMAKE_OSX_SYSROOT SDK, when using Xcode generator the name is enough
@@ -469,7 +505,9 @@ class AppleSystemBlock(Block):
 
 
 class FindFiles(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Define paths to find packages, programs, libraries, etc.
+
         {% if find_package_prefer_config %}
         set(CMAKE_FIND_PACKAGE_PREFER_CONFIG {{ find_package_prefer_config }})
         {% endif %}
@@ -618,7 +656,9 @@ class FindFiles(Block):
 
 
 class PkgConfigBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Define pkg-config from 'tools.gnu:pkg_config' executable and paths
+
         {% if pkg_config %}
         set(PKG_CONFIG_EXECUTABLE {{ pkg_config }} CACHE FILEPATH "pkg-config executable")
         {% endif %}
@@ -643,7 +683,9 @@ class PkgConfigBlock(Block):
 
 
 class UserToolchain(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Include one or more CMake user toolchain from tools.cmake.cmaketoolchain:user_toolchain
+
         {% for user_toolchain in paths %}
         include("{{user_toolchain}}")
         {% endfor %}
@@ -662,7 +704,10 @@ class UserToolchain(Block):
 class ExtraFlagsBlock(Block):
     """This block is adding flags directly from user [conf] section"""
 
-    _template = textwrap.dedent("""
+    _template = textwrap.dedent("""\
+        # Include extra C++, C and linker flags from configuration tools.build:<type>flags
+        # and from CMakeToolchain.extra_<type>_flags
+
         # Conan conf flags start: {{config}}
         {% if cxxflags %}
         string(APPEND CONAN_CXX_FLAGS{{suffix}} "{% for cxxflag in cxxflags %} {{ cxxflag }}{% endfor %}")
@@ -753,7 +798,9 @@ class ExtraFlagsBlock(Block):
 
 
 class CMakeFlagsInitBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Define CMAKE_<XXX>_FLAGS from CONAN_<XXX>_FLAGS
+
         foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES)
             string(TOUPPER ${config} config)
             if(DEFINED CONAN_CXX_FLAGS_${config})
@@ -782,12 +829,13 @@ class CMakeFlagsInitBlock(Block):
         if(DEFINED CONAN_EXE_LINKER_FLAGS)
           string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " ${CONAN_EXE_LINKER_FLAGS}")
         endif()
-
         """)
 
 
 class TryCompileBlock(Block):
-    template = textwrap.dedent("""
+    template = textwrap.dedent("""\
+        # Blocks after this one will not be added when running CMake try/checks
+
         get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE )
         if(_CMAKE_IN_TRY_COMPILE)
             message(STATUS "Running toolchain IN_TRY_COMPILE")
@@ -821,14 +869,12 @@ class CompilersBlock(Block):
 
 
 class GenericSystemBlock(Block):
-    template = textwrap.dedent("""
-        ########## generic_system block #############
+    template = textwrap.dedent("""\
         # Definition of system, platform and toolset
-        #############################################
+
         {% if cmake_sysroot %}
         set(CMAKE_SYSROOT {{ cmake_sysroot }})
         {% endif %}
-
         {% if cmake_system_name %}
         # Cross building
         set(CMAKE_SYSTEM_NAME {{ cmake_system_name }})
@@ -947,7 +993,8 @@ class GenericSystemBlock(Block):
         return os_host in ('iOS', 'watchOS', 'tvOS', 'visionOS') or (
                 os_host == 'Macos' and (arch_host != arch_build or os_build != os_host))
 
-    def _get_darwin_version(self, os_name, os_version):
+    @staticmethod
+    def _get_darwin_version(os_name, os_version):
         # version mapping from https://en.wikipedia.org/wiki/Darwin_(operating_system)
         version_mapping = {
             "Macos": {
@@ -1063,8 +1110,11 @@ class GenericSystemBlock(Block):
                 "winsdk_version": winsdk_version,
                 "gen_platform_sdk_version": gen_platform_sdk_version}
 
+
 class ExtraVariablesBlock(Block):
-    template = textwrap.dedent(r"""
+    template = textwrap.dedent("""\
+        # Definition of extra CMake variables from tools.cmake.cmaketoolchain:extra_variables
+
         {% if extra_variables %}
         {% for key, value in extra_variables.items() %}
         set({{ key }} {{ value }})
@@ -1072,7 +1122,7 @@ class ExtraVariablesBlock(Block):
         {% endif %}
     """)
 
-    CMAKE_CACHE_TYPES = ["BOOL","FILEPATH", "PATH", "STRING", "INTERNAL"]
+    CMAKE_CACHE_TYPES = ["BOOL", "FILEPATH", "PATH", "STRING", "INTERNAL"]
 
     def get_exact_type(self, key, value):
         if isinstance(value, str):
@@ -1096,27 +1146,30 @@ class ExtraVariablesBlock(Block):
                     raise ConanException(f'tools.cmake.cmaketoolchain:extra_variables "{key}" invalid type "{var_type}" for cache variable. Possible types: {", ".join(self.CMAKE_CACHE_TYPES)}')
                 # Set docstring as variable name if not defined
                 docstring = value.get("docstring") or key
-                force_str = " FORCE" if is_force else "" # Support python < 3.11
+                force_str = " FORCE" if is_force else ""  # Support python < 3.11
                 return f"{var_value} CACHE {var_type} \"{docstring}\"{force_str}"
             else:
                 if is_force:
                     raise ConanException(f'tools.cmake.cmaketoolchain:extra_variables "{key}" "force" is only allowed for cache variables')
                 return var_value
 
-
     def context(self):
         # Reading configuration from "tools.cmake.cmaketoolchain:extra_variables"
-        extra_variables = self._conanfile.conf.get("tools.cmake.cmaketoolchain:extra_variables", default={}, check_type=dict)
+        extra_variables = self._conanfile.conf.get("tools.cmake.cmaketoolchain:extra_variables",
+                                                   default={}, check_type=dict)
         parsed_extra_variables = {}
         for key, value in extra_variables.items():
             parsed_extra_variables[key] = self.get_exact_type(key, value)
         return {"extra_variables": parsed_extra_variables}
 
+
 class OutputDirsBlock(Block):
 
     @property
     def template(self):
-        return textwrap.dedent("""
+        return textwrap.dedent("""\
+           # Definition of CMAKE_INSTALL_XXX folders
+
            {% if package_folder %}
            set(CMAKE_INSTALL_PREFIX "{{package_folder}}")
            {% endif %}
@@ -1152,6 +1205,81 @@ class OutputDirsBlock(Block):
                 "default_res": self._get_cpp_info_value("resdirs")}
 
 
+class VariablesBlock(Block):
+    @property
+    def template(self):
+        return textwrap.dedent("""\
+            # Definition of CMake variables from CMakeToolchain.variables values
+
+            {% macro iterate_configs(var_config, action) %}
+            {% for it, values in var_config.items() %}
+                {% set genexpr = namespace(str='') %}
+                {% for conf, value in values -%}
+                set(CONAN_DEF_{{ conf }}{{ it }} "{{ value }}")
+                {% endfor %}
+                {% for conf, value in values -%}
+                    {% set genexpr.str = genexpr.str +
+                                          '$<IF:$<CONFIG:' + conf + '>,${CONAN_DEF_' + conf|string + it|string + '},' %}
+                    {% if loop.last %}{% set genexpr.str = genexpr.str + '""' -%}{%- endif -%}
+                {% endfor %}
+                {% for i in range(values|count) %}{% set genexpr.str = genexpr.str + '>' %}
+                {% endfor %}
+                set({{ it }} {{ genexpr.str }} CACHE STRING
+                    "Variable {{ it }} conan-toolchain defined")
+            {% endfor %}
+            {% endmacro %}
+            # Variables
+            {% for it, value in variables.items() %}
+            {% if value is boolean %}
+            set({{ it }} {{ "ON" if value else "OFF"}} CACHE BOOL "Variable {{ it }} conan-toolchain defined")
+            {% else %}
+            set({{ it }} "{{ value }}" CACHE STRING "Variable {{ it }} conan-toolchain defined")
+            {% endif %}
+            {% endfor %}
+            # Variables  per configuration
+            {{ iterate_configs(variables_config, action='set') }}
+            """)
+
+    def context(self):
+        return {"variables": self._toolchain.variables,
+                "variables_config": self._toolchain.variables.configuration_types}
+
+
+class PreprocessorBlock(Block):
+    @property
+    def template(self):
+        return textwrap.dedent("""\
+        # Preprocessor definitions from CMakeToolchain.preprocessor_definitions values
+
+        {% for it, value in preprocessor_definitions.items() %}
+        {% if value is none %}
+        add_compile_definitions("{{ it }}")
+        {% else %}
+        add_compile_definitions("{{ it }}={{ value }}")
+        {% endif %}
+        {% endfor %}
+        # Preprocessor definitions per configuration
+        {% for name, values in preprocessor_definitions_config.items() %}
+        {%- for (conf, value) in values %}
+        {% if value is none %}
+        set(CONAN_DEF_{{conf}}_{{name}} "{{name}}")
+        {% else %}
+        set(CONAN_DEF_{{conf}}_{{name}} "{{name}}={{value}}")
+        {% endif %}
+        {% endfor %}
+        add_compile_definitions(
+        {%- for (conf, value) in values %}
+        $<$<CONFIG:{{conf}}>:${CONAN_DEF_{{conf}}_{{name}}}>
+        {%- endfor -%})
+        {% endfor %}
+        """)
+
+    def context(self):
+        return {"preprocessor_definitions": self._toolchain.preprocessor_definitions,
+                "preprocessor_definitions_config":
+                    self._toolchain.preprocessor_definitions.configuration_types}
+
+
 class ToolchainBlocks:
     def __init__(self, conanfile, toolchain, items=None):
         self._blocks = OrderedDict()
@@ -1159,7 +1287,7 @@ class ToolchainBlocks:
         self._toolchain = toolchain
         if items:
             for name, block in items:
-                self._blocks[name] = block(conanfile, toolchain)
+                self._blocks[name] = block(conanfile, toolchain, name)
 
     def keys(self):
         return self._blocks.keys()
@@ -1173,6 +1301,16 @@ class ToolchainBlocks:
             del self._blocks[arg]
 
     def select(self, name, *args):
+        """
+        keep the blocks provided as arguments, remove the others, except pre-existing "variables"
+        and "preprocessor", to not break behavior
+        """
+        self._conanfile.output.warning("CMakeToolchain.select is deprecated. Use blocks.enabled()"
+                                       " instead", warn_tag="deprecated")
+        to_keep = [name] + list(args) + ["variables", "preprocessor"]
+        self._blocks = OrderedDict((k, v) for k, v in self._blocks.items() if k in to_keep)
+
+    def enabled(self, name, *args):
         """
         keep the blocks provided as arguments, remove the others
         """
@@ -1182,7 +1320,7 @@ class ToolchainBlocks:
     def __setitem__(self, name, block_type):
         # Create a new class inheriting Block with the elements of the provided one
         block_type = type('proxyUserBlock', (Block,), dict(block_type.__dict__))
-        self._blocks[name] = block_type(self._conanfile, self._toolchain)
+        self._blocks[name] = block_type(self._conanfile, self._toolchain, name)
 
     def __getitem__(self, name):
         return self._blocks[name]
diff --git a/conan/tools/cmake/toolchain/toolchain.py b/conan/tools/cmake/toolchain/toolchain.py
index 0692751eb..6d834ec9b 100644
--- a/conan/tools/cmake/toolchain/toolchain.py
+++ b/conan/tools/cmake/toolchain/toolchain.py
@@ -9,11 +9,12 @@ from conan.internal import check_duplicated_generator
 from conan.tools.build import use_win_mingw
 from conan.tools.cmake.presets import write_cmake_presets
 from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
-from conan.tools.cmake.toolchain.blocks import ExtraVariablesBlock, ToolchainBlocks, UserToolchain, GenericSystemBlock, \
+from conan.tools.cmake.toolchain.blocks import ExtraVariablesBlock, ToolchainBlocks, UserToolchain, \
+    GenericSystemBlock, \
     AndroidSystemBlock, AppleSystemBlock, FPicBlock, ArchitectureBlock, GLibCXXBlock, VSRuntimeBlock, \
     CppStdBlock, ParallelBlock, CMakeFlagsInitBlock, TryCompileBlock, FindFiles, PkgConfigBlock, \
     SkipRPath, SharedLibBock, OutputDirsBlock, ExtraFlagsBlock, CompilersBlock, LinkerScriptsBlock, \
-    VSDebuggerEnvironment
+    VSDebuggerEnvironment, VariablesBlock, PreprocessorBlock
 from conan.tools.cmake.utils import is_multi_configuration
 from conan.tools.env import VirtualBuildEnv, VirtualRunEnv
 from conan.tools.intel import IntelCC
@@ -56,39 +57,18 @@ class Variables(OrderedDict):
                     data[key] = str(var).replace('"', '\\"')
 
 
-class CMakeToolchain(object):
+class CMakeToolchain:
 
     filename = CONAN_TOOLCHAIN_FILENAME
 
-    # TODO: Clean this macro, do it explicitly for variables
-    _template = textwrap.dedent("""
-        {% macro iterate_configs(var_config, action) %}
-            {% for it, values in var_config.items() %}
-                {% set genexpr = namespace(str='') %}
-                {% for conf, value in values -%}
-                set(CONAN_DEF_{{ conf }}{{ it }} "{{ value }}")
-                {% endfor %}
-                {% for conf, value in values -%}
-                    {% set genexpr.str = genexpr.str +
-                                          '$<IF:$<CONFIG:' + conf + '>,${CONAN_DEF_' + conf|string + it|string + '},' %}
-                    {% if loop.last %}{% set genexpr.str = genexpr.str + '""' -%}{%- endif -%}
-                {% endfor %}
-                {% for i in range(values|count) %}{% set genexpr.str = genexpr.str + '>' %}
-                {% endfor %}
-                set({{ it }} {{ genexpr.str }} CACHE STRING
-                    "Variable {{ it }} conan-toolchain defined")
-            {% endfor %}
-        {% endmacro %}
-
+    _template = textwrap.dedent("""\
         # Conan automatically generated toolchain file
         # DO NOT EDIT MANUALLY, it will be overwritten
 
         # Avoid including toolchain file several times (bad if appending to variables like
         #   CMAKE_CXX_FLAGS. See https://github.com/android/ndk/issues/323
         include_guard()
-
         message(STATUS "Using Conan toolchain: ${CMAKE_CURRENT_LIST_FILE}")
-
         if(${CMAKE_VERSION} VERSION_LESS "3.15")
             message(FATAL_ERROR "The 'CMakeToolchain' generator only works with CMake >= 3.15")
         endif()
@@ -97,41 +77,6 @@ class CMakeToolchain(object):
         {{ conan_block }}
         {% endfor %}
 
-        # Variables
-        {% for it, value in variables.items() %}
-        {% if value is boolean %}
-        set({{ it }} {{ "ON" if value else "OFF"}} CACHE BOOL "Variable {{ it }} conan-toolchain defined")
-        {% else %}
-        set({{ it }} "{{ value }}" CACHE STRING "Variable {{ it }} conan-toolchain defined")
-        {% endif %}
-        {% endfor %}
-        # Variables  per configuration
-        {{ iterate_configs(variables_config, action='set') }}
-
-        # Preprocessor definitions
-        {% for it, value in preprocessor_definitions.items() %}
-        {% if value is none %}
-        add_compile_definitions("{{ it }}")
-        {% else %}
-        add_compile_definitions("{{ it }}={{ value }}")
-        {% endif %}
-        {% endfor %}
-        # Preprocessor definitions per configuration
-        {% for name, values in preprocessor_definitions_config.items() %}
-        {%- for (conf, value) in values %}
-        {% if value is none %}
-        set(CONAN_DEF_{{conf}}_{{name}} "{{name}}")
-        {% else %}
-        set(CONAN_DEF_{{conf}}_{{name}} "{{name}}={{value}}")
-        {% endif %}
-        {% endfor %}
-        add_compile_definitions(
-        {%- for (conf, value) in values %}
-        $<$<CONFIG:{{conf}}>:${CONAN_DEF_{{conf}}_{{name}}}>
-        {%- endfor -%})
-        {% endfor %}
-
-
         if(CMAKE_POLICY_DEFAULT_CMP0091)  # Avoid unused and not-initialized warnings
         endif()
         """)
@@ -171,7 +116,9 @@ class CMakeToolchain(object):
                                        ("pkg_config", PkgConfigBlock),
                                        ("rpath", SkipRPath),
                                        ("shared", SharedLibBock),
-                                       ("output_dirs", OutputDirsBlock)])
+                                       ("output_dirs", OutputDirsBlock),
+                                       ("variables", VariablesBlock),
+                                       ("preprocessor", PreprocessorBlock)])
 
         # Set the CMAKE_MODULE_PATH and CMAKE_PREFIX_PATH to the deps .builddirs
         self.find_builddirs = True
@@ -188,10 +135,7 @@ class CMakeToolchain(object):
 
         blocks = self.blocks.process_blocks()
         ctxt_toolchain = {
-            "variables": self.variables,
-            "variables_config": self.variables.configuration_types,
-            "preprocessor_definitions": self.preprocessor_definitions,
-            "preprocessor_definitions_config": self.preprocessor_definitions.configuration_types,
+
             "conan_blocks": blocks
         }
 
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index 0ac9b8a2d..33c9db521 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -62,6 +62,7 @@ os:
                 ios_version: *ios_version
     Android:
         api_level: [ANY]
+        ndk_version: [null, ANY]
     FreeBSD:
     SunOS:
     AIX:
diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py
index c532e6b69..ad4808177 100644
--- a/conans/client/profile_loader.py
+++ b/conans/client/profile_loader.py
@@ -41,6 +41,7 @@ def profile_plugin(profile):
     _check_correct_cppstd(settings)
     _check_correct_cstd(settings)
 
+
 def _check_correct_cppstd(settings):
     from conan.tools.scm import Version
     def _error(compiler, cppstd, min_version, version):


Test patch:
diff --git a/pytest.ini b/pytest.ini
index ceb01dcd0..a69be4e90 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,3 +1,3 @@
 [pytest]
 norecursedirs = '.*', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv', 'assets'
-testpaths = 'conans/test'
+testpaths = 'test'
diff --git a/test/README.md b/test/README.md
index 574c30349..22809fc9f 100644
--- a/test/README.md
+++ b/test/README.md
@@ -3,18 +3,18 @@
 
 Conan tests fall into three categories:
 
-- **Unit tests** in `conans/test/unittests` folder. These tests should test small pieces of code like
+- **Unit tests** in `test/unittests` folder. These tests should test small pieces of code like
   functions, methods, or properties. As long as it's possible they should not rely on anything
   external like the file system or system configuration,  and in case they need to do it should be
   mocked.
 
-- **Integration tests** in `conans/test/integration` folder. We consider integration tests the ones that
+- **Integration tests** in `test/integration` folder. We consider integration tests the ones that
   only will need pure python to execute, but that may test the interaction between different Conan
   modules. They could test the result of the execution of one or several Conan commands, but shouldn't
   depend on any external tools like compilers, build systems, or version-control system
   tools.
 
-- **Functional tests** in `conans/test/functional` folder. Under this category, we add tests that are
+- **Functional tests** in `test/functional` folder. Under this category, we add tests that are
   testing the complete Conan functionality. They may call external tools (please read the section
   below to check the tools installed on the CI). These tests should be avoided as long as
   it's possible as they may take considerable CI time.
@@ -70,7 +70,7 @@ def test_vcvars_priority(self):
     ...
 ```
 
-If the test needs any of those tools to run it should be marked as using that tool and moved to the `conans/test/functional` folder.
+If the test needs any of those tools to run it should be marked as using that tool and moved to the `test/functional` folder.
 
 ### Parametrizing tests
 
diff --git a/test/integration/configuration/test_profile_plugin.py b/test/integration/configuration/test_profile_plugin.py
index a88eb0de7..10e42c159 100644
--- a/test/integration/configuration/test_profile_plugin.py
+++ b/test/integration/configuration/test_profile_plugin.py
@@ -26,3 +26,13 @@ class TestErrorsProfilePlugin:
         os.remove(os.path.join(c.cache.plugins_path, "profile.py"))
         c.run("profile show", assert_error=True)
         assert "ERROR: The 'profile.py' plugin file doesn't exist" in c.out
+
+
+def test_android_ndk_version():
+    c = TestClient()
+    c.run("profile show -s os=Android")
+    assert "os.ndk_version" not in c.out
+    c.run("profile show -s os=Android -s os.ndk_version=r26")
+    assert "os.ndk_version=r26" in c.out
+    c.run("profile show -s os=Android -s os.ndk_version=r26a")
+    assert "os.ndk_version=r26a" in c.out
diff --git a/test/unittests/tools/cmake/test_cmaketoolchain.py b/test/unittests/tools/cmake/test_cmaketoolchain.py
index 3d66adada..8ba5ff7f0 100644
--- a/test/unittests/tools/cmake/test_cmaketoolchain.py
+++ b/test/unittests/tools/cmake/test_cmaketoolchain.py
@@ -58,10 +58,16 @@ def test_remove(conanfile):
     assert "_CMAKE_IN_TRY_COMPILE" in content
 
 
-def test_filter(conanfile):
+def test_select_blocks(conanfile):
     toolchain = CMakeToolchain(conanfile)
     toolchain.blocks.select("generic_system")
     content = toolchain.content
+    assert "########## 'generic_system' block #############" in content
+    assert "########## 'cmake_flags_init' block #############" not in content
+    assert "########## 'libcxx' block #############" not in content
+    # These are not removed by default, to not break behavior
+    assert "########## 'variables' block #############" in content
+    assert "########## 'preprocessor' block #############" in content
     assert 'CMAKE_SYSTEM_NAME' in content
     assert "CMAKE_CXX_FLAGS_INIT" not in content
     assert "_CMAKE_IN_TRY_COMPILE" not in content
@@ -70,10 +76,24 @@ def test_filter(conanfile):
     toolchain = CMakeToolchain(conanfile)
     toolchain.blocks.select("generic_system", "cmake_flags_init")
     content = toolchain.content
+    assert "########## 'generic_system' block #############" in content
+    assert "########## 'cmake_flags_init' block #############" in content
+    assert "########## 'libcxx' block #############" not in content
+    # These are not removed by default, to not break behavior
+    assert "########## 'variables' block #############" in content
+    assert "########## 'preprocessor' block #############" in content
     assert 'CMAKE_SYSTEM_NAME' in content
     assert "CMAKE_CXX_FLAGS_INIT" in content
     assert "_CMAKE_IN_TRY_COMPILE" not in content
 
+    # remove multiple
+    toolchain = CMakeToolchain(conanfile)
+    toolchain.blocks.enabled("generic_system")
+    content = toolchain.content
+    assert "########## 'generic_system' block #############" in content
+    assert "########## 'variables' block #############" not in content
+    assert "########## 'preprocessor' block #############" not in content
+
 
 def test_dict_keys(conanfile):
     toolchain = CMakeToolchain(conanfile)