conans/client/build/cmake_flags.py
import os
import platform
from collections import OrderedDict
from conans.client import tools
from conans.client.build.compiler_flags import architecture_flag, parallel_compiler_cl_flag
from conans.client.build.cppstd_flags import cppstd_from_settings, cppstd_flag_new as cppstd_flag
from conans.client.tools import cross_building, Version
from conans.client.tools.apple import is_apple_os
from conans.client.tools.oss import get_cross_building_settings
from conans.errors import ConanException
from conans.model.build_info import DEFAULT_BIN, DEFAULT_INCLUDE, DEFAULT_LIB, DEFAULT_SHARE
from conans.util.env_reader import get_env
from conans.util.log import logger
verbose_definition_name = "CMAKE_VERBOSE_MAKEFILE"
cmake_install_prefix_var_name = "CMAKE_INSTALL_PREFIX"
runtime_definition_var_name = "CONAN_LINK_RUNTIME"
cmake_in_local_cache_var_name = "CONAN_IN_LOCAL_CACHE"
def get_toolset(settings, generator):
compiler = settings.get_safe("compiler")
compiler_base = settings.get_safe("compiler.base")
if compiler == "Visual Studio":
subs_toolset = settings.get_safe("compiler.toolset")
if subs_toolset:
return subs_toolset
elif compiler == "intel" and compiler_base == "Visual Studio" and "Visual" in generator:
compiler_version = settings.get_safe("compiler.version")
if compiler_version:
compiler_version = compiler_version if "." in compiler_version else \
"%s.0" % compiler_version
return "Intel C++ Compiler " + compiler_version
return None
def get_generator(conanfile):
# Returns the name of the generator to be used by CMake
if "CONAN_CMAKE_GENERATOR" in os.environ:
return os.environ["CONAN_CMAKE_GENERATOR"]
compiler = conanfile.settings.get_safe("compiler")
compiler_base = conanfile.settings.get_safe("compiler.base")
arch = conanfile.settings.get_safe("arch")
compiler_version = conanfile.settings.get_safe("compiler.version")
compiler_base_version = conanfile.settings.get_safe("compiler.base.version")
os_build, _, _, _ = get_cross_building_settings(conanfile)
if not compiler or not compiler_version or not arch:
if os_build == "Windows":
logger.warning("CMake generator could not be deduced from settings")
return None
return "Unix Makefiles"
cmake_years = {'8': '8 2005',
'9': '9 2008',
'10': '10 2010',
'11': '11 2012',
'12': '12 2013',
'14': '14 2015',
'15': '15 2017',
'16': '16 2019',
'17': '17 2022'}
if compiler == "msvc":
if compiler_version is None:
raise ConanException("compiler.version must be defined")
from conan.tools.microsoft.visual import vs_ide_version
vs_version = vs_ide_version(conanfile)
return "Visual Studio %s" % cmake_years[vs_version]
if compiler == "Visual Studio" or compiler_base == "Visual Studio":
version = compiler_base_version or compiler_version
major_version = version.split('.', 1)[0]
_visuals = cmake_years.get(major_version, "UnknownVersion %s" % version)
base = "Visual Studio %s" % _visuals
return base
# The generator depends on the build machine, not the target
if os_build == "Windows" and compiler != "qcc":
return "MinGW Makefiles" # it is valid only under Windows
return "Unix Makefiles"
def get_generator_platform(settings, generator):
# Returns the generator platform to be used by CMake
if "CONAN_CMAKE_GENERATOR_PLATFORM" in os.environ:
return os.environ["CONAN_CMAKE_GENERATOR_PLATFORM"]
compiler = settings.get_safe("compiler")
compiler_base = settings.get_safe("compiler.base")
arch = settings.get_safe("arch")
if settings.get_safe("os") == "WindowsCE":
return settings.get_safe("os.platform")
if (compiler == "Visual Studio" or compiler_base == "Visual Studio") and \
generator and "Visual" in generator:
return {"x86": "Win32",
"x86_64": "x64",
"armv7": "ARM",
"armv8": "ARM64"}.get(arch)
return None
def is_multi_configuration(generator):
if not generator:
return False
return "Visual" in generator or "Xcode" in generator or "Multi-Config" in generator
def is_toolset_supported(generator):
# https://cmake.org/cmake/help/v3.14/variable/CMAKE_GENERATOR_TOOLSET.html
if not generator:
return False
return "Visual" in generator or "Xcode" in generator or "Green Hills MULTI" in generator
def is_generator_platform_supported(generator):
# https://cmake.org/cmake/help/v3.14/variable/CMAKE_GENERATOR_PLATFORM.html
if not generator:
return False
return "Visual" in generator or "Green Hills MULTI" in generator
def verbose_definition(value):
return {verbose_definition_name: "ON" if value else "OFF"}
def in_local_cache_definition(value):
return {cmake_in_local_cache_var_name: "ON" if value else "OFF"}
def runtime_definition(runtime):
return {runtime_definition_var_name: "/%s" % runtime} if runtime else {}
def build_type_definition(new_build_type, old_build_type, generator, output):
if new_build_type and new_build_type != old_build_type:
output.warn("Forced CMake build type ('%s') different from the settings build type ('%s')"
% (new_build_type, old_build_type))
build_type = new_build_type or old_build_type
if build_type and not is_multi_configuration(generator):
return {"CMAKE_BUILD_TYPE": build_type}
return {}
class CMakeDefinitionsBuilder(object):
def __init__(self, conanfile, cmake_system_name=True, make_program=None,
parallel=True, generator=None, set_cmake_flags=False,
forced_build_type=None, output=None):
self._conanfile = conanfile
self._forced_cmake_system_name = cmake_system_name
self._make_program = make_program
self._parallel = parallel
self._generator = generator
self._set_cmake_flags = set_cmake_flags
self._forced_build_type = forced_build_type
self._output = output
def _ss(self, setname):
"""safe setting"""
return self._conanfile.settings.get_safe(setname)
def _get_cpp_standard_vars(self):
cppstd = cppstd_from_settings(self._conanfile.settings)
if not cppstd:
return {}
definitions = {}
if cppstd.startswith("gnu"):
definitions["CONAN_CMAKE_CXX_STANDARD"] = cppstd[3:]
definitions["CONAN_CMAKE_CXX_EXTENSIONS"] = "ON"
else:
definitions["CONAN_CMAKE_CXX_STANDARD"] = cppstd
definitions["CONAN_CMAKE_CXX_EXTENSIONS"] = "OFF"
definitions["CONAN_STD_CXX_FLAG"] = cppstd_flag(self._conanfile.settings)
return definitions
def _cmake_cross_build_defines(self, cmake_version):
os_ = self._ss("os")
arch = self._ss("arch")
os_ver_str = "os.api_level" if os_ == "Android" else "os.version"
op_system_version = self._ss(os_ver_str)
env_sn = get_env("CONAN_CMAKE_SYSTEM_NAME", "")
env_sn = {"False": False, "True": True, "": None}.get(env_sn, env_sn)
cmake_system_name = env_sn or self._forced_cmake_system_name
os_build, _, _, _ = get_cross_building_settings(self._conanfile)
compiler = self._ss("compiler")
libcxx = self._ss("compiler.libcxx")
definitions = OrderedDict()
os_ver = get_env("CONAN_CMAKE_SYSTEM_VERSION", op_system_version)
toolchain_file = get_env("CONAN_CMAKE_TOOLCHAIN_FILE", "")
if toolchain_file != "":
logger.info("Setting Cross build toolchain file: %s" % toolchain_file)
definitions["CMAKE_TOOLCHAIN_FILE"] = toolchain_file
return definitions
if cmake_system_name is False:
return definitions
# System name and system version
if cmake_system_name is not True: # String not empty
definitions["CMAKE_SYSTEM_NAME"] = cmake_system_name
else: # detect if we are cross building and the system name and version
skip_x64_x86 = os_ in ['Windows', 'Linux', 'SunOS', 'AIX']
if cross_building(self._conanfile, skip_x64_x86=skip_x64_x86): # We are cross building
apple_system_name = "Darwin" if cmake_version and Version(cmake_version) < Version(
"3.14") or not cmake_version else None
cmake_system_name_map = {"Macos": "Darwin",
"iOS": apple_system_name or "iOS",
"tvOS": apple_system_name or "tvOS",
"watchOS": apple_system_name or "watchOS",
"Neutrino": "QNX",
"": "Generic",
None: "Generic"}
definitions["CMAKE_SYSTEM_NAME"] = cmake_system_name_map.get(os_, os_)
if os_ver:
definitions["CMAKE_SYSTEM_VERSION"] = os_ver
if is_apple_os(os_):
definitions["CMAKE_OSX_DEPLOYMENT_TARGET"] = os_ver
# system processor
cmake_system_processor = os.getenv("CONAN_CMAKE_SYSTEM_PROCESSOR")
if cmake_system_processor:
definitions["CMAKE_SYSTEM_PROCESSOR"] = cmake_system_processor
if definitions: # If enabled cross compile
for env_var in ["CONAN_CMAKE_FIND_ROOT_PATH",
"CONAN_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM",
"CONAN_CMAKE_FIND_ROOT_PATH_MODE_LIBRARY",
"CONAN_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE"]:
value = os.getenv(env_var)
if value:
definitions[env_var] = value
if self._conanfile and self._conanfile.deps_cpp_info.sysroot:
sysroot_path = self._conanfile.deps_cpp_info.sysroot
if sysroot_path:
# Needs to be set here, can't be managed in the cmake generator, CMake needs
# to know about the sysroot before any other thing
definitions["CMAKE_SYSROOT"] = sysroot_path.replace("\\", "/")
cmake_sysroot = os.getenv("CONAN_CMAKE_SYSROOT")
if cmake_sysroot is not None:
definitions["CMAKE_SYSROOT"] = cmake_sysroot.replace("\\", "/")
# Adjust Android stuff
if str(os_) == "Android" and definitions["CMAKE_SYSTEM_NAME"] == "Android":
arch_abi_settings = tools.to_android_abi(arch)
if arch_abi_settings:
definitions["CMAKE_ANDROID_ARCH_ABI"] = arch_abi_settings
definitions["ANDROID_ABI"] = arch_abi_settings
conan_cmake_android_ndk = os.getenv("CONAN_CMAKE_ANDROID_NDK")
if conan_cmake_android_ndk:
definitions["ANDROID_NDK"] = conan_cmake_android_ndk
definitions["ANDROID_PLATFORM"] = "android-%s" % op_system_version
definitions["ANDROID_TOOLCHAIN"] = compiler
# More details about supported stdc++ libraries here:
# https://developer.android.com/ndk/guides/cpp-support.html
if libcxx:
definitions["ANDROID_STL"] = libcxx
else:
definitions["ANDROID_STL"] = 'none'
logger.info("Setting Cross build flags: %s"
% ", ".join(["%s=%s" % (k, v) for k, v in definitions.items()]))
return definitions
def _get_make_program_definition(self):
make_program = os.getenv("CONAN_MAKE_PROGRAM") or self._make_program
if make_program:
if not tools.which(make_program):
self._output.warn("The specified make program '%s' cannot be found and will be "
"ignored" % make_program)
else:
self._output.info("Using '%s' as CMAKE_MAKE_PROGRAM" % make_program)
return {"CMAKE_MAKE_PROGRAM": make_program}
return {}
def get_definitions(self, cmake_version):
compiler = self._ss("compiler")
compiler_base = self._ss("compiler.base")
compiler_version = self._ss("compiler.version")
arch = self._ss("arch")
os_ = self._ss("os")
libcxx = self._ss("compiler.libcxx")
runtime = self._ss("compiler.runtime")
build_type = self._ss("build_type")
definitions = OrderedDict()
definitions.update(runtime_definition(runtime))
definitions.update(build_type_definition(self._forced_build_type, build_type,
self._generator, self._output))
# don't attempt to override variables set within toolchain
if (tools.is_apple_os(os_) and "CONAN_CMAKE_TOOLCHAIN_FILE" not in os.environ
and "CMAKE_TOOLCHAIN_FILE" not in definitions):
apple_arch = tools.to_apple_arch(arch)
if apple_arch:
definitions["CMAKE_OSX_ARCHITECTURES"] = apple_arch
# xcrun is only available on macOS, otherwise it's cross-compiling and it needs to be
# set within CMake toolchain. also, if SDKROOT is set, CMake will use it, and it's not
# needed to run xcrun.
if platform.system() == "Darwin" and "SDKROOT" not in os.environ:
sdk_path = tools.XCRun(self._conanfile.settings).sdk_path
if sdk_path:
definitions["CMAKE_OSX_SYSROOT"] = sdk_path
definitions.update(self._cmake_cross_build_defines(cmake_version))
definitions.update(self._get_cpp_standard_vars())
definitions.update(in_local_cache_definition(self._conanfile.in_local_cache))
if compiler:
definitions["CONAN_COMPILER"] = compiler
if compiler_version:
definitions["CONAN_COMPILER_VERSION"] = str(compiler_version)
# C, CXX, LINK FLAGS
if compiler == "Visual Studio" or compiler_base == "Visual Studio":
if self._parallel:
flag = parallel_compiler_cl_flag(output=self._output)
definitions['CONAN_CXX_FLAGS'] = flag
definitions['CONAN_C_FLAGS'] = flag
else: # arch_flag is only set for non Visual Studio
arch_flag = architecture_flag(self._conanfile.settings)
if arch_flag:
definitions['CONAN_CXX_FLAGS'] = arch_flag
definitions['CONAN_SHARED_LINKER_FLAGS'] = arch_flag
definitions['CONAN_C_FLAGS'] = arch_flag
if self._set_cmake_flags:
definitions['CMAKE_CXX_FLAGS'] = arch_flag
definitions['CMAKE_SHARED_LINKER_FLAGS'] = arch_flag
definitions['CMAKE_C_FLAGS'] = arch_flag
if libcxx:
definitions["CONAN_LIBCXX"] = libcxx
# Shared library
try:
definitions["BUILD_SHARED_LIBS"] = "ON" if self._conanfile.options.shared else "OFF"
except ConanException:
pass
# Install to package folder
try:
if self._conanfile.package_folder:
definitions["CMAKE_INSTALL_PREFIX"] = self._conanfile.package_folder
definitions["CMAKE_INSTALL_BINDIR"] = DEFAULT_BIN
definitions["CMAKE_INSTALL_SBINDIR"] = DEFAULT_BIN
definitions["CMAKE_INSTALL_LIBEXECDIR"] = DEFAULT_BIN
definitions["CMAKE_INSTALL_LIBDIR"] = DEFAULT_LIB
definitions["CMAKE_INSTALL_INCLUDEDIR"] = DEFAULT_INCLUDE
definitions["CMAKE_INSTALL_OLDINCLUDEDIR"] = DEFAULT_INCLUDE
definitions["CMAKE_INSTALL_DATAROOTDIR"] = DEFAULT_SHARE
except AttributeError:
pass
# fpic
if not str(os_).startswith("Windows"):
fpic = self._conanfile.options.get_safe("fPIC")
if fpic is not None:
shared = self._conanfile.options.get_safe("shared")
fpic_value = "ON" if (fpic or shared) else "OFF"
definitions["CONAN_CMAKE_POSITION_INDEPENDENT_CODE"] = fpic_value
# Adjust automatically the module path in case the conanfile is using the
# cmake_find_package or cmake_find_package_multi
install_folder = self._conanfile.install_folder.replace("\\", "/")
if "cmake_find_package" in self._conanfile.generators:
definitions["CMAKE_MODULE_PATH"] = install_folder
if ("cmake_find_package_multi" in self._conanfile.generators
or "CMakeDeps" in self._conanfile.generators):
# The cmake_find_package_multi only works with targets and generates XXXConfig.cmake
# that require the prefix path and the module path
definitions["CMAKE_PREFIX_PATH"] = install_folder
definitions["CMAKE_MODULE_PATH"] = install_folder
definitions.update(self._get_make_program_definition())
# Disable CMake export registry #3070 (CMake installing modules in user home's)
definitions["CMAKE_EXPORT_NO_PACKAGE_REGISTRY"] = "ON"
return definitions