File indexing completed on 2025-02-16 05:12:09

0001 import copy
0002 import os
0003 
0004 from conans.client.build.compiler_flags import build_type_define, build_type_flags, format_defines, \
0005     include_path_option, parallel_compiler_cl_flag, visual_runtime
0006 from conans.client.build.cppstd_flags import cppstd_from_settings, \
0007     cppstd_flag_new as cppstd_flag
0008 from conans.client.tools.files import VALID_LIB_EXTENSIONS
0009 
0010 
0011 class VisualStudioBuildEnvironment(object):
0012     """
0013     - LIB: library paths with semicolon separator
0014     - CL: /I (include paths)
0015     - _LINK_: linker options and libraries
0016     - UseEnv: True https://github.com/conan-io/conan/pull/4583
0017 
0018     https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
0019     https://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx
0020     https://msdn.microsoft.com/en-us/library/9s7c9wdw.aspx
0021     https://msdn.microsoft.com/en-us/library/6y6t9esh.aspx
0022 
0023     """
0024     def __init__(self, conanfile, with_build_type_flags=True):
0025         """
0026         :param conanfile: ConanFile instance
0027         """
0028         self._with_build_type_flags = with_build_type_flags
0029 
0030         self._conanfile = conanfile
0031         self._settings = conanfile.settings
0032         self._deps_cpp_info = conanfile.deps_cpp_info
0033         self._runtime = self._settings.get_safe("compiler.runtime")
0034 
0035         self.include_paths = conanfile.deps_cpp_info.include_paths
0036         self.lib_paths = conanfile.deps_cpp_info.lib_paths
0037         self.defines = copy.copy(conanfile.deps_cpp_info.defines)
0038         self.flags = self._configure_flags()
0039         self.cxx_flags = copy.copy(self._deps_cpp_info.cxxflags)
0040         self.link_flags = self._configure_link_flags()
0041         self.libs = conanfile.deps_cpp_info.libs
0042         self.std = self._std_cpp()
0043         self.parallel = False
0044 
0045     def _configure_link_flags(self):
0046         ret = copy.copy(self._deps_cpp_info.exelinkflags)
0047         ret.extend(self._deps_cpp_info.sharedlinkflags)
0048         return ret
0049 
0050     def _configure_flags(self):
0051         ret = copy.copy(self._deps_cpp_info.cflags)
0052         ret.extend(vs_build_type_flags(self._settings, with_flags=self._with_build_type_flags))
0053         return ret
0054 
0055     def _get_cl_list(self, quotes=True):
0056         # FIXME: It should be managed with the compiler_flags module
0057         # But need further investigation about the quotes and so on, so better to not break anything
0058         if quotes:
0059             ret = ['%s"%s"' % (include_path_option, lib) for lib in self.include_paths]
0060         else:
0061             ret = ['%s%s' % (include_path_option, lib) for lib in self.include_paths]
0062 
0063         runtime = visual_runtime(self._runtime)
0064         if runtime:
0065             ret.append(runtime)
0066 
0067         ret.extend(format_defines(self.defines))
0068         ret.extend(self.flags)
0069         ret.extend(self.cxx_flags)
0070 
0071         if self.parallel:  # Build source in parallel
0072             ret.append(parallel_compiler_cl_flag(output=self._conanfile.output))
0073 
0074         if self.std:
0075             ret.append(self.std)
0076 
0077         return ret
0078 
0079     def _get_link_list(self):
0080         # FIXME: Conan 2.0. The libs are being added twice to visual_studio
0081         # one in the conanbuildinfo.props, and the other in the env-vars
0082         def format_lib(lib):
0083             ext = os.path.splitext(lib)[1]
0084             return lib if ext in VALID_LIB_EXTENSIONS else '%s.lib' % lib
0085 
0086         ret = [flag for flag in self.link_flags]  # copy
0087         ret.extend([format_lib(lib) for lib in self.libs])
0088 
0089         return ret
0090 
0091     @property
0092     def vars(self):
0093         """Used in conanfile with environment_append"""
0094         flags = self._get_cl_list()
0095         link_flags = self._get_link_list()
0096 
0097         cl_args = " ".join(flags) + _environ_value_prefix("CL")
0098         link_args = " ".join(link_flags)
0099         lib_paths = (";".join(['%s' % lib for lib in self.lib_paths]) +
0100                      _environ_value_prefix("LIB", ";"))
0101         return {"CL": cl_args,
0102                 "LIB": lib_paths,
0103                 "_LINK_": link_args,
0104                 "UseEnv": "True"}
0105 
0106     @property
0107     def vars_dict(self):
0108         """Used in virtualbuildenvironment"""
0109         # Here we do not quote the include paths, it's going to be used by virtual environment
0110         cl = self._get_cl_list(quotes=False)
0111         link = self._get_link_list()
0112 
0113         lib = [lib for lib in self.lib_paths]  # copy
0114 
0115         if os.environ.get("CL", None):
0116             cl.append(os.environ.get("CL"))
0117 
0118         if os.environ.get("LIB", None):
0119             lib.append(os.environ.get("LIB"))
0120 
0121         if os.environ.get("_LINK_", None):
0122             link.append(os.environ.get("_LINK_"))
0123 
0124         ret = {"CL": cl,
0125                "LIB": lib,
0126                "_LINK_": link,
0127                "UseEnv": "True"}
0128         return ret
0129 
0130     def _std_cpp(self):
0131         return vs_std_cpp(self._settings)
0132 
0133 
0134 def vs_build_type_flags(settings, with_flags=True):
0135     build_type = settings.get_safe("build_type")
0136     ret = []
0137     btd = build_type_define(build_type=build_type)
0138     if btd:
0139         ret.extend(format_defines([btd]))
0140     if with_flags:
0141         # When using to build a vs project we don't want to adjust these flags
0142         btfs = build_type_flags(settings)
0143         if btfs:
0144             ret.extend(btfs)
0145 
0146     return ret
0147 
0148 
0149 def vs_std_cpp(settings):
0150     cppstd = cppstd_from_settings(settings)
0151     if settings.get_safe("compiler") == "Visual Studio" and cppstd:
0152         flag = cppstd_flag(settings)
0153         return flag
0154     return None
0155 
0156 
0157 def _environ_value_prefix(var_name, prefix=" "):
0158     if os.environ.get(var_name, ""):
0159         return "%s%s" % (prefix, os.environ.get(var_name, ""))
0160     else:
0161         return ""