Repository: conan-io/conan
Pull request #17259 (2024-10-31 11:45:59)

avoid colorama crash on large error
Changelog: Fix: Avoid ``colorama`` bug crashing for large outputs
Docs: Omit

Close https://github.com/conan-io/conan/issues/17245


Doesn't include tests, too tricky to test this.


Patch:
diff --git a/conan/api/output.py b/conan/api/output.py
index d224498bd..a94e16cb5 100644
--- a/conan/api/output.py
+++ b/conan/api/output.py
@@ -4,6 +4,7 @@ import sys
 import time
 from threading import Lock
 
+import colorama
 from colorama import Fore, Style
 
 from conan.errors import ConanException
@@ -283,15 +284,15 @@ def cli_out_write(data, fg=None, bg=None, endline="\n", indentation=0):
     """
     Output to be used by formatters to dump information to stdout
     """
-
-    fg_ = fg or ''
-    bg_ = bg or ''
-    if (fg or bg) and _color_enabled(sys.stdout):
-        data = f"{' ' * indentation}{fg_}{bg_}{data}{Style.RESET_ALL}{endline}"
+    if (fg or bg) and _color_enabled(sys.stdout):  # need color
+        data = f"{' ' * indentation}{fg or ''}{bg or ''}{data}{Style.RESET_ALL}{endline}"
+        sys.stdout.write(data)
     else:
         data = f"{' ' * indentation}{data}{endline}"
-
-    sys.stdout.write(data)
+        # https://github.com/conan-io/conan/issues/17245 avoid colorama crash and overhead
+        colorama.deinit()
+        sys.stdout.write(data)
+        colorama.reinit()
 
 
 class TimedOutput:
diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py
index 743d38cbb..363861b8e 100644
--- a/conan/tools/meson/toolchain.py
+++ b/conan/tools/meson/toolchain.py
@@ -16,7 +16,7 @@ from conan.tools.microsoft import VCVars, msvc_runtime_flag
 from conans.util.files import save
 
 
-class MesonToolchain(object):
+class MesonToolchain:
     """
     MesonToolchain generator
     """
@@ -156,7 +156,7 @@ class MesonToolchain(object):
         self._conanfile = conanfile
         self._native = native
         self._is_apple_system = is_apple_os(self._conanfile)
-        is_cross_building = cross_building(conanfile, skip_x64_x86=True)
+        is_cross_building = cross_building(conanfile)  # x86_64->x86 is considered cross-building
         if not is_cross_building and native:
             raise ConanException("You can only pass native=True if you're cross-building, "
                                  "otherwise, it could cause unexpected results.")
@@ -164,8 +164,8 @@ class MesonToolchain(object):
         # Values are kept as Python built-ins so users can modify them more easily, and they are
         # only converted to Meson file syntax for rendering
         # priority: first user conf, then recipe, last one is default "ninja"
-        self._backend = conanfile.conf.get("tools.meson.mesontoolchain:backend",
-                                           default=backend or 'ninja')
+        self._backend = self._conanfile_conf.get("tools.meson.mesontoolchain:backend",
+                                                 default=backend or 'ninja')
         build_type = self._conanfile.settings.get_safe("build_type")
         self._buildtype = {"Debug": "debug",  # Note, it is not "'debug'"
                            "Release": "release",
@@ -424,13 +424,13 @@ class MesonToolchain(object):
         exelinkflags = self._conanfile_conf.get("tools.build:exelinkflags", default=[], check_type=list)
         linker_scripts = self._conanfile_conf.get("tools.build:linker_scripts", default=[], check_type=list)
         linker_script_flags = ['-T"' + linker_script + '"' for linker_script in linker_scripts]
-        defines = self._conanfile.conf.get("tools.build:defines", default=[], check_type=list)
+        defines = self._conanfile_conf.get("tools.build:defines", default=[], check_type=list)
         sys_root = [f"--sysroot={self._sys_root}"] if self._sys_root else [""]
+        ld = sharedlinkflags + exelinkflags + linker_script_flags + sys_root + self.extra_ldflags
         return {
             "cxxflags": cxxflags + sys_root + self.extra_cxxflags,
             "cflags": cflags + sys_root + self.extra_cflags,
-            "ldflags": sharedlinkflags + exelinkflags + linker_script_flags
-                       + sys_root + self.extra_ldflags,
+            "ldflags": ld,
             "defines": [f"-D{d}" for d in (defines + self.extra_defines)]
         }
 
@@ -447,11 +447,12 @@ class MesonToolchain(object):
     def _sanitize_env_format(value):
         if value is None or isinstance(value, list):
             return value
+        if not isinstance(value, str):
+            raise ConanException(f"MesonToolchain: Value '{value}' should be a string")
         ret = [x.strip() for x in value.split() if x]
         return ret[0] if len(ret) == 1 else ret
 
     def _context(self):
-
         apple_flags = self.apple_isysroot_flag + self.apple_arch_flag + self.apple_min_version_flag
         extra_flags = self._get_extra_flags()
 
@@ -474,10 +475,11 @@ class MesonToolchain(object):
 
         subproject_options = {}
         for subproject, listkeypair in self.subproject_options.items():
-            if listkeypair is not None and listkeypair is not []:
-                subproject_options[subproject] = []
-                for keypair in listkeypair:
-                    subproject_options[subproject].append({k: to_meson_value(v) for k, v in keypair.items()})
+            if listkeypair:
+                if not isinstance(listkeypair, list):
+                    raise ConanException("MesonToolchain.subproject_options must be a list of dicts")
+                subproject_options[subproject] = [{k: to_meson_value(v) for k, v in keypair.items()}
+                                                  for keypair in listkeypair]
 
         return {
             # https://mesonbuild.com/Machine-files.html#properties
@@ -555,6 +557,7 @@ class MesonToolchain(object):
         If Windows OS, it will be created a ``conanvcvars.bat`` as well.
         """
         check_duplicated_generator(self, self._conanfile)
+        self._conanfile.output.info(f"MesonToolchain generated: {self._filename}")
         save(self._filename, self._content)
         # FIXME: Should we check the OS and compiler to call VCVars?
         VCVars(self._conanfile).generate()
diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py
index 5a6eb644b..fabe6866a 100644
--- a/conan/tools/ros/rosenv.py
+++ b/conan/tools/ros/rosenv.py
@@ -8,22 +8,27 @@ from conan.tools.files import save
 class ROSEnv:
     """
     Generator to serve as integration for Robot Operating System 2 development workspaces.
-    It generates a conanrosenv.sh file that when sources sets variables so the Conan
-    dependencies are found by CMake and the run environment is also set.
 
     IMPORTANT: This generator should be used together with CMakeDeps and CMakeToolchain generators.
     """
 
     def __init__(self, conanfile):
+        """
+        :param conanfile: ``< ConanFile object >`` The current recipe object. Always use ``self``.
+        """
         self._conanfile = conanfile
         self.variables = {}
         self._build_script_file = "conanrosenv-build.sh"
         self._wrapper_script_file = "conanrosenv.sh"
 
     def generate(self):
+        """
+        Creates a ``conanrosenv.sh`` with the environment variables that are needed to build and
+        execute ROS packages with Conan dependencies.
+        """
         cmake_toolchain_path = os.path.join(self._conanfile.generators_folder,
                                             "conan_toolchain.cmake")
-        self.variables["CMAKE_TOOLCHAIN_FILE"] = f"\"{cmake_toolchain_path}\""
+        self.variables["CMAKE_TOOLCHAIN_FILE"] = cmake_toolchain_path
         build_type = self._conanfile.settings.get_safe("build_type")
         if build_type:
             self.variables["CMAKE_BUILD_TYPE"] = build_type


Test patch:
diff --git a/test/integration/toolchains/meson/test_mesontoolchain.py b/test/integration/toolchains/meson/test_mesontoolchain.py
index d9ca23ea9..a9075c667 100644
--- a/test/integration/toolchains/meson/test_mesontoolchain.py
+++ b/test/integration/toolchains/meson/test_mesontoolchain.py
@@ -7,7 +7,6 @@ import pytest
 
 from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
-from conan.tools.files import load
 from conan.tools.meson import MesonToolchain
 
 
@@ -540,8 +539,8 @@ def test_native_attribute():
                  "build": build,
                  "conanfile.py": conanfile})
     client.run("install . -pr:h host -pr:b build")
-    native_content = load(None, os.path.join(client.current_folder, MesonToolchain.native_filename))
-    cross_content = load(None, os.path.join(client.current_folder, MesonToolchain.cross_filename))
+    native_content = client.load(MesonToolchain.native_filename)
+    cross_content = client.load(MesonToolchain.cross_filename)
     expected_native = textwrap.dedent("""
     [binaries]
     c = 'clang'
@@ -640,9 +639,23 @@ def test_meson_sysroot_app():
                  "host": profile})
     client.run("install . -pr:h host -pr:b build")
     # Check the meson configuration file
-    conan_meson = client.load(os.path.join(client.current_folder, "conan_meson_cross.ini"))
+    conan_meson = client.load("conan_meson_cross.ini")
     assert f"sys_root = '{sysroot}'\n" in conan_meson
     assert re.search(r"c_args =.+--sysroot={}.+".format(sysroot), conan_meson)
     assert re.search(r"c_link_args =.+--sysroot={}.+".format(sysroot), conan_meson)
     assert re.search(r"cpp_args =.+--sysroot={}.+".format(sysroot), conan_meson)
     assert re.search(r"cpp_link_args =.+--sysroot={}.+".format(sysroot), conan_meson)
+
+
+def test_cross_x86_64_to_x86():
+    """
+    https://github.com/conan-io/conan/issues/17261
+    """
+
+    c = TestClient()
+    c.save({"conanfile.py": GenConanfile().with_settings("os", "compiler", "arch", "build_type")})
+    c.run("install . -g MesonToolchain -s arch=x86 -s:b arch=x86_64")
+    assert not os.path.exists(os.path.join(c.current_folder, MesonToolchain.native_filename))
+    cross = c.load(MesonToolchain.cross_filename)
+    assert "cpu = 'x86_64'" in cross  # This is the build machine
+    assert "cpu = 'x86'" in cross  # This is the host machine
diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py
index 2865fb6c4..987558edc 100644
--- a/test/integration/tools/ros/test_rosenv.py
+++ b/test/integration/tools/ros/test_rosenv.py
@@ -32,9 +32,12 @@ def test_rosenv():
     assert os.path.exists(conanrosenv_path)
     conanrosenvbuild_path = os.path.join(install_folder, "conanrosenv-build.sh")
     assert os.path.exists(conanrosenvbuild_path)
-    client.run_command(f". \"{conanrosenv_path}\" && env")
     toolchain_path = os.path.join(client.current_folder, "install", "conan", "conan_toolchain.cmake")
-    assert f"CMAKE_TOOLCHAIN_FILE=\"{toolchain_path}\"" in client.out
+    content = client.load(conanrosenvbuild_path)
+    assert f'CMAKE_TOOLCHAIN_FILE="{toolchain_path}"' in content
+    assert 'CMAKE_BUILD_TYPE="Release"' in content
+    client.run_command(f'. "{conanrosenv_path}" && env')
+    assert f"CMAKE_TOOLCHAIN_FILE={toolchain_path}" in client.out
     assert "CMAKE_BUILD_TYPE=Release" in client.out