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 ""