import copy import os from conans.client.build.compiler_flags import build_type_define, build_type_flags, format_defines, \ include_path_option, parallel_compiler_cl_flag, visual_runtime from conans.client.build.cppstd_flags import cppstd_from_settings, \ cppstd_flag_new as cppstd_flag from conans.client.tools.files import VALID_LIB_EXTENSIONS class VisualStudioBuildEnvironment(object): """ - LIB: library paths with semicolon separator - CL: /I (include paths) - _LINK_: linker options and libraries - UseEnv: True https://github.com/conan-io/conan/pull/4583 https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx https://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx https://msdn.microsoft.com/en-us/library/9s7c9wdw.aspx https://msdn.microsoft.com/en-us/library/6y6t9esh.aspx """ def __init__(self, conanfile, with_build_type_flags=True): """ :param conanfile: ConanFile instance """ self._with_build_type_flags = with_build_type_flags self._conanfile = conanfile self._settings = conanfile.settings self._deps_cpp_info = conanfile.deps_cpp_info self._runtime = self._settings.get_safe("compiler.runtime") self.include_paths = conanfile.deps_cpp_info.include_paths self.lib_paths = conanfile.deps_cpp_info.lib_paths self.defines = copy.copy(conanfile.deps_cpp_info.defines) self.flags = self._configure_flags() self.cxx_flags = copy.copy(self._deps_cpp_info.cxxflags) self.link_flags = self._configure_link_flags() self.libs = conanfile.deps_cpp_info.libs self.std = self._std_cpp() self.parallel = False def _configure_link_flags(self): ret = copy.copy(self._deps_cpp_info.exelinkflags) ret.extend(self._deps_cpp_info.sharedlinkflags) return ret def _configure_flags(self): ret = copy.copy(self._deps_cpp_info.cflags) ret.extend(vs_build_type_flags(self._settings, with_flags=self._with_build_type_flags)) return ret def _get_cl_list(self, quotes=True): # FIXME: It should be managed with the compiler_flags module # But need further investigation about the quotes and so on, so better to not break anything if quotes: ret = ['%s"%s"' % (include_path_option, lib) for lib in self.include_paths] else: ret = ['%s%s' % (include_path_option, lib) for lib in self.include_paths] runtime = visual_runtime(self._runtime) if runtime: ret.append(runtime) ret.extend(format_defines(self.defines)) ret.extend(self.flags) ret.extend(self.cxx_flags) if self.parallel: # Build source in parallel ret.append(parallel_compiler_cl_flag(output=self._conanfile.output)) if self.std: ret.append(self.std) return ret def _get_link_list(self): # FIXME: Conan 2.0. The libs are being added twice to visual_studio # one in the conanbuildinfo.props, and the other in the env-vars def format_lib(lib): ext = os.path.splitext(lib)[1] return lib if ext in VALID_LIB_EXTENSIONS else '%s.lib' % lib ret = [flag for flag in self.link_flags] # copy ret.extend([format_lib(lib) for lib in self.libs]) return ret @property def vars(self): """Used in conanfile with environment_append""" flags = self._get_cl_list() link_flags = self._get_link_list() cl_args = " ".join(flags) + _environ_value_prefix("CL") link_args = " ".join(link_flags) lib_paths = (";".join(['%s' % lib for lib in self.lib_paths]) + _environ_value_prefix("LIB", ";")) return {"CL": cl_args, "LIB": lib_paths, "_LINK_": link_args, "UseEnv": "True"} @property def vars_dict(self): """Used in virtualbuildenvironment""" # Here we do not quote the include paths, it's going to be used by virtual environment cl = self._get_cl_list(quotes=False) link = self._get_link_list() lib = [lib for lib in self.lib_paths] # copy if os.environ.get("CL", None): cl.append(os.environ.get("CL")) if os.environ.get("LIB", None): lib.append(os.environ.get("LIB")) if os.environ.get("_LINK_", None): link.append(os.environ.get("_LINK_")) ret = {"CL": cl, "LIB": lib, "_LINK_": link, "UseEnv": "True"} return ret def _std_cpp(self): return vs_std_cpp(self._settings) def vs_build_type_flags(settings, with_flags=True): build_type = settings.get_safe("build_type") ret = [] btd = build_type_define(build_type=build_type) if btd: ret.extend(format_defines([btd])) if with_flags: # When using to build a vs project we don't want to adjust these flags btfs = build_type_flags(settings) if btfs: ret.extend(btfs) return ret def vs_std_cpp(settings): cppstd = cppstd_from_settings(settings) if settings.get_safe("compiler") == "Visual Studio" and cppstd: flag = cppstd_flag(settings) return flag return None def _environ_value_prefix(var_name, prefix=" "): if os.environ.get(var_name, ""): return "%s%s" % (prefix, os.environ.get(var_name, "")) else: return ""