File indexing completed on 2025-02-16 05:12:09
0001 import os 0002 import platform 0003 0004 0005 from conans.client import tools 0006 from conans.client.build import defs_to_string, join_arguments 0007 from conans.client.build.autotools_environment import AutoToolsBuildEnvironment 0008 from conans.client.build.cppstd_flags import cppstd_from_settings 0009 from conans.client.tools.env import environment_append, _environment_add 0010 from conans.client.tools.oss import args_to_string 0011 from conans.errors import ConanException 0012 from conans.model.build_info import DEFAULT_BIN, DEFAULT_INCLUDE, DEFAULT_LIB 0013 from conans.model.version import Version 0014 from conans.util.conan_v2_mode import conan_v2_error 0015 from conans.util.env_reader import get_env 0016 from conans.util.files import decode_text, get_abs_path, mkdir 0017 from conans.util.runners import version_runner 0018 0019 0020 class Meson(object): 0021 0022 def __init__(self, conanfile, backend=None, build_type=None, append_vcvars=False): 0023 """ 0024 :param conanfile: Conanfile instance (or settings for retro compatibility) 0025 :param backend: Generator name to use or none to autodetect. 0026 Possible values: ninja,vs,vs2010,vs2015,vs2017,xcode 0027 :param build_type: Overrides default build type comming from settings 0028 """ 0029 self._conanfile = conanfile 0030 self._settings = conanfile.settings 0031 self._append_vcvars = append_vcvars 0032 0033 self._os = self._ss("os") 0034 0035 self._compiler = self._ss("compiler") 0036 conan_v2_error("compiler setting should be defined.", not self._compiler) 0037 0038 self._compiler_version = self._ss("compiler.version") 0039 0040 self._build_type = self._ss("build_type") 0041 0042 self.backend = backend or "ninja" # Other backends are poorly supported, not default other. 0043 0044 self.options = dict() 0045 if self._conanfile.package_folder: 0046 self.options['prefix'] = self._conanfile.package_folder 0047 self.options['libdir'] = DEFAULT_LIB 0048 self.options['bindir'] = DEFAULT_BIN 0049 self.options['sbindir'] = DEFAULT_BIN 0050 self.options['libexecdir'] = DEFAULT_BIN 0051 self.options['includedir'] = DEFAULT_INCLUDE 0052 0053 # C++ standard 0054 cppstd = cppstd_from_settings(self._conanfile.settings) 0055 cppstd_conan2meson = { 0056 '98': 'c++03', 'gnu98': 'gnu++03', 0057 '11': 'c++11', 'gnu11': 'gnu++11', 0058 '14': 'c++14', 'gnu14': 'gnu++14', 0059 '17': 'c++17', 'gnu17': 'gnu++17', 0060 '20': 'c++1z', 'gnu20': 'gnu++1z' 0061 } 0062 0063 if cppstd: 0064 self.options['cpp_std'] = cppstd_conan2meson[cppstd] 0065 0066 # shared 0067 shared = self._so("shared") 0068 self.options['default_library'] = "shared" if shared is None or shared else "static" 0069 0070 # fpic 0071 if self._os and "Windows" not in self._os: 0072 fpic = self._so("fPIC") 0073 if fpic is not None: 0074 shared = self._so("shared") 0075 self.options['b_staticpic'] = "true" if (fpic or shared) else "false" 0076 0077 self.build_dir = None 0078 if build_type and build_type != self._build_type: 0079 # Call the setter to warn and update the definitions if needed 0080 self.build_type = build_type 0081 0082 def _ss(self, setname): 0083 """safe setting""" 0084 return self._conanfile.settings.get_safe(setname) 0085 0086 def _so(self, setname): 0087 """safe option""" 0088 return self._conanfile.options.get_safe(setname) 0089 0090 @property 0091 def build_type(self): 0092 return self._build_type 0093 0094 @build_type.setter 0095 def build_type(self, build_type): 0096 settings_build_type = self._settings.get_safe("build_type") 0097 if build_type != settings_build_type: 0098 self._conanfile.output.warn( 0099 'Set build type "%s" is different than the settings build_type "%s"' 0100 % (build_type, settings_build_type)) 0101 self._build_type = build_type 0102 0103 @property 0104 def build_folder(self): 0105 return self.build_dir 0106 0107 @build_folder.setter 0108 def build_folder(self, value): 0109 self.build_dir = value 0110 0111 def _get_dirs(self, source_folder, build_folder, source_dir, build_dir, cache_build_folder): 0112 if (source_folder or build_folder) and (source_dir or build_dir): 0113 raise ConanException("Use 'build_folder'/'source_folder'") 0114 0115 if source_dir or build_dir: # OLD MODE 0116 build_ret = build_dir or self.build_dir or self._conanfile.build_folder 0117 source_ret = source_dir or self._conanfile.source_folder 0118 else: 0119 build_ret = get_abs_path(build_folder, self._conanfile.build_folder) 0120 source_ret = get_abs_path(source_folder, self._conanfile.source_folder) 0121 0122 if self._conanfile.in_local_cache and cache_build_folder: 0123 build_ret = get_abs_path(cache_build_folder, self._conanfile.build_folder) 0124 0125 return source_ret, build_ret 0126 0127 @property 0128 def flags(self): 0129 return defs_to_string(self.options) 0130 0131 def configure(self, args=None, defs=None, source_dir=None, build_dir=None, 0132 pkg_config_paths=None, cache_build_folder=None, 0133 build_folder=None, source_folder=None): 0134 if not self._conanfile.should_configure: 0135 return 0136 args = args or [] 0137 defs = defs or {} 0138 0139 # overwrite default values with user's inputs 0140 self.options.update(defs) 0141 0142 source_dir, self.build_dir = self._get_dirs(source_folder, build_folder, 0143 source_dir, build_dir, 0144 cache_build_folder) 0145 0146 if pkg_config_paths: 0147 pc_paths = os.pathsep.join(get_abs_path(f, self._conanfile.install_folder) 0148 for f in pkg_config_paths) 0149 else: 0150 pc_paths = self._conanfile.install_folder 0151 0152 mkdir(self.build_dir) 0153 0154 bt = {"RelWithDebInfo": "debugoptimized", 0155 "MinSizeRel": "release", 0156 "Debug": "debug", 0157 "Release": "release"}.get(str(self.build_type), "") 0158 0159 build_type = "--buildtype=%s" % bt 0160 arg_list = join_arguments([ 0161 "--backend=%s" % self.backend, 0162 self.flags, 0163 args_to_string(args), 0164 build_type 0165 ]) 0166 command = 'meson "%s" "%s" %s' % (source_dir, self.build_dir, arg_list) 0167 with environment_append({"PKG_CONFIG_PATH": pc_paths}): 0168 self._run(command) 0169 0170 @property 0171 def _vcvars_needed(self): 0172 return (self._compiler == "Visual Studio" and self.backend == "ninja" and 0173 platform.system() == "Windows") 0174 0175 def _run(self, command): 0176 def _build(): 0177 env_build = AutoToolsBuildEnvironment(self._conanfile) 0178 with environment_append(env_build.vars): 0179 self._conanfile.run(command) 0180 0181 if self._vcvars_needed: 0182 vcvars_dict = tools.vcvars_dict(self._settings, output=self._conanfile.output) 0183 with _environment_add(vcvars_dict, post=self._append_vcvars): 0184 _build() 0185 else: 0186 _build() 0187 0188 def _run_meson_targets(self, args=None, build_dir=None, targets=None): 0189 args = args or [] 0190 build_dir = build_dir or self.build_dir or self._conanfile.build_folder 0191 0192 arg_list = join_arguments([ 0193 '-C "%s"' % build_dir, 0194 args_to_string(args), 0195 args_to_string(targets) 0196 ]) 0197 # FIXME: We are assuming for other backends that meson version is > 0.55.0 0198 # so you can use new command "meson compile" 0199 command = "ninja" if self.backend == "ninja" else "meson compile" 0200 self._run("%s %s" % (command, arg_list)) 0201 0202 def _run_meson_command(self, subcommand=None, args=None, build_dir=None): 0203 args = args or [] 0204 build_dir = build_dir or self.build_dir or self._conanfile.build_folder 0205 0206 arg_list = join_arguments([ 0207 subcommand, 0208 '-C "%s"' % build_dir, 0209 args_to_string(args) 0210 ]) 0211 self._run("meson %s" % arg_list) 0212 0213 def build(self, args=None, build_dir=None, targets=None): 0214 if not self._conanfile.should_build: 0215 return 0216 conan_v2_error("build_type setting should be defined.", not self._build_type) 0217 self._run_meson_targets(args=args, build_dir=build_dir, targets=targets) 0218 0219 def install(self, args=None, build_dir=None): 0220 if not self._conanfile.should_install: 0221 return 0222 mkdir(self._conanfile.package_folder) 0223 if not self.options.get('prefix'): 0224 raise ConanException("'prefix' not defined for 'meson.install()'\n" 0225 "Make sure 'package_folder' is defined") 0226 self._run_meson_targets(args=args, build_dir=build_dir, targets=["install"]) 0227 0228 def test(self, args=None, build_dir=None, targets=None): 0229 if not self._conanfile.should_test or not get_env("CONAN_RUN_TESTS", True) or \ 0230 self._conanfile.conf["tools.build:skip_test"]: 0231 return 0232 if not targets: 0233 targets = ["test"] 0234 self._run_meson_targets(args=args, build_dir=build_dir, targets=targets) 0235 0236 def meson_install(self, args=None, build_dir=None): 0237 if not self._conanfile.should_install: 0238 return 0239 self._run_meson_command(subcommand='install', args=args, build_dir=build_dir) 0240 0241 def meson_test(self, args=None, build_dir=None): 0242 if not self._conanfile.should_test or not get_env("CONAN_RUN_TESTS", True) or \ 0243 self._conanfile.conf["tools.build:skip_test"]: 0244 return 0245 self._run_meson_command(subcommand='test', args=args, build_dir=build_dir) 0246 0247 @staticmethod 0248 def get_version(): 0249 try: 0250 out = version_runner(["meson", "--version"]) 0251 version_line = decode_text(out).split('\n', 1)[0] 0252 version_str = version_line.rsplit(' ', 1)[-1] 0253 return Version(version_str) 0254 except Exception as e: 0255 raise ConanException("Error retrieving Meson version: '{}'".format(e))