diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py index 25786a168ce..2824a1e9bf5 100644 --- a/conan/tools/meson/toolchain.py +++ b/conan/tools/meson/toolchain.py @@ -3,6 +3,7 @@ from jinja2 import Template from conan.tools._check_build_profile import check_using_build_profile +from conan.tools.apple.apple import is_apple_os, to_apple_arch, apple_min_version_flag, XCRun from conan.tools.env import VirtualBuildEnv from conan.tools.microsoft import VCVars from conans.client.build.cppstd_flags import cppstd_from_settings @@ -10,7 +11,7 @@ from conans.util.files import save -class MesonToolchain(object): +class MesonBaseToolchain(object): native_filename = "conan_meson_native.ini" cross_filename = "conan_meson_cross.ini" @@ -46,7 +47,14 @@ class MesonToolchain(object): c_link_args = {{c_link_args}} cpp_args = {{cpp_args}} + preprocessor_definitions cpp_link_args = {{cpp_link_args}} + objc_args = {{objc_args}} + preprocessor_definitions + objc_link_args = {{objc_link_args}} + objcpp_args = {{objcpp_args}} + preprocessor_definitions + objcpp_link_args = {{objcpp_link_args}} {% if pkg_config_path %}pkg_config_path = {{pkg_config_path}}{% endif %} + + [properties] + {% if root %}root = {{root}}{% endif %} """) _cross_file_template = _native_file_template + textwrap.dedent(""" @@ -111,15 +119,30 @@ def from_build_env(name): # https://mesonbuild.com/Builtin-options.html#compiler-options self.cpp_std = self._to_meson_cppstd(self._cppstd) if self._cppstd else None - self.c_args = self._to_meson_value(self._env_array('CPPFLAGS') + self._env_array('CFLAGS')) - self.c_link_args = self._to_meson_value(self._env_array('LDFLAGS')) - self.cpp_args = self._to_meson_value(self._env_array('CPPFLAGS') + - self._env_array('CXXFLAGS')) - self.cpp_link_args = self._to_meson_value(self._env_array('LDFLAGS')) + self.c_args = self._env_array('CPPFLAGS') + self._env_array('CFLAGS') + self.c_link_args = self._env_array('LDFLAGS') + self.cpp_args = self._env_array('CPPFLAGS') + self._env_array('CXXFLAGS') + self.cpp_link_args = self._env_array('LDFLAGS') + self.objc_args = self._env_array('CPPFLAGS') + self._env_array('OBJCFLAGS') + self.objc_link_args = self._env_array('LDFLAGS') + self.objcpp_args = self._env_array('CPPFLAGS') + self._env_array('OBJCXXFLAGS') + self.objcpp_link_args = self._env_array('LDFLAGS') self.pkg_config_path = "'%s'" % self._conanfile.generators_folder + self.root = None check_using_build_profile(self._conanfile) + super(MesonBaseToolchain, self).__init__(conanfile) + + self.c_args = self._to_meson_value(self.c_args) + self.c_link_args = self._to_meson_value(self.c_link_args) + self.cpp_args = self._to_meson_value(self.cpp_args) + self.cpp_link_args = self._to_meson_value(self.cpp_link_args) + self.objc_args = self._to_meson_value(self.objc_args) + self.objc_link_args = self._to_meson_value(self.objc_link_args) + self.objcpp_args = self._to_meson_value(self.objcpp_args) + self.objcpp_link_args = self._to_meson_value(self.objcpp_link_args) + def _get_backend(self, recipe_backend): # Returns the name of the backend used by Meson conanfile = self._conanfile @@ -238,8 +261,13 @@ def _context(self): "c_link_args": self.c_link_args, "cpp_args": self.cpp_args, "cpp_link_args": self.cpp_link_args, + "objc_args": self.objc_args, + "objc_link_args": self.objc_link_args, + "objcpp_args": self.objcpp_args, + "objcpp_link_args": self.objcpp_link_args, "pkg_config_path": self.pkg_config_path, - "preprocessor_definitions": self.preprocessor_definitions + "preprocessor_definitions": self.preprocessor_definitions, + "root": self.root } return context @@ -333,3 +361,45 @@ def generate(self): else: self._write_native_file() VCVars(self._conanfile).generate() + + +class MesonAppleMixin(object): + def __init__(self, conanfile): + if not cross_building(conanfile): + return + if self._base_compiler != 'apple-clang': + return + if not is_apple_os(conanfile.settings.get_safe("os")): + return + + xcrun = XCRun(conanfile.settings) + self.c = self.c or self._to_meson_value(xcrun.cc) + self.cpp = self.cpp or self._to_meson_value(xcrun.cxx) + + arch = to_apple_arch(conanfile.settings.get_safe("arch")) + if arch: + self.extend_args(["-arch", arch]) + + os_version = conanfile.settings.get_safe("os.version") + if os_version: + flag = apple_min_version_flag(conanfile) + self.extend_args([flag]) + + if xcrun.sdk_path: + self.extend_args(["-isysroot", xcrun.sdk_path]) + self.root = self._to_meson_value(xcrun.sdk_path) + + def extend_args(self, args): + self.c_args.extend(args) + self.c_link_args.extend(args) + self.cpp_args.extend(args) + self.cpp_link_args.extend(args) + self.objc_args.extend(args) + self.objc_link_args.extend(args) + self.objcpp_args.extend(args) + self.objcpp_link_args.extend(args) + + +class MesonToolchain(MesonBaseToolchain, MesonAppleMixin): + def __init__(self, conanfile): + super(MesonToolchain, self).__init__(conanfile) diff --git a/conans/test/functional/toolchains/meson/test_ios.py b/conans/test/functional/toolchains/meson/test_ios.py index 9b2e6786e09..abfffff34b6 100644 --- a/conans/test/functional/toolchains/meson/test_ios.py +++ b/conans/test/functional/toolchains/meson/test_ios.py @@ -7,7 +7,7 @@ import pytest from parameterized import parameterized -from conans.client.tools.apple import XCRun, apple_deployment_target_flag, to_apple_arch +from conans.client.tools.apple import XCRun, to_apple_arch from conans.test.assets.sources import gen_function_cpp, gen_function_h from conans.test.utils.tools import TestClient @@ -61,32 +61,14 @@ def settings(self): ("compiler.version", "12.0"), ("compiler.libcxx", "libc++")] - def env(self): - cc = self.xcrun.cc - cxx = self.xcrun.cxx - - deployment_flag = apple_deployment_target_flag(self.os, self.os_version) - sysroot_flag = " -isysroot " + self.xcrun.sdk_path - arch_flag = " -arch " + to_apple_arch(self.arch) - flags = deployment_flag + sysroot_flag + arch_flag - - return {'CC': cc, - 'CXX': cxx, - 'CFLAGS': flags, - 'CXXFLAGS': flags, - 'LDFLAGS': flags} - def profile(self): template = textwrap.dedent(""" include(default) [settings] {settings} - [buildenv] - {env} """) settings = '\n'.join(["%s = %s" % (s[0], s[1]) for s in self.settings()]) - env = '\n'.join(["%s=%s" % (k, v) for k, v in self.env().items()]) - return template.format(settings=settings, env=env) + return template.format(settings=settings) @parameterized.expand([('armv8', 'iOS', '10.0', 'iphoneos'), ('armv7', 'iOS', '10.0', 'iphoneos'), diff --git a/conans/test/functional/toolchains/meson/test_m1.py b/conans/test/functional/toolchains/meson/test_m1.py new file mode 100644 index 00000000000..f4214d16c5e --- /dev/null +++ b/conans/test/functional/toolchains/meson/test_m1.py @@ -0,0 +1,103 @@ +import os +import platform +import sys +import textwrap +import unittest + +import pytest + +from conans.client.tools.apple import XCRun +from conans.test.assets.sources import gen_function_cpp, gen_function_h +from conans.test.utils.tools import TestClient + + +@pytest.mark.tool_meson +@pytest.mark.skipif(sys.version_info.major == 2, reason="Meson not supported in Py2") +@pytest.mark.skipif(platform.system() != "Darwin", reason="requires Xcode") +class M1MesonTestCase(unittest.TestCase): + + _conanfile_py = textwrap.dedent(""" + from conans import ConanFile, tools + from conan.tools.meson import Meson, MesonToolchain + + + class App(ConanFile): + settings = "os", "arch", "compiler", "build_type" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def generate(self): + tc = MesonToolchain(self) + tc.generate() + + def build(self): + meson = Meson(self) + meson.configure() + meson.build() + """) + + _meson_build = textwrap.dedent(""" + project('tutorial', 'cpp') + add_global_arguments('-DSTRING_DEFINITION="' + get_option('STRING_DEFINITION') + '"', + language : 'cpp') + hello = library('hello', 'hello.cpp') + executable('demo', 'main.cpp', link_with: hello) + """) + + _meson_options_txt = textwrap.dedent(""" + option('STRING_DEFINITION', type : 'string', description : 'a string option') + """) + + def settings(self): + return [("os", "Macos"), + ("arch", "armv8"), + ("compiler", "apple-clang"), + ("compiler.version", "12.0"), + ("compiler.libcxx", "libc++")] + + def profile(self): + template = textwrap.dedent(""" + include(default) + [settings] + {settings} + """) + settings = '\n'.join(["%s = %s" % (s[0], s[1]) for s in self.settings()]) + return template.format(settings=settings) + + def test_meson_toolchain(self): + self.xcrun = XCRun(None) + + hello_h = gen_function_h(name="hello") + hello_cpp = gen_function_cpp(name="hello", preprocessor=["STRING_DEFINITION"]) + app = gen_function_cpp(name="main", includes=["hello"], calls=["hello"]) + + self.t = TestClient() + + self.t.save({"conanfile.py": self._conanfile_py, + "meson.build": self._meson_build, + "meson_options.txt": self._meson_options_txt, + "hello.h": hello_h, + "hello.cpp": hello_cpp, + "main.cpp": app, + "profile_host": self.profile()}) + + self.t.run("install . --profile:build=default --profile:host=profile_host") + + self.t.run("build .") + + libhello = os.path.join(self.t.current_folder, "build", "libhello.a") + self.assertTrue(os.path.isfile(libhello)) + demo = os.path.join(self.t.current_folder, "build", "demo") + self.assertTrue(os.path.isfile(demo)) + + lipo = self.xcrun.find('lipo') + + self.t.run_command('"%s" -info "%s"' % (lipo, libhello)) + self.assertIn("architecture: arm64", self.t.out) + + self.t.run_command('"%s" -info "%s"' % (lipo, demo)) + self.assertIn("architecture: arm64", self.t.out)