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