File indexing completed on 2025-02-16 05:12:09
0001 import os 0002 import platform 0003 from collections import OrderedDict 0004 0005 from conans.client import tools 0006 from conans.client.build.compiler_flags import architecture_flag, parallel_compiler_cl_flag 0007 from conans.client.build.cppstd_flags import cppstd_from_settings, cppstd_flag_new as cppstd_flag 0008 from conans.client.tools import cross_building, Version 0009 from conans.client.tools.apple import is_apple_os 0010 from conans.client.tools.oss import get_cross_building_settings 0011 from conans.errors import ConanException 0012 from conans.model.build_info import DEFAULT_BIN, DEFAULT_INCLUDE, DEFAULT_LIB, DEFAULT_SHARE 0013 from conans.util.env_reader import get_env 0014 from conans.util.log import logger 0015 0016 verbose_definition_name = "CMAKE_VERBOSE_MAKEFILE" 0017 cmake_install_prefix_var_name = "CMAKE_INSTALL_PREFIX" 0018 runtime_definition_var_name = "CONAN_LINK_RUNTIME" 0019 cmake_in_local_cache_var_name = "CONAN_IN_LOCAL_CACHE" 0020 0021 0022 def get_toolset(settings, generator): 0023 compiler = settings.get_safe("compiler") 0024 compiler_base = settings.get_safe("compiler.base") 0025 if compiler == "Visual Studio": 0026 subs_toolset = settings.get_safe("compiler.toolset") 0027 if subs_toolset: 0028 return subs_toolset 0029 elif compiler == "intel" and compiler_base == "Visual Studio" and "Visual" in generator: 0030 compiler_version = settings.get_safe("compiler.version") 0031 if compiler_version: 0032 compiler_version = compiler_version if "." in compiler_version else \ 0033 "%s.0" % compiler_version 0034 return "Intel C++ Compiler " + compiler_version 0035 return None 0036 0037 0038 def get_generator(conanfile): 0039 # Returns the name of the generator to be used by CMake 0040 if "CONAN_CMAKE_GENERATOR" in os.environ: 0041 return os.environ["CONAN_CMAKE_GENERATOR"] 0042 0043 compiler = conanfile.settings.get_safe("compiler") 0044 compiler_base = conanfile.settings.get_safe("compiler.base") 0045 arch = conanfile.settings.get_safe("arch") 0046 compiler_version = conanfile.settings.get_safe("compiler.version") 0047 compiler_base_version = conanfile.settings.get_safe("compiler.base.version") 0048 os_build, _, _, _ = get_cross_building_settings(conanfile) 0049 0050 if not compiler or not compiler_version or not arch: 0051 if os_build == "Windows": 0052 logger.warning("CMake generator could not be deduced from settings") 0053 return None 0054 return "Unix Makefiles" 0055 0056 cmake_years = {'8': '8 2005', 0057 '9': '9 2008', 0058 '10': '10 2010', 0059 '11': '11 2012', 0060 '12': '12 2013', 0061 '14': '14 2015', 0062 '15': '15 2017', 0063 '16': '16 2019', 0064 '17': '17 2022'} 0065 0066 if compiler == "msvc": 0067 if compiler_version is None: 0068 raise ConanException("compiler.version must be defined") 0069 from conan.tools.microsoft.visual import vs_ide_version 0070 vs_version = vs_ide_version(conanfile) 0071 return "Visual Studio %s" % cmake_years[vs_version] 0072 0073 if compiler == "Visual Studio" or compiler_base == "Visual Studio": 0074 version = compiler_base_version or compiler_version 0075 major_version = version.split('.', 1)[0] 0076 _visuals = cmake_years.get(major_version, "UnknownVersion %s" % version) 0077 base = "Visual Studio %s" % _visuals 0078 return base 0079 0080 # The generator depends on the build machine, not the target 0081 if os_build == "Windows" and compiler != "qcc": 0082 return "MinGW Makefiles" # it is valid only under Windows 0083 0084 return "Unix Makefiles" 0085 0086 0087 def get_generator_platform(settings, generator): 0088 # Returns the generator platform to be used by CMake 0089 if "CONAN_CMAKE_GENERATOR_PLATFORM" in os.environ: 0090 return os.environ["CONAN_CMAKE_GENERATOR_PLATFORM"] 0091 0092 compiler = settings.get_safe("compiler") 0093 compiler_base = settings.get_safe("compiler.base") 0094 arch = settings.get_safe("arch") 0095 0096 if settings.get_safe("os") == "WindowsCE": 0097 return settings.get_safe("os.platform") 0098 0099 if (compiler == "Visual Studio" or compiler_base == "Visual Studio") and \ 0100 generator and "Visual" in generator: 0101 return {"x86": "Win32", 0102 "x86_64": "x64", 0103 "armv7": "ARM", 0104 "armv8": "ARM64"}.get(arch) 0105 return None 0106 0107 0108 def is_multi_configuration(generator): 0109 if not generator: 0110 return False 0111 return "Visual" in generator or "Xcode" in generator or "Multi-Config" in generator 0112 0113 0114 def is_toolset_supported(generator): 0115 # https://cmake.org/cmake/help/v3.14/variable/CMAKE_GENERATOR_TOOLSET.html 0116 if not generator: 0117 return False 0118 return "Visual" in generator or "Xcode" in generator or "Green Hills MULTI" in generator 0119 0120 0121 def is_generator_platform_supported(generator): 0122 # https://cmake.org/cmake/help/v3.14/variable/CMAKE_GENERATOR_PLATFORM.html 0123 if not generator: 0124 return False 0125 return "Visual" in generator or "Green Hills MULTI" in generator 0126 0127 0128 def verbose_definition(value): 0129 return {verbose_definition_name: "ON" if value else "OFF"} 0130 0131 0132 def in_local_cache_definition(value): 0133 return {cmake_in_local_cache_var_name: "ON" if value else "OFF"} 0134 0135 0136 def runtime_definition(runtime): 0137 return {runtime_definition_var_name: "/%s" % runtime} if runtime else {} 0138 0139 0140 def build_type_definition(new_build_type, old_build_type, generator, output): 0141 if new_build_type and new_build_type != old_build_type: 0142 output.warn("Forced CMake build type ('%s') different from the settings build type ('%s')" 0143 % (new_build_type, old_build_type)) 0144 0145 build_type = new_build_type or old_build_type 0146 if build_type and not is_multi_configuration(generator): 0147 return {"CMAKE_BUILD_TYPE": build_type} 0148 return {} 0149 0150 0151 class CMakeDefinitionsBuilder(object): 0152 0153 def __init__(self, conanfile, cmake_system_name=True, make_program=None, 0154 parallel=True, generator=None, set_cmake_flags=False, 0155 forced_build_type=None, output=None): 0156 self._conanfile = conanfile 0157 self._forced_cmake_system_name = cmake_system_name 0158 self._make_program = make_program 0159 self._parallel = parallel 0160 self._generator = generator 0161 self._set_cmake_flags = set_cmake_flags 0162 self._forced_build_type = forced_build_type 0163 self._output = output 0164 0165 def _ss(self, setname): 0166 """safe setting""" 0167 return self._conanfile.settings.get_safe(setname) 0168 0169 def _get_cpp_standard_vars(self): 0170 cppstd = cppstd_from_settings(self._conanfile.settings) 0171 0172 if not cppstd: 0173 return {} 0174 0175 definitions = {} 0176 if cppstd.startswith("gnu"): 0177 definitions["CONAN_CMAKE_CXX_STANDARD"] = cppstd[3:] 0178 definitions["CONAN_CMAKE_CXX_EXTENSIONS"] = "ON" 0179 else: 0180 definitions["CONAN_CMAKE_CXX_STANDARD"] = cppstd 0181 definitions["CONAN_CMAKE_CXX_EXTENSIONS"] = "OFF" 0182 0183 definitions["CONAN_STD_CXX_FLAG"] = cppstd_flag(self._conanfile.settings) 0184 return definitions 0185 0186 def _cmake_cross_build_defines(self, cmake_version): 0187 os_ = self._ss("os") 0188 arch = self._ss("arch") 0189 os_ver_str = "os.api_level" if os_ == "Android" else "os.version" 0190 op_system_version = self._ss(os_ver_str) 0191 0192 env_sn = get_env("CONAN_CMAKE_SYSTEM_NAME", "") 0193 env_sn = {"False": False, "True": True, "": None}.get(env_sn, env_sn) 0194 cmake_system_name = env_sn or self._forced_cmake_system_name 0195 0196 os_build, _, _, _ = get_cross_building_settings(self._conanfile) 0197 compiler = self._ss("compiler") 0198 libcxx = self._ss("compiler.libcxx") 0199 0200 definitions = OrderedDict() 0201 os_ver = get_env("CONAN_CMAKE_SYSTEM_VERSION", op_system_version) 0202 toolchain_file = get_env("CONAN_CMAKE_TOOLCHAIN_FILE", "") 0203 0204 if toolchain_file != "": 0205 logger.info("Setting Cross build toolchain file: %s" % toolchain_file) 0206 definitions["CMAKE_TOOLCHAIN_FILE"] = toolchain_file 0207 return definitions 0208 0209 if cmake_system_name is False: 0210 return definitions 0211 0212 # System name and system version 0213 if cmake_system_name is not True: # String not empty 0214 definitions["CMAKE_SYSTEM_NAME"] = cmake_system_name 0215 else: # detect if we are cross building and the system name and version 0216 skip_x64_x86 = os_ in ['Windows', 'Linux', 'SunOS', 'AIX'] 0217 if cross_building(self._conanfile, skip_x64_x86=skip_x64_x86): # We are cross building 0218 apple_system_name = "Darwin" if cmake_version and Version(cmake_version) < Version( 0219 "3.14") or not cmake_version else None 0220 cmake_system_name_map = {"Macos": "Darwin", 0221 "iOS": apple_system_name or "iOS", 0222 "tvOS": apple_system_name or "tvOS", 0223 "watchOS": apple_system_name or "watchOS", 0224 "Neutrino": "QNX", 0225 "": "Generic", 0226 None: "Generic"} 0227 definitions["CMAKE_SYSTEM_NAME"] = cmake_system_name_map.get(os_, os_) 0228 0229 if os_ver: 0230 definitions["CMAKE_SYSTEM_VERSION"] = os_ver 0231 if is_apple_os(os_): 0232 definitions["CMAKE_OSX_DEPLOYMENT_TARGET"] = os_ver 0233 0234 # system processor 0235 cmake_system_processor = os.getenv("CONAN_CMAKE_SYSTEM_PROCESSOR") 0236 if cmake_system_processor: 0237 definitions["CMAKE_SYSTEM_PROCESSOR"] = cmake_system_processor 0238 0239 if definitions: # If enabled cross compile 0240 for env_var in ["CONAN_CMAKE_FIND_ROOT_PATH", 0241 "CONAN_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", 0242 "CONAN_CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", 0243 "CONAN_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE"]: 0244 0245 value = os.getenv(env_var) 0246 if value: 0247 definitions[env_var] = value 0248 0249 if self._conanfile and self._conanfile.deps_cpp_info.sysroot: 0250 sysroot_path = self._conanfile.deps_cpp_info.sysroot 0251 0252 if sysroot_path: 0253 # Needs to be set here, can't be managed in the cmake generator, CMake needs 0254 # to know about the sysroot before any other thing 0255 definitions["CMAKE_SYSROOT"] = sysroot_path.replace("\\", "/") 0256 0257 cmake_sysroot = os.getenv("CONAN_CMAKE_SYSROOT") 0258 if cmake_sysroot is not None: 0259 definitions["CMAKE_SYSROOT"] = cmake_sysroot.replace("\\", "/") 0260 0261 # Adjust Android stuff 0262 if str(os_) == "Android" and definitions["CMAKE_SYSTEM_NAME"] == "Android": 0263 arch_abi_settings = tools.to_android_abi(arch) 0264 if arch_abi_settings: 0265 definitions["CMAKE_ANDROID_ARCH_ABI"] = arch_abi_settings 0266 definitions["ANDROID_ABI"] = arch_abi_settings 0267 0268 conan_cmake_android_ndk = os.getenv("CONAN_CMAKE_ANDROID_NDK") 0269 if conan_cmake_android_ndk: 0270 definitions["ANDROID_NDK"] = conan_cmake_android_ndk 0271 0272 definitions["ANDROID_PLATFORM"] = "android-%s" % op_system_version 0273 definitions["ANDROID_TOOLCHAIN"] = compiler 0274 0275 # More details about supported stdc++ libraries here: 0276 # https://developer.android.com/ndk/guides/cpp-support.html 0277 if libcxx: 0278 definitions["ANDROID_STL"] = libcxx 0279 else: 0280 definitions["ANDROID_STL"] = 'none' 0281 0282 logger.info("Setting Cross build flags: %s" 0283 % ", ".join(["%s=%s" % (k, v) for k, v in definitions.items()])) 0284 return definitions 0285 0286 def _get_make_program_definition(self): 0287 make_program = os.getenv("CONAN_MAKE_PROGRAM") or self._make_program 0288 if make_program: 0289 if not tools.which(make_program): 0290 self._output.warn("The specified make program '%s' cannot be found and will be " 0291 "ignored" % make_program) 0292 else: 0293 self._output.info("Using '%s' as CMAKE_MAKE_PROGRAM" % make_program) 0294 return {"CMAKE_MAKE_PROGRAM": make_program} 0295 0296 return {} 0297 0298 def get_definitions(self, cmake_version): 0299 0300 compiler = self._ss("compiler") 0301 compiler_base = self._ss("compiler.base") 0302 compiler_version = self._ss("compiler.version") 0303 arch = self._ss("arch") 0304 os_ = self._ss("os") 0305 libcxx = self._ss("compiler.libcxx") 0306 runtime = self._ss("compiler.runtime") 0307 build_type = self._ss("build_type") 0308 0309 definitions = OrderedDict() 0310 definitions.update(runtime_definition(runtime)) 0311 definitions.update(build_type_definition(self._forced_build_type, build_type, 0312 self._generator, self._output)) 0313 0314 # don't attempt to override variables set within toolchain 0315 if (tools.is_apple_os(os_) and "CONAN_CMAKE_TOOLCHAIN_FILE" not in os.environ 0316 and "CMAKE_TOOLCHAIN_FILE" not in definitions): 0317 apple_arch = tools.to_apple_arch(arch) 0318 if apple_arch: 0319 definitions["CMAKE_OSX_ARCHITECTURES"] = apple_arch 0320 # xcrun is only available on macOS, otherwise it's cross-compiling and it needs to be 0321 # set within CMake toolchain. also, if SDKROOT is set, CMake will use it, and it's not 0322 # needed to run xcrun. 0323 if platform.system() == "Darwin" and "SDKROOT" not in os.environ: 0324 sdk_path = tools.XCRun(self._conanfile.settings).sdk_path 0325 if sdk_path: 0326 definitions["CMAKE_OSX_SYSROOT"] = sdk_path 0327 0328 definitions.update(self._cmake_cross_build_defines(cmake_version)) 0329 definitions.update(self._get_cpp_standard_vars()) 0330 0331 definitions.update(in_local_cache_definition(self._conanfile.in_local_cache)) 0332 0333 if compiler: 0334 definitions["CONAN_COMPILER"] = compiler 0335 if compiler_version: 0336 definitions["CONAN_COMPILER_VERSION"] = str(compiler_version) 0337 0338 # C, CXX, LINK FLAGS 0339 if compiler == "Visual Studio" or compiler_base == "Visual Studio": 0340 if self._parallel: 0341 flag = parallel_compiler_cl_flag(output=self._output) 0342 definitions['CONAN_CXX_FLAGS'] = flag 0343 definitions['CONAN_C_FLAGS'] = flag 0344 else: # arch_flag is only set for non Visual Studio 0345 arch_flag = architecture_flag(self._conanfile.settings) 0346 if arch_flag: 0347 definitions['CONAN_CXX_FLAGS'] = arch_flag 0348 definitions['CONAN_SHARED_LINKER_FLAGS'] = arch_flag 0349 definitions['CONAN_C_FLAGS'] = arch_flag 0350 if self._set_cmake_flags: 0351 definitions['CMAKE_CXX_FLAGS'] = arch_flag 0352 definitions['CMAKE_SHARED_LINKER_FLAGS'] = arch_flag 0353 definitions['CMAKE_C_FLAGS'] = arch_flag 0354 0355 if libcxx: 0356 definitions["CONAN_LIBCXX"] = libcxx 0357 0358 # Shared library 0359 try: 0360 definitions["BUILD_SHARED_LIBS"] = "ON" if self._conanfile.options.shared else "OFF" 0361 except ConanException: 0362 pass 0363 0364 # Install to package folder 0365 try: 0366 if self._conanfile.package_folder: 0367 definitions["CMAKE_INSTALL_PREFIX"] = self._conanfile.package_folder 0368 definitions["CMAKE_INSTALL_BINDIR"] = DEFAULT_BIN 0369 definitions["CMAKE_INSTALL_SBINDIR"] = DEFAULT_BIN 0370 definitions["CMAKE_INSTALL_LIBEXECDIR"] = DEFAULT_BIN 0371 definitions["CMAKE_INSTALL_LIBDIR"] = DEFAULT_LIB 0372 definitions["CMAKE_INSTALL_INCLUDEDIR"] = DEFAULT_INCLUDE 0373 definitions["CMAKE_INSTALL_OLDINCLUDEDIR"] = DEFAULT_INCLUDE 0374 definitions["CMAKE_INSTALL_DATAROOTDIR"] = DEFAULT_SHARE 0375 except AttributeError: 0376 pass 0377 0378 # fpic 0379 if not str(os_).startswith("Windows"): 0380 fpic = self._conanfile.options.get_safe("fPIC") 0381 if fpic is not None: 0382 shared = self._conanfile.options.get_safe("shared") 0383 fpic_value = "ON" if (fpic or shared) else "OFF" 0384 definitions["CONAN_CMAKE_POSITION_INDEPENDENT_CODE"] = fpic_value 0385 0386 # Adjust automatically the module path in case the conanfile is using the 0387 # cmake_find_package or cmake_find_package_multi 0388 install_folder = self._conanfile.install_folder.replace("\\", "/") 0389 if "cmake_find_package" in self._conanfile.generators: 0390 definitions["CMAKE_MODULE_PATH"] = install_folder 0391 0392 if ("cmake_find_package_multi" in self._conanfile.generators 0393 or "CMakeDeps" in self._conanfile.generators): 0394 # The cmake_find_package_multi only works with targets and generates XXXConfig.cmake 0395 # that require the prefix path and the module path 0396 definitions["CMAKE_PREFIX_PATH"] = install_folder 0397 definitions["CMAKE_MODULE_PATH"] = install_folder 0398 0399 definitions.update(self._get_make_program_definition()) 0400 0401 # Disable CMake export registry #3070 (CMake installing modules in user home's) 0402 definitions["CMAKE_EXPORT_NO_PACKAGE_REGISTRY"] = "ON" 0403 return definitions