File indexing completed on 2025-02-16 05:12:09
0001 import copy 0002 import os 0003 import platform 0004 0005 from conans.client.build import join_arguments 0006 from conans.client.build.compiler_flags import (architecture_flag, build_type_define, 0007 build_type_flags, format_defines, 0008 format_include_paths, format_libraries, 0009 format_library_paths, libcxx_define, libcxx_flag, 0010 pic_flag, rpath_flags, sysroot_flag, 0011 format_frameworks, format_framework_paths) 0012 from conans.client.build.cppstd_flags import cppstd_from_settings, \ 0013 cppstd_flag_new as cppstd_flag 0014 from conans.client import tools 0015 from conans.client.tools.env import environment_append 0016 from conans.client.tools.oss import OSInfo, args_to_string, cpu_count, cross_building, \ 0017 detected_architecture, detected_os, get_gnu_triplet, get_target_os_arch, get_build_os_arch 0018 from conans.client.tools.win import unix_path 0019 from conans.errors import ConanException 0020 from conans.model.build_info import DEFAULT_BIN, DEFAULT_INCLUDE, DEFAULT_LIB, DEFAULT_SHARE 0021 from conans.util.conan_v2_mode import conan_v2_error 0022 from conans.util.files import get_abs_path 0023 0024 0025 class AutoToolsBuildEnvironment(object): 0026 """ 0027 - CPPFLAGS (C-PreProcesor-Flags NOT related with c++) (-I -D) 0028 - CFLAGS (not CPPFLAGS nor LDFLAGS, used for optimization or debugging) 0029 - CXXFLAGS (the CFLAGS for c++) 0030 - LDFLAGS (-L, others like -m64 -m32) linker 0031 """ 0032 0033 def __init__(self, conanfile, win_bash=False, include_rpath_flags=False): 0034 """ 0035 FIXME: include_rpath_flags CONAN 2.0 to default True? Could break many packages in center 0036 """ 0037 self._conanfile = conanfile 0038 self._win_bash = win_bash 0039 self._include_rpath_flags = include_rpath_flags 0040 self.subsystem = OSInfo().detect_windows_subsystem() if self._win_bash else None 0041 self._deps_cpp_info = conanfile.deps_cpp_info 0042 self._os = conanfile.settings.get_safe("os") 0043 self._os_version = conanfile.settings.get_safe("os.version") 0044 self._os_sdk = conanfile.settings.get_safe("os.sdk") 0045 self._os_subsystem = conanfile.settings.get_safe("os.subsystem") 0046 self._arch = conanfile.settings.get_safe("arch") 0047 self._os_target, self._arch_target = get_target_os_arch(conanfile) 0048 0049 self._build_type = conanfile.settings.get_safe("build_type") 0050 0051 self._compiler = conanfile.settings.get_safe("compiler") 0052 conan_v2_error("compiler setting should be defined.", not self._compiler) 0053 0054 self._compiler_version = conanfile.settings.get_safe("compiler.version") 0055 self._compiler_runtime = conanfile.settings.get_safe("compiler.runtime") 0056 self._libcxx = conanfile.settings.get_safe("compiler.libcxx") 0057 self._cppstd = cppstd_from_settings(conanfile.settings) 0058 0059 # Set the generic objects before mapping to env vars to let the user 0060 # alter some value 0061 self.libs = list(self._deps_cpp_info.libs) 0062 self.libs.extend(list(self._deps_cpp_info.system_libs)) 0063 self.include_paths = list(self._deps_cpp_info.include_paths) 0064 self.library_paths = list(self._deps_cpp_info.lib_paths) 0065 0066 self.defines = self._configure_defines() 0067 # Will go to CFLAGS and CXXFLAGS ["-m64" "-m32", "-g", "-s"] 0068 self.flags = self._configure_flags() 0069 # Only c++ flags [-stdlib, -library], will go to CXXFLAGS 0070 self.cxx_flags = self._configure_cxx_flags() 0071 # cpp standard 0072 self.cppstd_flag = cppstd_flag(conanfile.settings) 0073 # Not -L flags, ["-m64" "-m32"] 0074 self.link_flags = self._configure_link_flags() # TEST! 0075 # Precalculate -fPIC 0076 self.fpic = self._configure_fpic() 0077 0078 # Precalculate build, host, target triplets 0079 self.build, self.host, self.target = self._get_host_build_target_flags() 0080 0081 def _configure_fpic(self): 0082 if not str(self._os).startswith("Windows"): 0083 fpic = self._conanfile.options.get_safe("fPIC") 0084 if fpic is not None: 0085 shared = self._conanfile.options.get_safe("shared") 0086 return True if (fpic or shared) else None 0087 0088 def _get_host_build_target_flags(self): 0089 """Based on google search for build/host triplets, it could need a lot 0090 and complex verification""" 0091 0092 if self._os_target and self._arch_target: 0093 try: 0094 target = get_gnu_triplet(self._os_target, self._arch_target, self._compiler) 0095 except ConanException as exc: 0096 self._conanfile.output.warn(str(exc)) 0097 target = None 0098 else: 0099 target = None 0100 0101 if hasattr(self._conanfile, 'settings_build'): 0102 os_build, arch_build = get_build_os_arch(self._conanfile) 0103 else: 0104 # FIXME: Why not use 'os_build' and 'arch_build' from conanfile.settings? 0105 os_build = detected_os() or platform.system() 0106 arch_build = detected_architecture() or platform.machine() 0107 0108 if os_build is None or arch_build is None or self._arch is None or self._os is None: 0109 return False, False, target 0110 0111 if not cross_building(self._conanfile, os_build, arch_build): 0112 return False, False, target 0113 0114 try: 0115 build = get_gnu_triplet(os_build, arch_build, self._compiler) 0116 except ConanException as exc: 0117 self._conanfile.output.warn(str(exc)) 0118 build = None 0119 try: 0120 host = get_gnu_triplet(self._os, self._arch, self._compiler) 0121 except ConanException as exc: 0122 self._conanfile.output.warn(str(exc)) 0123 host = None 0124 return build, host, target 0125 0126 def configure(self, configure_dir=None, args=None, build=None, host=None, target=None, 0127 pkg_config_paths=None, vars=None, use_default_install_dirs=True): 0128 """ 0129 :param pkg_config_paths: Optional paths to locate the *.pc files 0130 :param configure_dir: Absolute or relative path to the configure script 0131 :param args: Optional arguments to pass to configure. 0132 :param build: In which system the program will be built. "False" skips the --build flag 0133 :param host: In which system the generated program will run. "False" skips the --host flag 0134 :param target: This option is only used to build a cross-compiling toolchain. 0135 "False" skips the --target flag 0136 When the tool chain generates executable program, in which target system 0137 the program will run. 0138 0139 http://jingfenghanmax.blogspot.com.es/2010/09/configure-with-host-target-and-build.html 0140 https://gcc.gnu.org/onlinedocs/gccint/Configure-Terms.html 0141 :param use_default_install_dirs: Use or not the defaulted installation dirs 0142 0143 """ 0144 if not self._conanfile.should_configure: 0145 return 0146 if configure_dir: 0147 configure_dir = configure_dir.rstrip("/") 0148 else: 0149 configure_dir = "." 0150 0151 triplet_args = [] 0152 0153 if build is not False: # Skipped by user 0154 if build or self.build: # User specified value or automatic 0155 triplet_args.append("--build=%s" % (build or self.build)) 0156 0157 if host is not False: # Skipped by user 0158 if host or self.host: # User specified value or automatic 0159 triplet_args.append("--host=%s" % (host or self.host)) 0160 0161 if target is not False: # Skipped by user 0162 if target or self.target: # User specified value or automatic 0163 triplet_args.append("--target=%s" % (target or self.target)) 0164 0165 if pkg_config_paths: 0166 pkg_env = {"PKG_CONFIG_PATH": 0167 [os.pathsep.join(get_abs_path(f, self._conanfile.install_folder) 0168 for f in pkg_config_paths)]} 0169 else: 0170 # If we are using pkg_config generator automate the pcs location, otherwise it could 0171 # read wrong files 0172 pkg_env = {"PKG_CONFIG_PATH": [self._conanfile.install_folder]} \ 0173 if "pkg_config" in self._conanfile.generators else None 0174 0175 configure_dir = self._adjust_path(configure_dir) 0176 0177 if self._conanfile.package_folder is not None: 0178 if not args: 0179 args = ["--prefix=%s" % self._conanfile.package_folder.replace("\\", "/")] 0180 elif not self._is_flag_in_args("prefix", args): 0181 args.append("--prefix=%s" % self._conanfile.package_folder.replace("\\", "/")) 0182 0183 all_flags = ["bindir", "sbindir", "libexecdir", "libdir", "includedir", "oldincludedir", 0184 "datarootdir"] 0185 help_output = self._configure_help_output(configure_dir) 0186 available_flags = [flag for flag in all_flags if "--%s" % flag in help_output] 0187 0188 if use_default_install_dirs: 0189 for varname in ["bindir", "sbindir", "libexecdir"]: 0190 if self._valid_configure_flag(varname, args, available_flags): 0191 args.append("--%s=${prefix}/%s" % (varname, DEFAULT_BIN)) 0192 if self._valid_configure_flag("libdir", args, available_flags): 0193 args.append("--libdir=${prefix}/%s" % DEFAULT_LIB) 0194 for varname in ["includedir", "oldincludedir"]: 0195 if self._valid_configure_flag(varname, args, available_flags): 0196 args.append("--%s=${prefix}/%s" % (varname, DEFAULT_INCLUDE)) 0197 if self._valid_configure_flag("datarootdir", args, available_flags): 0198 args.append("--datarootdir=${prefix}/%s" % DEFAULT_SHARE) 0199 0200 with environment_append(pkg_env): 0201 with environment_append(vars or self.vars): 0202 command = '%s/configure %s %s' % (configure_dir, args_to_string(args), 0203 " ".join(triplet_args)) 0204 self._conanfile.output.info("Calling:\n > %s" % command) 0205 self._conanfile.run(command, win_bash=self._win_bash, subsystem=self.subsystem) 0206 0207 def _configure_help_output(self, configure_path): 0208 from six import StringIO # Python 2 and 3 compatible 0209 mybuf = StringIO() 0210 try: 0211 self._conanfile.run("%s/configure --help" % configure_path, win_bash=self._win_bash, 0212 output=mybuf) 0213 except ConanException as e: 0214 self._conanfile.output.warn("Error running `configure --help`: %s" % e) 0215 return "" 0216 return mybuf.getvalue() 0217 0218 def _adjust_path(self, path): 0219 if self._win_bash: 0220 path = unix_path(path, path_flavor=self.subsystem) 0221 return '"%s"' % path if " " in path else path 0222 0223 @staticmethod 0224 def _valid_configure_flag(varname, args, available_flags): 0225 return not AutoToolsBuildEnvironment._is_flag_in_args(varname, args) and \ 0226 varname in available_flags 0227 0228 @staticmethod 0229 def _is_flag_in_args(varname, args): 0230 flag = "--%s=" % varname 0231 return any([flag in arg for arg in args]) 0232 0233 def make(self, args="", make_program=None, target=None, vars=None): 0234 if not self._conanfile.should_build: 0235 return 0236 conan_v2_error("build_type setting should be defined.", not self._build_type) 0237 make_program = os.getenv("CONAN_MAKE_PROGRAM") or make_program or "make" 0238 with environment_append(vars or self.vars): 0239 str_args = args_to_string(args) 0240 cpu_count_option = (("-j%s" % cpu_count(output=self._conanfile.output)) 0241 if ("-j" not in str_args and "nmake" not in make_program.lower()) 0242 else None) 0243 self._conanfile.run("%s" % join_arguments([make_program, target, str_args, 0244 cpu_count_option]), 0245 win_bash=self._win_bash, subsystem=self.subsystem) 0246 0247 def install(self, args="", make_program=None, vars=None): 0248 if not self._conanfile.should_install: 0249 return 0250 self.make(args=args, make_program=make_program, target="install", vars=vars) 0251 0252 def _configure_link_flags(self): 0253 """Not the -L""" 0254 ret = list(self._deps_cpp_info.sharedlinkflags) 0255 ret.extend(list(self._deps_cpp_info.exelinkflags)) 0256 ret.extend(format_frameworks(self._deps_cpp_info.frameworks, self._conanfile.settings)) 0257 ret.extend(format_framework_paths(self._deps_cpp_info.framework_paths, 0258 self._conanfile.settings)) 0259 arch_flag = architecture_flag(self._conanfile.settings) 0260 if arch_flag: 0261 ret.append(arch_flag) 0262 0263 sysf = sysroot_flag(self._deps_cpp_info.sysroot, self._conanfile.settings, 0264 win_bash=self._win_bash, 0265 subsystem=self.subsystem) 0266 if sysf: 0267 ret.append(sysf) 0268 0269 if self._include_rpath_flags: 0270 os_build, _ = get_build_os_arch(self._conanfile) 0271 if not hasattr(self._conanfile, 'settings_build'): 0272 os_build = os_build or self._os 0273 ret.extend(rpath_flags(self._conanfile.settings, os_build, 0274 self._deps_cpp_info.lib_paths)) 0275 0276 return ret 0277 0278 def _configure_flags(self): 0279 ret = list(self._deps_cpp_info.cflags) 0280 arch_flag = architecture_flag(self._conanfile.settings) 0281 if arch_flag: 0282 ret.append(arch_flag) 0283 btfs = build_type_flags(self._conanfile.settings) 0284 if btfs: 0285 ret.extend(btfs) 0286 srf = sysroot_flag(self._deps_cpp_info.sysroot, 0287 self._conanfile.settings, 0288 win_bash=self._win_bash, 0289 subsystem=self.subsystem) 0290 if srf: 0291 ret.append(srf) 0292 if self._compiler_runtime: 0293 ret.append("-%s" % self._compiler_runtime) 0294 0295 return ret 0296 0297 def _configure_cxx_flags(self): 0298 ret = list(self._deps_cpp_info.cxxflags) 0299 cxxf = libcxx_flag(self._conanfile.settings) 0300 if cxxf: 0301 ret.append(cxxf) 0302 return ret 0303 0304 def _configure_defines(self): 0305 # requires declared defines 0306 ret = list(self._deps_cpp_info.defines) 0307 0308 # Debug definition for GCC 0309 btf = build_type_define(build_type=self._build_type) 0310 if btf: 0311 ret.append(btf) 0312 0313 # CXX11 ABI 0314 abif = libcxx_define(self._conanfile.settings) 0315 if abif: 0316 ret.append(abif) 0317 return ret 0318 0319 def _get_vars(self): 0320 def append(*args): 0321 ret = [] 0322 for arg in args: 0323 if arg: 0324 if isinstance(arg, list): 0325 ret.extend(arg) 0326 else: 0327 ret.append(arg) 0328 return ret 0329 0330 lib_paths = format_library_paths(self.library_paths, 0331 self._conanfile.settings, 0332 win_bash=self._win_bash, 0333 subsystem=self.subsystem) 0334 include_paths = format_include_paths(self.include_paths, 0335 self._conanfile.settings, 0336 win_bash=self._win_bash, 0337 subsystem=self.subsystem) 0338 0339 ld_flags = append(self.link_flags, lib_paths) 0340 cpp_flags = append(include_paths, format_defines(self.defines)) 0341 libs = format_libraries(self.libs, self._conanfile.settings) 0342 0343 tmp_compilation_flags = copy.copy(self.flags) 0344 if self.fpic: 0345 tmp_compilation_flags.append(pic_flag(self._conanfile.settings)) 0346 if tools.is_apple_os(self._os): 0347 concat = " ".join(tmp_compilation_flags) 0348 if os.environ.get("CFLAGS", None): 0349 concat += " " + os.environ.get("CFLAGS", None) 0350 if os.environ.get("CXXFLAGS", None): 0351 concat += " " + os.environ.get("CXXFLAGS", None) 0352 if (self._os_version and "-version-min" not in concat and "-target" not in concat) or \ 0353 self._os_subsystem: 0354 tmp_compilation_flags.append(tools.apple_deployment_target_flag(self._os, 0355 self._os_version, 0356 self._os_sdk, 0357 self._os_subsystem, 0358 self._arch)) 0359 if "-isysroot" not in concat and platform.system() == "Darwin": 0360 isysroot = tools.XCRun(self._conanfile.settings).sdk_path 0361 if isysroot: 0362 tmp_compilation_flags.extend(["-isysroot", isysroot]) 0363 if "-arch" not in concat and self._arch: 0364 apple_arch = tools.to_apple_arch(self._arch) 0365 if apple_arch: 0366 tmp_compilation_flags.extend(["-arch", apple_arch]) 0367 0368 cxx_flags = append(tmp_compilation_flags, self.cxx_flags, self.cppstd_flag) 0369 c_flags = tmp_compilation_flags 0370 0371 return ld_flags, cpp_flags, libs, cxx_flags, c_flags 0372 0373 @property 0374 def vars_dict(self): 0375 0376 ld_flags, cpp_flags, libs, cxx_flags, c_flags = self._get_vars() 0377 0378 if os.environ.get("CPPFLAGS", None): 0379 cpp_flags.append(os.environ.get("CPPFLAGS", None)) 0380 0381 if os.environ.get("CXXFLAGS", None): 0382 cxx_flags.append(os.environ.get("CXXFLAGS", None)) 0383 0384 if os.environ.get("CFLAGS", None): 0385 c_flags.append(os.environ.get("CFLAGS", None)) 0386 0387 if os.environ.get("LDFLAGS", None): 0388 ld_flags.append(os.environ.get("LDFLAGS", None)) 0389 0390 if os.environ.get("LIBS", None): 0391 libs.append(os.environ.get("LIBS", None)) 0392 0393 ret = {"CPPFLAGS": cpp_flags, 0394 "CXXFLAGS": cxx_flags, 0395 "CFLAGS": c_flags, 0396 "LDFLAGS": ld_flags, 0397 "LIBS": libs 0398 } 0399 0400 return ret 0401 0402 @property 0403 def vars(self): 0404 ld_flags, cpp_flags, libs, cxx_flags, c_flags = self._get_vars() 0405 0406 cpp_flags = " ".join(cpp_flags) + _environ_value_prefix("CPPFLAGS") 0407 cxx_flags = " ".join(cxx_flags) + _environ_value_prefix("CXXFLAGS") 0408 cflags = " ".join(c_flags) + _environ_value_prefix("CFLAGS") 0409 ldflags = " ".join(ld_flags) + _environ_value_prefix("LDFLAGS") 0410 libs = " ".join(libs) + _environ_value_prefix("LIBS") 0411 0412 ret = {"CPPFLAGS": cpp_flags.strip(), 0413 "CXXFLAGS": cxx_flags.strip(), 0414 "CFLAGS": cflags.strip(), 0415 "LDFLAGS": ldflags.strip(), 0416 "LIBS": libs.strip() 0417 } 0418 0419 return ret 0420 0421 0422 def _environ_value_prefix(var_name, prefix=" "): 0423 if os.environ.get(var_name, ""): 0424 return "%s%s" % (prefix, os.environ.get(var_name, "")) 0425 else: 0426 return ""