Repository: conan-io/conan
Pull request #17024 (2024-09-19 12:52:55)

Test for universal binaries macOS using Ninja
Changelog: Fix: Replace `|` character in generated CMake and Environment files.
Docs: Omit

Closes: https://github.com/conan-io/conan/issues/17009


czoido: We could also do the replaces to the `sh's` for the environment generated scripts.

Patch:
diff --git a/conan/internal/default_settings.py b/conan/internal/default_settings.py
index 99bc04c30..a8f752e91 100644
--- a/conan/internal/default_settings.py
+++ b/conan/internal/default_settings.py
@@ -120,7 +120,7 @@ compiler:
     clang:
         version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0",
                   "5.0", "6.0", "7.0", "7.1",
-                  "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"]
+                  "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"]
         libcxx: [null, libstdc++, libstdc++11, libc++, c++_shared, c++_static]
         cppstd: [null, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
         runtime: [null, static, dynamic]
diff --git a/conan/tools/cmake/cmakedeps/templates/target_data.py b/conan/tools/cmake/cmakedeps/templates/target_data.py
index 35330ee09..1e5a6e286 100644
--- a/conan/tools/cmake/cmakedeps/templates/target_data.py
+++ b/conan/tools/cmake/cmakedeps/templates/target_data.py
@@ -23,7 +23,8 @@ class ConfigDataTemplate(CMakeDepsFileTemplate):
         if self.arch:
             data_fname += "-{}".format(self.arch)
         data_fname += "-data.cmake"
-        return data_fname
+        # https://github.com/conan-io/conan/issues/17009
+        return data_fname.replace("|", "_")
 
     @property
     def _build_modules_activated(self):
diff --git a/conan/tools/env/virtualbuildenv.py b/conan/tools/env/virtualbuildenv.py
index 0da4b9250..49e9d4847 100644
--- a/conan/tools/env/virtualbuildenv.py
+++ b/conan/tools/env/virtualbuildenv.py
@@ -34,7 +34,7 @@ class VirtualBuildEnv:
         if configuration:
             f += "-" + configuration.replace(".", "_")
         if arch:
-            f += "-" + arch.replace(".", "_")
+            f += "-" + arch.replace(".", "_").replace("|", "_")
         return f
 
     def environment(self):
diff --git a/conan/tools/env/virtualrunenv.py b/conan/tools/env/virtualrunenv.py
index c7308418b..890e8cd19 100644
--- a/conan/tools/env/virtualrunenv.py
+++ b/conan/tools/env/virtualrunenv.py
@@ -52,7 +52,7 @@ class VirtualRunEnv:
         if self.configuration:
             f += "-" + self.configuration.replace(".", "_")
         if self.arch:
-            f += "-" + self.arch.replace(".", "_")
+            f += "-" + self.arch.replace(".", "_").replace("|", "_")
         return f
 
     def environment(self):
diff --git a/conan/tools/gnu/pkgconfig.py b/conan/tools/gnu/pkgconfig.py
index 9d053819d..fb911ba93 100644
--- a/conan/tools/gnu/pkgconfig.py
+++ b/conan/tools/gnu/pkgconfig.py
@@ -1,3 +1,4 @@
+import textwrap
 from io import StringIO
 
 from conan.tools.build import cmd_args_to_string
@@ -30,8 +31,13 @@ class PkgConfig:
             env.prepend_path("PKG_CONFIG_PATH", self._pkg_config_path)
         with env.vars(self._conanfile).apply():
             # This way we get the environment from ConanFile, from profile (default buildenv)
-            output = StringIO()
-            self._conanfile.run(command, stdout=output, quiet=True)
+            output, err = StringIO(), StringIO()
+            ret = self._conanfile.run(command, stdout=output, stderr=err, quiet=True,
+                                      ignore_errors=True)
+            if ret != 0:
+                raise ConanException(f"PkgConfig failed. Command: {command}\n"
+                                     f"    stdout:\n{textwrap.indent(output.getvalue(), '    ')}\n"
+                                     f"    stderr:\n{textwrap.indent(err.getvalue(), '    ')}\n")
         value = output.getvalue().strip()
         return value
 


Test patch:
diff --git a/test/functional/toolchains/cmake/test_universal_binaries.py b/test/functional/toolchains/cmake/test_universal_binaries.py
index 45dcc2a5f..b02a2e412 100644
--- a/test/functional/toolchains/cmake/test_universal_binaries.py
+++ b/test/functional/toolchains/cmake/test_universal_binaries.py
@@ -99,3 +99,47 @@ def test_create_universal_binary():
     client.run_command("lipo -info './build/armv8|armv8.3|x86_64/Release/foo'")
 
     assert "foo are: x86_64 arm64 arm64e" in client.out
+
+
+@pytest.mark.skipif(platform.system() != "Darwin", reason="Only OSX")
+@pytest.mark.tool("cmake", "3.23")
+def test_create_universal_binary_ninja():
+    client = TestClient()
+
+    client.run("new cmake_lib -d name=mylibrary -d version=1.0")
+
+    client.run('export .')
+
+    conanfile = textwrap.dedent("""
+        [requires]
+        mylibrary/1.0
+        [generators]
+        CMakeToolchain
+        CMakeDeps
+        VirtualBuildEnv
+        VirtualRunEnv
+        """)
+
+    cmake = textwrap.dedent("""
+        cmake_minimum_required(VERSION 3.23)
+        project(ninjatest CXX)
+
+        find_package(mylibrary CONFIG REQUIRED)
+        """)
+
+    client.save({"conanfile.txt": conanfile,
+                 "CMakeLists.txt": cmake},
+                 clean_first=True)
+
+    client.run('install . -s=arch="armv8|x86_64" --build=missing -of=build')
+
+    with client.chdir("build"):
+        client.run_command("cmake .. -GNinja "
+                           "-DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake "
+                           "-DCMAKE_POLICY_DEFAULT_CMP0091=NEW "
+                           "-DCMAKE_BUILD_TYPE=Release")
+
+    assert "expected newline, got '|'" not in client.out
+    assert "Build files have been written to:" in client.out
+    # test that there are no files with the "|" character in the build folder
+    assert not any("|" in f for f in os.listdir(os.path.join(client.current_folder, "build")))
diff --git a/test/functional/toolchains/gnu/test_pkg_config.py b/test/functional/toolchains/gnu/test_pkg_config.py
index 5b9105f25..6d972f4a1 100644
--- a/test/functional/toolchains/gnu/test_pkg_config.py
+++ b/test/functional/toolchains/gnu/test_pkg_config.py
@@ -24,6 +24,8 @@ class TestPkgConfig:
                 """)
         c.save({"conanfile.py": conanfile})
         c.run("install .", assert_error=True)
+        assert "PkgConfig failed. Command: pkg-config --libs-only-l something_that_not_exist " \
+               "--print-errors" in c.out
         assert "Package something_that_not_exist was not found" in c.out
 
     def test_pc(self):