1import copy 2import os 3 4from conans.client.build.compiler_flags import build_type_define, build_type_flags, format_defines, \ 5 include_path_option, parallel_compiler_cl_flag, visual_runtime 6from conans.client.build.cppstd_flags import cppstd_from_settings, \ 7 cppstd_flag_new as cppstd_flag 8from conans.client.tools.files import VALID_LIB_EXTENSIONS 9 10 11class VisualStudioBuildEnvironment(object): 12 """ 13 - LIB: library paths with semicolon separator 14 - CL: /I (include paths) 15 - _LINK_: linker options and libraries 16 - UseEnv: True https://github.com/conan-io/conan/pull/4583 17 18 https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx 19 https://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx 20 https://msdn.microsoft.com/en-us/library/9s7c9wdw.aspx 21 https://msdn.microsoft.com/en-us/library/6y6t9esh.aspx 22 23 """ 24 def __init__(self, conanfile, with_build_type_flags=True): 25 """ 26 :param conanfile: ConanFile instance 27 """ 28 self._with_build_type_flags = with_build_type_flags 29 30 self._conanfile = conanfile 31 self._settings = conanfile.settings 32 self._deps_cpp_info = conanfile.deps_cpp_info 33 self._runtime = self._settings.get_safe("compiler.runtime") 34 35 self.include_paths = conanfile.deps_cpp_info.include_paths 36 self.lib_paths = conanfile.deps_cpp_info.lib_paths 37 self.defines = copy.copy(conanfile.deps_cpp_info.defines) 38 self.flags = self._configure_flags() 39 self.cxx_flags = copy.copy(self._deps_cpp_info.cxxflags) 40 self.link_flags = self._configure_link_flags() 41 self.libs = conanfile.deps_cpp_info.libs 42 self.std = self._std_cpp() 43 self.parallel = False 44 45 def _configure_link_flags(self): 46 ret = copy.copy(self._deps_cpp_info.exelinkflags) 47 ret.extend(self._deps_cpp_info.sharedlinkflags) 48 return ret 49 50 def _configure_flags(self): 51 ret = copy.copy(self._deps_cpp_info.cflags) 52 ret.extend(vs_build_type_flags(self._settings, with_flags=self._with_build_type_flags)) 53 return ret 54 55 def _get_cl_list(self, quotes=True): 56 # FIXME: It should be managed with the compiler_flags module 57 # But need further investigation about the quotes and so on, so better to not break anything 58 if quotes: 59 ret = ['%s"%s"' % (include_path_option, lib) for lib in self.include_paths] 60 else: 61 ret = ['%s%s' % (include_path_option, lib) for lib in self.include_paths] 62 63 runtime = visual_runtime(self._runtime) 64 if runtime: 65 ret.append(runtime) 66 67 ret.extend(format_defines(self.defines)) 68 ret.extend(self.flags) 69 ret.extend(self.cxx_flags) 70 71 if self.parallel: # Build source in parallel 72 ret.append(parallel_compiler_cl_flag(output=self._conanfile.output)) 73 74 if self.std: 75 ret.append(self.std) 76 77 return ret 78 79 def _get_link_list(self): 80 # FIXME: Conan 2.0. The libs are being added twice to visual_studio 81 # one in the conanbuildinfo.props, and the other in the env-vars 82 def format_lib(lib): 83 ext = os.path.splitext(lib)[1] 84 return lib if ext in VALID_LIB_EXTENSIONS else '%s.lib' % lib 85 86 ret = [flag for flag in self.link_flags] # copy 87 ret.extend([format_lib(lib) for lib in self.libs]) 88 89 return ret 90 91 @property 92 def vars(self): 93 """Used in conanfile with environment_append""" 94 flags = self._get_cl_list() 95 link_flags = self._get_link_list() 96 97 cl_args = " ".join(flags) + _environ_value_prefix("CL") 98 link_args = " ".join(link_flags) 99 lib_paths = (";".join(['%s' % lib for lib in self.lib_paths]) + 100 _environ_value_prefix("LIB", ";")) 101 return {"CL": cl_args, 102 "LIB": lib_paths, 103 "_LINK_": link_args, 104 "UseEnv": "True"} 105 106 @property 107 def vars_dict(self): 108 """Used in virtualbuildenvironment""" 109 # Here we do not quote the include paths, it's going to be used by virtual environment 110 cl = self._get_cl_list(quotes=False) 111 link = self._get_link_list() 112 113 lib = [lib for lib in self.lib_paths] # copy 114 115 if os.environ.get("CL", None): 116 cl.append(os.environ.get("CL")) 117 118 if os.environ.get("LIB", None): 119 lib.append(os.environ.get("LIB")) 120 121 if os.environ.get("_LINK_", None): 122 link.append(os.environ.get("_LINK_")) 123 124 ret = {"CL": cl, 125 "LIB": lib, 126 "_LINK_": link, 127 "UseEnv": "True"} 128 return ret 129 130 def _std_cpp(self): 131 return vs_std_cpp(self._settings) 132 133 134def vs_build_type_flags(settings, with_flags=True): 135 build_type = settings.get_safe("build_type") 136 ret = [] 137 btd = build_type_define(build_type=build_type) 138 if btd: 139 ret.extend(format_defines([btd])) 140 if with_flags: 141 # When using to build a vs project we don't want to adjust these flags 142 btfs = build_type_flags(settings) 143 if btfs: 144 ret.extend(btfs) 145 146 return ret 147 148 149def vs_std_cpp(settings): 150 cppstd = cppstd_from_settings(settings) 151 if settings.get_safe("compiler") == "Visual Studio" and cppstd: 152 flag = cppstd_flag(settings) 153 return flag 154 return None 155 156 157def _environ_value_prefix(var_name, prefix=" "): 158 if os.environ.get(var_name, ""): 159 return "%s%s" % (prefix, os.environ.get(var_name, "")) 160 else: 161 return "" 162