Skip to content

Commit

Permalink
[MesonToolchain] Added native mechanism when cross-building (#15919)
Browse files Browse the repository at this point in the history
* Added native attribute to MesonToolchain. Adapted the whole Meson mechanism

* Added functional test

* Added integration test

* Update conans/test/functional/toolchains/meson/test_meson_native_attribute.py

Co-authored-by: James <memsharded@gmail.com>

* typo

* Get back to cross_build check

* Fixed if-else condition if native

---------

Co-authored-by: James <memsharded@gmail.com>
  • Loading branch information
franramirez688 and memsharded committed May 6, 2024
1 parent 0fcb1dc commit 66ef385
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 59 deletions.
24 changes: 11 additions & 13 deletions conan/tools/meson/meson.py
Expand Up @@ -29,24 +29,22 @@ def configure(self, reconfigure=False):
" Removing in Conan 2.x.", warn_tag="deprecated")
source_folder = self._conanfile.source_folder
build_folder = self._conanfile.build_folder
cmd = "meson setup"
generators_folder = self._conanfile.generators_folder
cross = os.path.join(generators_folder, MesonToolchain.cross_filename)
native = os.path.join(generators_folder, MesonToolchain.native_filename)
meson_filenames = []
if os.path.exists(cross):
cmd_param = " --cross-file"
meson_filenames.append(cross)
else:
cmd_param = " --native-file"
meson_filenames.append(native)

is_cross_build = os.path.exists(cross)
machine_files = self._conanfile.conf.get("tools.meson.mesontoolchain:extra_machine_files",
default=[], check_type=list)
if machine_files:
meson_filenames.extend(machine_files)

cmd += "".join([f'{cmd_param} "{meson_option}"' for meson_option in meson_filenames])
cmd = "meson setup "
if is_cross_build:
machine_files.insert(0, cross)
cmd += "".join([f'--cross-file "{file}"' for file in machine_files])
if os.path.exists(native):
if not is_cross_build: # machine files are only appended to the cross or the native one
machine_files.insert(0, native)
cmd += "".join([f'--native-file "{file}"' for file in machine_files])
else: # extra native file for cross-building scenarios
cmd += f' --native-file "{native}"'
cmd += ' "{}" "{}"'.format(build_folder, source_folder)
# Issue related: https://github.com/mesonbuild/meson/issues/12880
cmd += ' --prefix=/' # this must be an absolute path, otherwise, meson complains
Expand Down
146 changes: 101 additions & 45 deletions conan/tools/meson/toolchain.py
@@ -1,7 +1,7 @@
import os
import textwrap

from jinja2 import Template
from jinja2 import Template, StrictUndefined

from conan.errors import ConanException
from conan.internal import check_duplicated_generator
Expand All @@ -20,11 +20,10 @@ class MesonToolchain(object):
"""
MesonToolchain generator
"""

native_filename = "conan_meson_native.ini"
cross_filename = "conan_meson_cross.ini"

_meson_file_template = textwrap.dedent("""
_meson_file_template = textwrap.dedent("""\
[properties]
{% for it, value in properties.items() -%}
{{it}} = {{value}}
Expand All @@ -49,33 +48,76 @@ class MesonToolchain(object):
{% endfor %}
[binaries]
{% if c %}c = {{c}}{% endif %}
{% if cpp %}cpp = {{cpp}}{% endif %}
{% if ld %}ld = {{ld}}{% endif %}
{% if c %}
c = {{c}}
{% endif %}
{% if cpp %}
cpp = {{cpp}}
{% endif %}
{% if ld %}
ld = {{ld}}
{% endif %}
{% if is_apple_system %}
{% if objc %}objc = '{{objc}}'{% endif %}
{% if objcpp %}objcpp = '{{objcpp}}'{% endif %}
{% if objc %}
objc = '{{objc}}'
{% endif %}
{% if objcpp %}
objcpp = '{{objcpp}}'
{% endif %}
{% endif %}
{% if c_ld %}
c_ld = '{{c_ld}}'
{% endif %}
{% if cpp_ld %}
cpp_ld = '{{cpp_ld}}'
{% endif %}
{% if ar %}
ar = '{{ar}}'
{% endif %}
{% if strip %}
strip = '{{strip}}'
{% endif %}
{% if as %}
as = '{{as}}'
{% endif %}
{% if windres %}
windres = '{{windres}}'
{% endif %}
{% if pkgconfig %}
pkgconfig = '{{pkgconfig}}'
{% endif %}
{% if pkgconfig %}
pkg-config = '{{pkgconfig}}'
{% endif %}
{% if c_ld %}c_ld = '{{c_ld}}'{% endif %}
{% if cpp_ld %}cpp_ld = '{{cpp_ld}}'{% endif %}
{% if ar %}ar = '{{ar}}'{% endif %}
{% if strip %}strip = '{{strip}}'{% endif %}
{% if as %}as = '{{as}}'{% endif %}
{% if windres %}windres = '{{windres}}'{% endif %}
{% if pkgconfig %}pkgconfig = '{{pkgconfig}}'{% endif %}
{% if pkgconfig %}pkg-config = '{{pkgconfig}}'{% endif %}
[built-in options]
{% if buildtype %}buildtype = '{{buildtype}}'{% endif %}
{% if debug %}debug = {{debug}}{% endif %}
{% if default_library %}default_library = '{{default_library}}'{% endif %}
{% if b_vscrt %}b_vscrt = '{{b_vscrt}}' {% endif %}
{% if b_ndebug %}b_ndebug = {{b_ndebug}}{% endif %}
{% if b_staticpic %}b_staticpic = {{b_staticpic}}{% endif %}
{% if cpp_std %}cpp_std = '{{cpp_std}}' {% endif %}
{% if backend %}backend = '{{backend}}' {% endif %}
{% if pkg_config_path %}pkg_config_path = '{{pkg_config_path}}'{% endif %}
{% if build_pkg_config_path %}build.pkg_config_path = '{{build_pkg_config_path}}'{% endif %}
{% if buildtype %}
buildtype = '{{buildtype}}'
{% endif %}
{% if default_library %}
default_library = '{{default_library}}'
{% endif %}
{% if b_vscrt %}
b_vscrt = '{{b_vscrt}}'
{% endif %}
{% if b_ndebug %}
b_ndebug = {{b_ndebug}}
{% endif %}
{% if b_staticpic %}
b_staticpic = {{b_staticpic}}
{% endif %}
{% if cpp_std %}
cpp_std = '{{cpp_std}}'
{% endif %}
{% if backend %}
backend = '{{backend}}'
{% endif %}
{% if pkg_config_path %}
pkg_config_path = '{{pkg_config_path}}'
{% endif %}
{% if build_pkg_config_path %}
build.pkg_config_path = '{{build_pkg_config_path}}'
{% endif %}
# C/C++ arguments
c_args = {{c_args}} + preprocessor_definitions
c_link_args = {{c_link_args}}
Expand All @@ -98,16 +140,20 @@ class MesonToolchain(object):
{% endfor %}
""")

def __init__(self, conanfile, backend=None):
def __init__(self, conanfile, backend=None, native=False):
"""
:param conanfile: ``< ConanFile object >`` The current recipe object. Always use ``self``.
:param backend: ``str`` ``backend`` Meson variable value. By default, ``ninja``.
"""
raise_on_universal_arch(conanfile)
self._conanfile = conanfile
self._os = self._conanfile.settings.get_safe("os")
self._native = native
self._is_apple_system = is_apple_os(self._conanfile)

is_cross_building = cross_building(conanfile, skip_x64_x86=True)
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.")
self._conanfile_conf = self._conanfile.conf_build if native else self._conanfile.conf
# 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"
Expand Down Expand Up @@ -163,12 +209,11 @@ def __init__(self, conanfile, backend=None):
# Issue: https://github.com/conan-io/conan/issues/14935
self.build_pkg_config_path = None
self.libcxx, self.gcc_cxx11_abi = libcxx_flags(self._conanfile)

#: Dict-like object with the build, host, and target as the Meson machine context
self.cross_build = {}
default_comp = ""
default_comp_cpp = ""
if cross_building(conanfile, skip_x64_x86=True):
if native is False and is_cross_building:
os_host = conanfile.settings.get_safe("os")
arch_host = conanfile.settings.get_safe("arch")
os_build = conanfile.settings_build.get_safe('os')
Expand Down Expand Up @@ -197,10 +242,11 @@ def __init__(self, conanfile, backend=None):
default_comp_cpp = "cl"

# Read configuration for compilers
compilers_by_conf = self._conanfile.conf.get("tools.build:compiler_executables", default={},
compilers_by_conf = self._conanfile_conf.get("tools.build:compiler_executables", default={},
check_type=dict)
# Read the VirtualBuildEnv to update the variables
build_env = VirtualBuildEnv(self._conanfile, auto_generate=True).vars()
build_env = self._conanfile.buildenv_build.vars(self._conanfile) if native else (
VirtualBuildEnv(self._conanfile, auto_generate=True).vars())
#: Sets the Meson ``c`` variable, defaulting to the ``CC`` build environment value.
#: If provided as a blank-separated string, it will be transformed into a list.
#: Otherwise, it remains a single string.
Expand Down Expand Up @@ -232,7 +278,7 @@ def __init__(self, conanfile, backend=None):
self.windres = build_env.get("WINDRES")
#: Defines the Meson ``pkgconfig`` variable. Defaulted to ``PKG_CONFIG``
#: build environment value
self.pkgconfig = (self._conanfile.conf.get("tools.gnu:pkg_config", check_type=str) or
self.pkgconfig = (self._conanfile_conf.get("tools.gnu:pkg_config", check_type=str) or
build_env.get("PKG_CONFIG"))
#: Defines the Meson ``c_args`` variable. Defaulted to ``CFLAGS`` build environment value
self.c_args = self._get_env_list(build_env.get("CFLAGS", []))
Expand Down Expand Up @@ -266,7 +312,8 @@ def __init__(self, conanfile, backend=None):
self.objcpp_link_args = []

self._resolve_apple_flags_and_variables(build_env, compilers_by_conf)
self._resolve_android_cross_compilation()
if native is False:
self._resolve_android_cross_compilation()

def _get_default_dirs(self):
"""
Expand Down Expand Up @@ -309,7 +356,7 @@ def _resolve_apple_flags_and_variables(self, build_env, compilers_by_conf):
return
# Calculating the main Apple flags
min_flag, arch_flag, isysroot_flag = (
resolve_apple_flags(self._conanfile, is_cross_building=bool(self.cross_build)))
resolve_apple_flags(self._conanfile, is_cross_building=self.cross_build))
self.apple_arch_flag = arch_flag.split() if arch_flag else []
self.apple_isysroot_flag = isysroot_flag.split() if isysroot_flag else []
self.apple_min_version_flag = [apple_min_version_flag(self._conanfile)]
Expand All @@ -325,7 +372,7 @@ def _resolve_android_cross_compilation(self):
if not self.cross_build or not self.cross_build["host"]["system"] == "android":
return

ndk_path = self._conanfile.conf.get("tools.android:ndk_path")
ndk_path = self._conanfile_conf.get("tools.android:ndk_path")
if not ndk_path:
raise ConanException("You must provide a NDK path. Use 'tools.android:ndk_path' "
"configuration field.")
Expand All @@ -346,11 +393,11 @@ def _resolve_android_cross_compilation(self):

def _get_extra_flags(self):
# Now, it's time to get all the flags defined by the user
cxxflags = self._conanfile.conf.get("tools.build:cxxflags", default=[], check_type=list)
cflags = self._conanfile.conf.get("tools.build:cflags", default=[], check_type=list)
sharedlinkflags = self._conanfile.conf.get("tools.build:sharedlinkflags", default=[], check_type=list)
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)
cxxflags = self._conanfile_conf.get("tools.build:cxxflags", default=[], check_type=list)
cflags = self._conanfile_conf.get("tools.build:cflags", default=[], check_type=list)
sharedlinkflags = self._conanfile_conf.get("tools.build:sharedlinkflags", default=[], check_type=list)
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 = [f"-D{d}" for d in self._conanfile.conf.get("tools.build:defines", default=[], check_type=list)]
return {
Expand Down Expand Up @@ -450,6 +497,15 @@ def _sanitize_format(v):
"is_apple_system": self._is_apple_system
}

@property
def _filename(self):
if self.cross_build and self._native:
return self.native_filename
elif self.cross_build:
return self.cross_filename
else:
return self.native_filename

@property
def _content(self):
"""
Expand All @@ -458,7 +514,8 @@ def _content(self):
:return: ``str`` whole Meson context content.
"""
context = self._context()
content = Template(self._meson_file_template).render(context)
content = Template(self._meson_file_template, trim_blocks=True, lstrip_blocks=True,
undefined=StrictUndefined).render(context)
return content

def generate(self):
Expand All @@ -468,7 +525,6 @@ def generate(self):
If Windows OS, it will be created a ``conanvcvars.bat`` as well.
"""
check_duplicated_generator(self, self._conanfile)
filename = self.native_filename if not self.cross_build else self.cross_filename
save(filename, self._content)
save(self._filename, self._content)
# FIXME: Should we check the OS and compiler to call VCVars?
VCVars(self._conanfile).generate()
5 changes: 5 additions & 0 deletions conans/client/graph/profile_node_definer.py
Expand Up @@ -26,8 +26,13 @@ def initialize_conanfile_profile(conanfile, profile_build, profile_host, base_co
settings_build = _per_package_settings(conanfile, profile_build, ref)
if is_build_require or base_context == CONTEXT_BUILD:
_initialize_conanfile(conanfile, profile_build, settings_build.copy(), ref)
conanfile.buildenv_build = None
conanfile.conf_build = None
else:
_initialize_conanfile(conanfile, profile_host, settings_host, ref)
# Host profile with some build profile information
conanfile.buildenv_build = profile_build.buildenv.get_profile_env(ref, conanfile._conan_is_consumer)
conanfile.conf_build = profile_build.conf.get_conanfile_conf(ref, conanfile._conan_is_consumer)
conanfile.settings_build = settings_build
conanfile.settings_target = None

Expand Down
1 change: 0 additions & 1 deletion conans/model/conan_file.py
Expand Up @@ -9,7 +9,6 @@
from conans.model.dependencies import ConanFileDependencies
from conans.model.layout import Folders, Infos, Layouts
from conans.model.options import Options

from conans.model.requires import Requirements
from conans.model.settings import Settings

Expand Down

0 comments on commit 66ef385

Please sign in to comment.