1# Copyright 2014-2016 The Meson development team 2 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6 7# http://www.apache.org/licenses/LICENSE-2.0 8 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import copy 16import os 17import xml.dom.minidom 18import xml.etree.ElementTree as ET 19import uuid 20import typing as T 21from pathlib import Path, PurePath 22 23from . import backends 24from .. import build 25from .. import dependencies 26from .. import mlog 27from .. import compilers 28from ..interpreter import Interpreter 29from ..mesonlib import ( 30 File, MesonException, replace_if_different, OptionKey, version_compare, MachineChoice 31) 32from ..environment import Environment, build_filename 33 34def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]) -> backends.Backend: 35 vs_version = os.getenv('VisualStudioVersion', None) 36 vs_install_dir = os.getenv('VSINSTALLDIR', None) 37 if not vs_install_dir: 38 raise MesonException('Could not detect Visual Studio: Environment variable VSINSTALLDIR is not set!\n' 39 'Are you running meson from the Visual Studio Developer Command Prompt?') 40 # VisualStudioVersion is set since Visual Studio 11.0, but sometimes 41 # vcvarsall.bat doesn't set it, so also use VSINSTALLDIR 42 if vs_version == '11.0' or 'Visual Studio 11' in vs_install_dir: 43 from mesonbuild.backend.vs2012backend import Vs2012Backend 44 return Vs2012Backend(build, interpreter) 45 if vs_version == '12.0' or 'Visual Studio 12' in vs_install_dir: 46 from mesonbuild.backend.vs2013backend import Vs2013Backend 47 return Vs2013Backend(build, interpreter) 48 if vs_version == '14.0' or 'Visual Studio 14' in vs_install_dir: 49 from mesonbuild.backend.vs2015backend import Vs2015Backend 50 return Vs2015Backend(build, interpreter) 51 if vs_version == '15.0' or 'Visual Studio 17' in vs_install_dir or \ 52 'Visual Studio\\2017' in vs_install_dir: 53 from mesonbuild.backend.vs2017backend import Vs2017Backend 54 return Vs2017Backend(build, interpreter) 55 if vs_version == '16.0' or 'Visual Studio 19' in vs_install_dir or \ 56 'Visual Studio\\2019' in vs_install_dir: 57 from mesonbuild.backend.vs2019backend import Vs2019Backend 58 return Vs2019Backend(build, interpreter) 59 if 'Visual Studio 10.0' in vs_install_dir: 60 return Vs2010Backend(build, interpreter) 61 raise MesonException('Could not detect Visual Studio using VisualStudioVersion: {!r} or VSINSTALLDIR: {!r}!\n' 62 'Please specify the exact backend to use.'.format(vs_version, vs_install_dir)) 63 64def split_o_flags_args(args): 65 """ 66 Splits any /O args and returns them. Does not take care of flags overriding 67 previous ones. Skips non-O flag arguments. 68 69 ['/Ox', '/Ob1'] returns ['/Ox', '/Ob1'] 70 ['/Oxj', '/MP'] returns ['/Ox', '/Oj'] 71 """ 72 o_flags = [] 73 for arg in args: 74 if not arg.startswith('/O'): 75 continue 76 flags = list(arg[2:]) 77 # Assume that this one can't be clumped with the others since it takes 78 # an argument itself 79 if 'b' in flags: 80 o_flags.append(arg) 81 else: 82 o_flags += ['/O' + f for f in flags] 83 return o_flags 84 85def generate_guid_from_path(path, path_type): 86 return str(uuid.uuid5(uuid.NAMESPACE_URL, 'meson-vs-' + path_type + ':' + str(path))).upper() 87 88class Vs2010Backend(backends.Backend): 89 def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]): 90 super().__init__(build, interpreter) 91 self.name = 'vs2010' 92 self.project_file_version = '10.0.30319.1' 93 self.platform_toolset = None 94 self.vs_version = '2010' 95 self.windows_target_platform_version = None 96 self.subdirs = {} 97 self.handled_target_deps = {} 98 99 def get_target_private_dir(self, target): 100 return os.path.join(self.get_target_dir(target), target.get_id()) 101 102 def generate_custom_generator_commands(self, target, parent_node): 103 generator_output_files = [] 104 custom_target_include_dirs = [] 105 custom_target_output_files = [] 106 target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)) 107 down = self.target_to_build_root(target) 108 for genlist in target.get_generated_sources(): 109 if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)): 110 for i in genlist.get_outputs(): 111 # Path to the generated source from the current vcxproj dir via the build root 112 ipath = os.path.join(down, self.get_target_dir(genlist), i) 113 custom_target_output_files.append(ipath) 114 idir = self.relpath(self.get_target_dir(genlist), self.get_target_dir(target)) 115 if idir not in custom_target_include_dirs: 116 custom_target_include_dirs.append(idir) 117 else: 118 generator = genlist.get_generator() 119 exe = generator.get_exe() 120 infilelist = genlist.get_inputs() 121 outfilelist = genlist.get_outputs() 122 source_dir = os.path.join(down, self.build_to_src, genlist.subdir) 123 exe_arr = self.build_target_to_cmd_array(exe) 124 idgroup = ET.SubElement(parent_node, 'ItemGroup') 125 for i, curfile in enumerate(infilelist): 126 if len(infilelist) == len(outfilelist): 127 sole_output = os.path.join(target_private_dir, outfilelist[i]) 128 else: 129 sole_output = '' 130 infilename = os.path.join(down, curfile.rel_to_builddir(self.build_to_src)) 131 deps = self.get_custom_target_depend_files(genlist, True) 132 base_args = generator.get_arglist(infilename) 133 outfiles_rel = genlist.get_outputs_for(curfile) 134 outfiles = [os.path.join(target_private_dir, of) for of in outfiles_rel] 135 generator_output_files += outfiles 136 args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output) 137 for x in base_args] 138 args = self.replace_outputs(args, target_private_dir, outfiles_rel) 139 args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()) 140 .replace("@BUILD_DIR@", target_private_dir) 141 for x in args] 142 args = [x.replace("@CURRENT_SOURCE_DIR@", source_dir) for x in args] 143 args = [x.replace("@SOURCE_ROOT@", self.environment.get_source_dir()) 144 .replace("@BUILD_ROOT@", self.environment.get_build_dir()) 145 for x in args] 146 args = [x.replace('\\', '/') for x in args] 147 cmd = exe_arr + self.replace_extra_args(args, genlist) 148 # Always use a wrapper because MSBuild eats random characters when 149 # there are many arguments. 150 tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) 151 cmd, _ = self.as_meson_exe_cmdline( 152 cmd[0], 153 cmd[1:], 154 workdir=tdir_abs, 155 capture=outfiles[0] if generator.capture else None, 156 force_serialize=True 157 ) 158 deps = cmd[-1:] + deps 159 abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) 160 os.makedirs(abs_pdir, exist_ok=True) 161 cbs = ET.SubElement(idgroup, 'CustomBuild', Include=infilename) 162 ET.SubElement(cbs, 'Command').text = ' '.join(self.quote_arguments(cmd)) 163 ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) 164 ET.SubElement(cbs, 'AdditionalInputs').text = ';'.join(deps) 165 return generator_output_files, custom_target_output_files, custom_target_include_dirs 166 167 def generate(self): 168 target_machine = self.interpreter.builtin['target_machine'].cpu_family_method(None, None) 169 if target_machine == '64' or target_machine == 'x86_64': 170 # amd64 or x86_64 171 self.platform = 'x64' 172 elif target_machine == 'x86': 173 # x86 174 self.platform = 'Win32' 175 elif target_machine == 'aarch64' or target_machine == 'arm64': 176 target_cpu = self.interpreter.builtin['target_machine'].cpu_method(None, None) 177 if target_cpu == 'arm64ec': 178 self.platform = 'arm64ec' 179 else: 180 self.platform = 'arm64' 181 elif 'arm' in target_machine.lower(): 182 self.platform = 'ARM' 183 else: 184 raise MesonException('Unsupported Visual Studio platform: ' + target_machine) 185 186 build_machine = self.interpreter.builtin['build_machine'].cpu_family_method(None, None) 187 if build_machine == '64' or build_machine == 'x86_64': 188 # amd64 or x86_64 189 self.build_platform = 'x64' 190 elif build_machine == 'x86': 191 # x86 192 self.build_platform = 'Win32' 193 elif build_machine == 'aarch64' or build_machine == 'arm64': 194 target_cpu = self.interpreter.builtin['build_machine'].cpu_method(None, None) 195 if target_cpu == 'arm64ec': 196 self.build_platform = 'arm64ec' 197 else: 198 self.build_platform = 'arm64' 199 elif 'arm' in build_machine.lower(): 200 self.build_platform = 'ARM' 201 else: 202 raise MesonException('Unsupported Visual Studio platform: ' + build_machine) 203 204 self.buildtype = self.environment.coredata.get_option(OptionKey('buildtype')) 205 self.optimization = self.environment.coredata.get_option(OptionKey('optimization')) 206 self.debug = self.environment.coredata.get_option(OptionKey('debug')) 207 try: 208 self.sanitize = self.environment.coredata.get_option(OptionKey('b_sanitize')) 209 except MesonException: 210 self.sanitize = 'none' 211 sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln') 212 projlist = self.generate_projects() 213 self.gen_testproj('RUN_TESTS', os.path.join(self.environment.get_build_dir(), 'RUN_TESTS.vcxproj')) 214 self.gen_installproj('RUN_INSTALL', os.path.join(self.environment.get_build_dir(), 'RUN_INSTALL.vcxproj')) 215 self.gen_regenproj('REGEN', os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj')) 216 self.generate_solution(sln_filename, projlist) 217 self.generate_regen_info() 218 Vs2010Backend.touch_regen_timestamp(self.environment.get_build_dir()) 219 220 @staticmethod 221 def get_regen_stampfile(build_dir: str) -> None: 222 return os.path.join(os.path.join(build_dir, Environment.private_dir), 'regen.stamp') 223 224 @staticmethod 225 def touch_regen_timestamp(build_dir: str) -> None: 226 with open(Vs2010Backend.get_regen_stampfile(build_dir), 'w', encoding='utf-8'): 227 pass 228 229 def get_vcvars_command(self): 230 has_arch_values = 'VSCMD_ARG_TGT_ARCH' in os.environ and 'VSCMD_ARG_HOST_ARCH' in os.environ 231 232 # Use vcvarsall.bat if we found it. 233 if 'VCINSTALLDIR' in os.environ: 234 vs_version = os.environ['VisualStudioVersion'] \ 235 if 'VisualStudioVersion' in os.environ else None 236 relative_path = 'Auxiliary\\Build\\' if vs_version is not None and vs_version >= '15.0' else '' 237 script_path = os.environ['VCINSTALLDIR'] + relative_path + 'vcvarsall.bat' 238 if os.path.exists(script_path): 239 if has_arch_values: 240 target_arch = os.environ['VSCMD_ARG_TGT_ARCH'] 241 host_arch = os.environ['VSCMD_ARG_HOST_ARCH'] 242 else: 243 target_arch = os.environ.get('Platform', 'x86') 244 host_arch = target_arch 245 arch = host_arch + '_' + target_arch if host_arch != target_arch else target_arch 246 return f'"{script_path}" {arch}' 247 248 # Otherwise try the VS2017 Developer Command Prompt. 249 if 'VS150COMNTOOLS' in os.environ and has_arch_values: 250 script_path = os.environ['VS150COMNTOOLS'] + 'VsDevCmd.bat' 251 if os.path.exists(script_path): 252 return '"%s" -arch=%s -host_arch=%s' % \ 253 (script_path, os.environ['VSCMD_ARG_TGT_ARCH'], os.environ['VSCMD_ARG_HOST_ARCH']) 254 return '' 255 256 def get_obj_target_deps(self, obj_list): 257 result = {} 258 for o in obj_list: 259 if isinstance(o, build.ExtractedObjects): 260 result[o.target.get_id()] = o.target 261 return result.items() 262 263 def get_target_deps(self, t, recursive=False): 264 all_deps = {} 265 for target in t.values(): 266 if isinstance(target, build.CustomTarget): 267 for d in target.get_target_dependencies(): 268 all_deps[d.get_id()] = d 269 elif isinstance(target, build.RunTarget): 270 for d in target.get_dependencies(): 271 all_deps[d.get_id()] = d 272 elif isinstance(target, build.BuildTarget): 273 for ldep in target.link_targets: 274 if isinstance(ldep, build.CustomTargetIndex): 275 all_deps[ldep.get_id()] = ldep.target 276 else: 277 all_deps[ldep.get_id()] = ldep 278 for ldep in target.link_whole_targets: 279 if isinstance(ldep, build.CustomTargetIndex): 280 all_deps[ldep.get_id()] = ldep.target 281 else: 282 all_deps[ldep.get_id()] = ldep 283 for obj_id, objdep in self.get_obj_target_deps(target.objects): 284 all_deps[obj_id] = objdep 285 else: 286 raise MesonException('Unknown target type for target %s' % target) 287 288 for gendep in target.get_generated_sources(): 289 if isinstance(gendep, build.CustomTarget): 290 all_deps[gendep.get_id()] = gendep 291 elif isinstance(gendep, build.CustomTargetIndex): 292 all_deps[gendep.target.get_id()] = gendep.target 293 else: 294 generator = gendep.get_generator() 295 gen_exe = generator.get_exe() 296 if isinstance(gen_exe, build.Executable): 297 all_deps[gen_exe.get_id()] = gen_exe 298 for d in generator.depends: 299 if isinstance(d, build.CustomTargetIndex): 300 all_deps[d.get_id()] = d.target 301 else: 302 all_deps[d.get_id()] = d 303 304 if not t or not recursive: 305 return all_deps 306 ret = self.get_target_deps(all_deps, recursive) 307 ret.update(all_deps) 308 return ret 309 310 def generate_solution_dirs(self, ofile, parents): 311 prj_templ = 'Project("{%s}") = "%s", "%s", "{%s}"\n' 312 iterpaths = reversed(parents) 313 # Skip first path 314 next(iterpaths) 315 for path in iterpaths: 316 if path not in self.subdirs: 317 basename = path.name 318 identifier = generate_guid_from_path(path, 'subdir') 319 # top-level directories have None as their parent_dir 320 parent_dir = path.parent 321 parent_identifier = self.subdirs[parent_dir][0] \ 322 if parent_dir != PurePath('.') else None 323 self.subdirs[path] = (identifier, parent_identifier) 324 prj_line = prj_templ % ( 325 self.environment.coredata.lang_guids['directory'], 326 basename, basename, self.subdirs[path][0]) 327 ofile.write(prj_line) 328 ofile.write('EndProject\n') 329 330 def generate_solution(self, sln_filename, projlist): 331 default_projlist = self.get_build_by_default_targets() 332 sln_filename_tmp = sln_filename + '~' 333 with open(sln_filename_tmp, 'w', encoding='utf-8') as ofile: 334 ofile.write('Microsoft Visual Studio Solution File, Format ' 335 'Version 11.00\n') 336 ofile.write('# Visual Studio ' + self.vs_version + '\n') 337 prj_templ = 'Project("{%s}") = "%s", "%s", "{%s}"\n' 338 for prj in projlist: 339 coredata = self.environment.coredata 340 if coredata.get_option(OptionKey('layout')) == 'mirror': 341 self.generate_solution_dirs(ofile, prj[1].parents) 342 target = self.build.targets[prj[0]] 343 lang = 'default' 344 if hasattr(target, 'compilers') and target.compilers: 345 for lang_out in target.compilers.keys(): 346 lang = lang_out 347 break 348 prj_line = prj_templ % ( 349 self.environment.coredata.lang_guids[lang], 350 prj[0], prj[1], prj[2]) 351 ofile.write(prj_line) 352 target_dict = {target.get_id(): target} 353 # Get recursive deps 354 recursive_deps = self.get_target_deps( 355 target_dict, recursive=True) 356 ofile.write('EndProject\n') 357 for dep, target in recursive_deps.items(): 358 if prj[0] in default_projlist: 359 default_projlist[dep] = target 360 361 test_line = prj_templ % (self.environment.coredata.lang_guids['default'], 362 'RUN_TESTS', 'RUN_TESTS.vcxproj', 363 self.environment.coredata.test_guid) 364 ofile.write(test_line) 365 ofile.write('EndProject\n') 366 regen_line = prj_templ % (self.environment.coredata.lang_guids['default'], 367 'REGEN', 'REGEN.vcxproj', 368 self.environment.coredata.regen_guid) 369 ofile.write(regen_line) 370 ofile.write('EndProject\n') 371 install_line = prj_templ % (self.environment.coredata.lang_guids['default'], 372 'RUN_INSTALL', 'RUN_INSTALL.vcxproj', 373 self.environment.coredata.install_guid) 374 ofile.write(install_line) 375 ofile.write('EndProject\n') 376 ofile.write('Global\n') 377 ofile.write('\tGlobalSection(SolutionConfigurationPlatforms) = ' 378 'preSolution\n') 379 ofile.write('\t\t%s|%s = %s|%s\n' % 380 (self.buildtype, self.platform, self.buildtype, 381 self.platform)) 382 ofile.write('\tEndGlobalSection\n') 383 ofile.write('\tGlobalSection(ProjectConfigurationPlatforms) = ' 384 'postSolution\n') 385 ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % 386 (self.environment.coredata.regen_guid, self.buildtype, 387 self.platform, self.buildtype, self.platform)) 388 ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % 389 (self.environment.coredata.regen_guid, self.buildtype, 390 self.platform, self.buildtype, self.platform)) 391 # Create the solution configuration 392 for p in projlist: 393 if p[3] is MachineChoice.BUILD: 394 config_platform = self.build_platform 395 else: 396 config_platform = self.platform 397 # Add to the list of projects in this solution 398 ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % 399 (p[2], self.buildtype, self.platform, 400 self.buildtype, config_platform)) 401 if p[0] in default_projlist and \ 402 not isinstance(self.build.targets[p[0]], build.RunTarget): 403 # Add to the list of projects to be built 404 ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % 405 (p[2], self.buildtype, self.platform, 406 self.buildtype, config_platform)) 407 ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % 408 (self.environment.coredata.test_guid, self.buildtype, 409 self.platform, self.buildtype, self.platform)) 410 ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % 411 (self.environment.coredata.install_guid, self.buildtype, 412 self.platform, self.buildtype, self.platform)) 413 ofile.write('\tEndGlobalSection\n') 414 ofile.write('\tGlobalSection(SolutionProperties) = preSolution\n') 415 ofile.write('\t\tHideSolutionNode = FALSE\n') 416 ofile.write('\tEndGlobalSection\n') 417 if self.subdirs: 418 ofile.write('\tGlobalSection(NestedProjects) = ' 419 'preSolution\n') 420 for p in projlist: 421 if p[1].parent != PurePath('.'): 422 ofile.write("\t\t{{{}}} = {{{}}}\n".format(p[2], self.subdirs[p[1].parent][0])) 423 for subdir in self.subdirs.values(): 424 if subdir[1]: 425 ofile.write("\t\t{{{}}} = {{{}}}\n".format(subdir[0], subdir[1])) 426 ofile.write('\tEndGlobalSection\n') 427 ofile.write('EndGlobal\n') 428 replace_if_different(sln_filename, sln_filename_tmp) 429 430 def generate_projects(self): 431 startup_project = self.environment.coredata.options[OptionKey('backend_startup_project')].value 432 projlist = [] 433 startup_idx = 0 434 for (i, (name, target)) in enumerate(self.build.targets.items()): 435 if startup_project and startup_project == target.get_basename(): 436 startup_idx = i 437 outdir = Path( 438 self.environment.get_build_dir(), 439 self.get_target_dir(target) 440 ) 441 outdir.mkdir(exist_ok=True, parents=True) 442 fname = name + '.vcxproj' 443 target_dir = PurePath(self.get_target_dir(target)) 444 relname = target_dir / fname 445 projfile_path = outdir / fname 446 proj_uuid = self.environment.coredata.target_guids[name] 447 self.gen_vcxproj(target, str(projfile_path), proj_uuid) 448 projlist.append((name, relname, proj_uuid, target.for_machine)) 449 450 # Put the startup project first in the project list 451 if startup_idx: 452 projlist = [projlist[startup_idx]] + projlist[0:startup_idx] + projlist[startup_idx + 1:-1] 453 454 return projlist 455 456 def split_sources(self, srclist): 457 sources = [] 458 headers = [] 459 objects = [] 460 languages = [] 461 for i in srclist: 462 if self.environment.is_header(i): 463 headers.append(i) 464 elif self.environment.is_object(i): 465 objects.append(i) 466 elif self.environment.is_source(i): 467 sources.append(i) 468 lang = self.lang_from_source_file(i) 469 if lang not in languages: 470 languages.append(lang) 471 elif self.environment.is_library(i): 472 pass 473 else: 474 # Everything that is not an object or source file is considered a header. 475 headers.append(i) 476 return sources, headers, objects, languages 477 478 def target_to_build_root(self, target): 479 if self.get_target_dir(target) == '': 480 return '' 481 482 directories = os.path.normpath(self.get_target_dir(target)).split(os.sep) 483 return os.sep.join(['..'] * len(directories)) 484 485 def quote_arguments(self, arr): 486 return ['"%s"' % i for i in arr] 487 488 def add_project_reference(self, root, include, projid, link_outputs=False): 489 ig = ET.SubElement(root, 'ItemGroup') 490 pref = ET.SubElement(ig, 'ProjectReference', Include=include) 491 ET.SubElement(pref, 'Project').text = '{%s}' % projid 492 if not link_outputs: 493 # Do not link in generated .lib files from dependencies automatically. 494 # We only use the dependencies for ordering and link in the generated 495 # objects and .lib files manually. 496 ET.SubElement(pref, 'LinkLibraryDependencies').text = 'false' 497 498 def add_target_deps(self, root, target): 499 target_dict = {target.get_id(): target} 500 for dep in self.get_target_deps(target_dict).values(): 501 if dep.get_id() in self.handled_target_deps[target.get_id()]: 502 # This dependency was already handled manually. 503 continue 504 relpath = self.get_target_dir_relative_to(dep, target) 505 vcxproj = os.path.join(relpath, dep.get_id() + '.vcxproj') 506 tid = self.environment.coredata.target_guids[dep.get_id()] 507 self.add_project_reference(root, vcxproj, tid) 508 509 def create_basic_crap(self, target, guid): 510 project_name = target.name 511 root = ET.Element('Project', {'DefaultTargets': "Build", 512 'ToolsVersion': '4.0', 513 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) 514 confitems = ET.SubElement(root, 'ItemGroup', {'Label': 'ProjectConfigurations'}) 515 prjconf = ET.SubElement(confitems, 'ProjectConfiguration', 516 {'Include': self.buildtype + '|' + self.platform}) 517 p = ET.SubElement(prjconf, 'Configuration') 518 p.text = self.buildtype 519 pl = ET.SubElement(prjconf, 'Platform') 520 pl.text = self.platform 521 globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') 522 guidelem = ET.SubElement(globalgroup, 'ProjectGuid') 523 guidelem.text = '{%s}' % guid 524 kw = ET.SubElement(globalgroup, 'Keyword') 525 kw.text = self.platform + 'Proj' 526 p = ET.SubElement(globalgroup, 'Platform') 527 p.text = self.platform 528 pname = ET.SubElement(globalgroup, 'ProjectName') 529 pname.text = project_name 530 if self.windows_target_platform_version: 531 ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version 532 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.Default.props') 533 type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') 534 ET.SubElement(type_config, 'ConfigurationType') 535 ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' 536 ET.SubElement(type_config, 'UseOfMfc').text = 'false' 537 if self.platform_toolset: 538 ET.SubElement(type_config, 'PlatformToolset').text = self.platform_toolset 539 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.props') 540 direlem = ET.SubElement(root, 'PropertyGroup') 541 fver = ET.SubElement(direlem, '_ProjectFileVersion') 542 fver.text = self.project_file_version 543 outdir = ET.SubElement(direlem, 'OutDir') 544 outdir.text = '.\\' 545 intdir = ET.SubElement(direlem, 'IntDir') 546 intdir.text = target.get_id() + '\\' 547 tname = ET.SubElement(direlem, 'TargetName') 548 tname.text = target.name 549 return root 550 551 def gen_run_target_vcxproj(self, target, ofname, guid): 552 root = self.create_basic_crap(target, guid) 553 depend_files = self.get_custom_target_depend_files(target) 554 555 if not target.command: 556 # This is an alias target and thus doesn't run any command. It's 557 # enough to emit the references to the other projects for them to 558 # be built/run/..., if necessary. 559 assert isinstance(target, build.AliasTarget) 560 assert len(depend_files) == 0 561 else: 562 assert not isinstance(target, build.AliasTarget) 563 564 target_env = self.get_run_target_env(target) 565 _, _, cmd_raw = self.eval_custom_target_command(target) 566 wrapper_cmd, _ = self.as_meson_exe_cmdline(target.command[0], cmd_raw[1:], 567 force_serialize=True, env=target_env, 568 verbose=True) 569 self.add_custom_build(root, 'run_target', ' '.join(self.quote_arguments(wrapper_cmd)), 570 deps=depend_files) 571 572 # The import is needed even for alias targets, otherwise the build 573 # target isn't defined 574 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') 575 self.add_regen_dependency(root) 576 self.add_target_deps(root, target) 577 self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) 578 579 def gen_custom_target_vcxproj(self, target, ofname, guid): 580 root = self.create_basic_crap(target, guid) 581 # We need to always use absolute paths because our invocation is always 582 # from the target dir, not the build root. 583 target.absolute_paths = True 584 (srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True) 585 depend_files = self.get_custom_target_depend_files(target, True) 586 # Always use a wrapper because MSBuild eats random characters when 587 # there are many arguments. 588 tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) 589 extra_bdeps = target.get_transitive_build_target_deps() 590 wrapper_cmd, _ = self.as_meson_exe_cmdline(target.command[0], cmd[1:], 591 # All targets run from the target dir 592 workdir=tdir_abs, 593 extra_bdeps=extra_bdeps, 594 capture=ofilenames[0] if target.capture else None, 595 feed=srcs[0] if target.feed else None, 596 force_serialize=True, 597 env=target.env) 598 if target.build_always_stale: 599 # Use a nonexistent file to always consider the target out-of-date. 600 ofilenames += [self.nonexistent_file(os.path.join(self.environment.get_scratch_dir(), 601 'outofdate.file'))] 602 self.add_custom_build(root, 'custom_target', ' '.join(self.quote_arguments(wrapper_cmd)), 603 deps=wrapper_cmd[-1:] + srcs + depend_files, outputs=ofilenames, 604 verify_files=not target.build_always_stale) 605 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') 606 self.generate_custom_generator_commands(target, root) 607 self.add_regen_dependency(root) 608 self.add_target_deps(root, target) 609 self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) 610 611 @classmethod 612 def lang_from_source_file(cls, src): 613 ext = src.split('.')[-1] 614 if ext in compilers.c_suffixes: 615 return 'c' 616 if ext in compilers.cpp_suffixes: 617 return 'cpp' 618 raise MesonException('Could not guess language from source file %s.' % src) 619 620 def add_pch(self, pch_sources, lang, inc_cl): 621 if lang in pch_sources: 622 self.use_pch(pch_sources, lang, inc_cl) 623 624 def create_pch(self, pch_sources, lang, inc_cl): 625 pch = ET.SubElement(inc_cl, 'PrecompiledHeader') 626 pch.text = 'Create' 627 self.add_pch_files(pch_sources, lang, inc_cl) 628 629 def use_pch(self, pch_sources, lang, inc_cl): 630 pch = ET.SubElement(inc_cl, 'PrecompiledHeader') 631 pch.text = 'Use' 632 header = self.add_pch_files(pch_sources, lang, inc_cl) 633 pch_include = ET.SubElement(inc_cl, 'ForcedIncludeFiles') 634 pch_include.text = header + ';%(ForcedIncludeFiles)' 635 636 def add_pch_files(self, pch_sources, lang, inc_cl): 637 header = os.path.basename(pch_sources[lang][0]) 638 pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile') 639 # When USING PCHs, MSVC will not do the regular include 640 # directory lookup, but simply use a string match to find the 641 # PCH to use. That means the #include directive must match the 642 # pch_file.text used during PCH CREATION verbatim. 643 # When CREATING a PCH, MSVC will do the include directory 644 # lookup to find the actual PCH header to use. Thus, the PCH 645 # header must either be in the include_directories of the target 646 # or be in the same directory as the PCH implementation. 647 pch_file.text = header 648 pch_out = ET.SubElement(inc_cl, 'PrecompiledHeaderOutputFile') 649 pch_out.text = '$(IntDir)$(TargetName)-%s.pch' % lang 650 return header 651 652 def is_argument_with_msbuild_xml_entry(self, entry): 653 # Remove arguments that have a top level XML entry so 654 # they are not used twice. 655 # FIXME add args as needed. 656 if entry[1:].startswith('fsanitize'): 657 return True 658 return entry[1:].startswith('M') 659 660 def add_additional_options(self, lang, parent_node, file_args): 661 args = [] 662 for arg in file_args[lang].to_native(): 663 if self.is_argument_with_msbuild_xml_entry(arg): 664 continue 665 if arg == '%(AdditionalOptions)': 666 args.append(arg) 667 else: 668 args.append(self.escape_additional_option(arg)) 669 ET.SubElement(parent_node, "AdditionalOptions").text = ' '.join(args) 670 671 def add_preprocessor_defines(self, lang, parent_node, file_defines): 672 defines = [] 673 for define in file_defines[lang]: 674 if define == '%(PreprocessorDefinitions)': 675 defines.append(define) 676 else: 677 defines.append(self.escape_preprocessor_define(define)) 678 ET.SubElement(parent_node, "PreprocessorDefinitions").text = ';'.join(defines) 679 680 def add_include_dirs(self, lang, parent_node, file_inc_dirs): 681 dirs = file_inc_dirs[lang] 682 ET.SubElement(parent_node, "AdditionalIncludeDirectories").text = ';'.join(dirs) 683 684 @staticmethod 685 def has_objects(objects, additional_objects, generated_objects): 686 # Ignore generated objects, those are automatically used by MSBuild because they are part of 687 # the CustomBuild Outputs. 688 return len(objects) + len(additional_objects) > 0 689 690 @staticmethod 691 def add_generated_objects(node, generated_objects): 692 # Do not add generated objects to project file. Those are automatically used by MSBuild, because 693 # they are part of the CustomBuild Outputs. 694 return 695 696 @staticmethod 697 def escape_preprocessor_define(define): 698 # See: https://msdn.microsoft.com/en-us/library/bb383819.aspx 699 table = str.maketrans({'%': '%25', '$': '%24', '@': '%40', 700 "'": '%27', ';': '%3B', '?': '%3F', '*': '%2A', 701 # We need to escape backslash because it'll be un-escaped by 702 # Windows during process creation when it parses the arguments 703 # Basically, this converts `\` to `\\`. 704 '\\': '\\\\'}) 705 return define.translate(table) 706 707 @staticmethod 708 def escape_additional_option(option): 709 # See: https://msdn.microsoft.com/en-us/library/bb383819.aspx 710 table = str.maketrans({'%': '%25', '$': '%24', '@': '%40', 711 "'": '%27', ';': '%3B', '?': '%3F', '*': '%2A', ' ': '%20'}) 712 option = option.translate(table) 713 # Since we're surrounding the option with ", if it ends in \ that will 714 # escape the " when the process arguments are parsed and the starting 715 # " will not terminate. So we escape it if that's the case. I'm not 716 # kidding, this is how escaping works for process args on Windows. 717 if option.endswith('\\'): 718 option += '\\' 719 return f'"{option}"' 720 721 @staticmethod 722 def split_link_args(args): 723 """ 724 Split a list of link arguments into three lists: 725 * library search paths 726 * library filenames (or paths) 727 * other link arguments 728 """ 729 lpaths = [] 730 libs = [] 731 other = [] 732 for arg in args: 733 if arg.startswith('/LIBPATH:'): 734 lpath = arg[9:] 735 # De-dup library search paths by removing older entries when 736 # a new one is found. This is necessary because unlike other 737 # search paths such as the include path, the library is 738 # searched for in the newest (right-most) search path first. 739 if lpath in lpaths: 740 lpaths.remove(lpath) 741 lpaths.append(lpath) 742 elif arg.startswith(('/', '-')): 743 other.append(arg) 744 # It's ok if we miss libraries with non-standard extensions here. 745 # They will go into the general link arguments. 746 elif arg.endswith('.lib') or arg.endswith('.a'): 747 # De-dup 748 if arg not in libs: 749 libs.append(arg) 750 else: 751 other.append(arg) 752 return lpaths, libs, other 753 754 def _get_cl_compiler(self, target): 755 for lang, c in target.compilers.items(): 756 if lang in ('c', 'cpp'): 757 return c 758 # No source files, only objects, but we still need a compiler, so 759 # return a found compiler 760 if len(target.objects) > 0: 761 for lang, c in self.environment.coredata.compilers[target.for_machine].items(): 762 if lang in ('c', 'cpp'): 763 return c 764 raise MesonException('Could not find a C or C++ compiler. MSVC can only build C/C++ projects.') 765 766 def _prettyprint_vcxproj_xml(self, tree, ofname): 767 ofname_tmp = ofname + '~' 768 tree.write(ofname_tmp, encoding='utf-8', xml_declaration=True) 769 770 # ElementTree can not do prettyprinting so do it manually 771 doc = xml.dom.minidom.parse(ofname_tmp) 772 with open(ofname_tmp, 'w', encoding='utf-8') as of: 773 of.write(doc.toprettyxml()) 774 replace_if_different(ofname, ofname_tmp) 775 776 def gen_vcxproj(self, target, ofname, guid): 777 mlog.debug('Generating vcxproj %s.' % target.name) 778 subsystem = 'Windows' 779 self.handled_target_deps[target.get_id()] = [] 780 if isinstance(target, build.Executable): 781 conftype = 'Application' 782 if target.gui_app is not None: 783 if not target.gui_app: 784 subsystem = 'Console' 785 else: 786 # If someone knows how to set the version properly, 787 # please send a patch. 788 subsystem = target.win_subsystem.split(',')[0] 789 elif isinstance(target, build.StaticLibrary): 790 conftype = 'StaticLibrary' 791 elif isinstance(target, build.SharedLibrary): 792 conftype = 'DynamicLibrary' 793 elif isinstance(target, build.CustomTarget): 794 return self.gen_custom_target_vcxproj(target, ofname, guid) 795 elif isinstance(target, build.RunTarget): 796 return self.gen_run_target_vcxproj(target, ofname, guid) 797 else: 798 raise MesonException('Unknown target type for %s' % target.get_basename()) 799 # Prefix to use to access the build root from the vcxproj dir 800 down = self.target_to_build_root(target) 801 # Prefix to use to access the source tree's root from the vcxproj dir 802 proj_to_src_root = os.path.join(down, self.build_to_src) 803 # Prefix to use to access the source tree's subdir from the vcxproj dir 804 proj_to_src_dir = os.path.join(proj_to_src_root, self.get_target_dir(target)) 805 (sources, headers, objects, languages) = self.split_sources(target.sources) 806 if self.is_unity(target): 807 sources = self.generate_unity_files(target, sources) 808 compiler = self._get_cl_compiler(target) 809 build_args = compiler.get_buildtype_args(self.buildtype) 810 build_args += compiler.get_optimization_args(self.optimization) 811 build_args += compiler.get_debug_args(self.debug) 812 build_args += compiler.sanitizer_compile_args(self.sanitize) 813 buildtype_link_args = compiler.get_buildtype_linker_args(self.buildtype) 814 vscrt_type = self.environment.coredata.options[OptionKey('b_vscrt')] 815 project_name = target.name 816 target_name = target.name 817 if target.for_machine is MachineChoice.BUILD: 818 platform = self.build_platform 819 else: 820 platform = self.platform 821 root = ET.Element('Project', {'DefaultTargets': "Build", 822 'ToolsVersion': '4.0', 823 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) 824 confitems = ET.SubElement(root, 'ItemGroup', {'Label': 'ProjectConfigurations'}) 825 prjconf = ET.SubElement(confitems, 'ProjectConfiguration', 826 {'Include': self.buildtype + '|' + platform}) 827 p = ET.SubElement(prjconf, 'Configuration') 828 p.text = self.buildtype 829 pl = ET.SubElement(prjconf, 'Platform') 830 pl.text = platform 831 # Globals 832 globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') 833 guidelem = ET.SubElement(globalgroup, 'ProjectGuid') 834 guidelem.text = '{%s}' % guid 835 kw = ET.SubElement(globalgroup, 'Keyword') 836 kw.text = self.platform + 'Proj' 837 ns = ET.SubElement(globalgroup, 'RootNamespace') 838 ns.text = target_name 839 p = ET.SubElement(globalgroup, 'Platform') 840 p.text = platform 841 pname = ET.SubElement(globalgroup, 'ProjectName') 842 pname.text = project_name 843 if self.windows_target_platform_version: 844 ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version 845 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.Default.props') 846 # Start configuration 847 type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') 848 ET.SubElement(type_config, 'ConfigurationType').text = conftype 849 ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' 850 if self.platform_toolset: 851 ET.SubElement(type_config, 'PlatformToolset').text = self.platform_toolset 852 # FIXME: Meson's LTO support needs to be integrated here 853 ET.SubElement(type_config, 'WholeProgramOptimization').text = 'false' 854 # Let VS auto-set the RTC level 855 ET.SubElement(type_config, 'BasicRuntimeChecks').text = 'Default' 856 # Incremental linking increases code size 857 if '/INCREMENTAL:NO' in buildtype_link_args: 858 ET.SubElement(type_config, 'LinkIncremental').text = 'false' 859 860 # Build information 861 compiles = ET.SubElement(root, 'ItemDefinitionGroup') 862 clconf = ET.SubElement(compiles, 'ClCompile') 863 # CRT type; debug or release 864 if vscrt_type.value == 'from_buildtype': 865 if self.buildtype == 'debug': 866 ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' 867 ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDebugDLL' 868 else: 869 ET.SubElement(type_config, 'UseDebugLibraries').text = 'false' 870 ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDLL' 871 elif vscrt_type.value == 'static_from_buildtype': 872 if self.buildtype == 'debug': 873 ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' 874 ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDebug' 875 else: 876 ET.SubElement(type_config, 'UseDebugLibraries').text = 'false' 877 ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreaded' 878 elif vscrt_type.value == 'mdd': 879 ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' 880 ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDebugDLL' 881 elif vscrt_type.value == 'mt': 882 # FIXME, wrong 883 ET.SubElement(type_config, 'UseDebugLibraries').text = 'false' 884 ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreaded' 885 elif vscrt_type.value == 'mtd': 886 # FIXME, wrong 887 ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' 888 ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDebug' 889 else: 890 ET.SubElement(type_config, 'UseDebugLibraries').text = 'false' 891 ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDLL' 892 # Sanitizers 893 if '/fsanitize=address' in build_args: 894 ET.SubElement(type_config, 'EnableASAN').text = 'true' 895 # Debug format 896 if '/ZI' in build_args: 897 ET.SubElement(clconf, 'DebugInformationFormat').text = 'EditAndContinue' 898 elif '/Zi' in build_args: 899 ET.SubElement(clconf, 'DebugInformationFormat').text = 'ProgramDatabase' 900 elif '/Z7' in build_args: 901 ET.SubElement(clconf, 'DebugInformationFormat').text = 'OldStyle' 902 else: 903 ET.SubElement(clconf, 'DebugInformationFormat').text = 'None' 904 # Runtime checks 905 if '/RTC1' in build_args: 906 ET.SubElement(clconf, 'BasicRuntimeChecks').text = 'EnableFastChecks' 907 elif '/RTCu' in build_args: 908 ET.SubElement(clconf, 'BasicRuntimeChecks').text = 'UninitializedLocalUsageCheck' 909 elif '/RTCs' in build_args: 910 ET.SubElement(clconf, 'BasicRuntimeChecks').text = 'StackFrameRuntimeCheck' 911 # Exception handling has to be set in the xml in addition to the "AdditionalOptions" because otherwise 912 # cl will give warning D9025: overriding '/Ehs' with cpp_eh value 913 if 'cpp' in target.compilers: 914 eh = self.environment.coredata.options[OptionKey('eh', machine=target.for_machine, lang='cpp')] 915 if eh.value == 'a': 916 ET.SubElement(clconf, 'ExceptionHandling').text = 'Async' 917 elif eh.value == 's': 918 ET.SubElement(clconf, 'ExceptionHandling').text = 'SyncCThrow' 919 elif eh.value == 'none': 920 ET.SubElement(clconf, 'ExceptionHandling').text = 'false' 921 else: # 'sc' or 'default' 922 ET.SubElement(clconf, 'ExceptionHandling').text = 'Sync' 923 # End configuration 924 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.props') 925 generated_files, custom_target_output_files, generated_files_include_dirs = self.generate_custom_generator_commands(target, root) 926 (gen_src, gen_hdrs, gen_objs, gen_langs) = self.split_sources(generated_files) 927 (custom_src, custom_hdrs, custom_objs, custom_langs) = self.split_sources(custom_target_output_files) 928 gen_src += custom_src 929 gen_hdrs += custom_hdrs 930 gen_langs += custom_langs 931 # Project information 932 direlem = ET.SubElement(root, 'PropertyGroup') 933 fver = ET.SubElement(direlem, '_ProjectFileVersion') 934 fver.text = self.project_file_version 935 outdir = ET.SubElement(direlem, 'OutDir') 936 outdir.text = '.\\' 937 intdir = ET.SubElement(direlem, 'IntDir') 938 intdir.text = target.get_id() + '\\' 939 tfilename = os.path.splitext(target.get_filename()) 940 ET.SubElement(direlem, 'TargetName').text = tfilename[0] 941 ET.SubElement(direlem, 'TargetExt').text = tfilename[1] 942 943 # Arguments, include dirs, defines for all files in the current target 944 target_args = [] 945 target_defines = [] 946 target_inc_dirs = [] 947 # Arguments, include dirs, defines passed to individual files in 948 # a target; perhaps because the args are language-specific 949 # 950 # file_args is also later split out into defines and include_dirs in 951 # case someone passed those in there 952 file_args = {l: c.compiler_args() for l, c in target.compilers.items()} 953 file_defines = {l: [] for l in target.compilers} 954 file_inc_dirs = {l: [] for l in target.compilers} 955 # The order in which these compile args are added must match 956 # generate_single_compile() and generate_basic_compiler_args() 957 for l, comp in target.compilers.items(): 958 if l in file_args: 959 file_args[l] += compilers.get_base_compile_args( 960 self.get_base_options_for_target(target), comp) 961 file_args[l] += comp.get_option_compile_args( 962 self.environment.coredata.options) 963 964 # Add compile args added using add_project_arguments() 965 for l, args in self.build.projects_args[target.for_machine].get(target.subproject, {}).items(): 966 if l in file_args: 967 file_args[l] += args 968 # Add compile args added using add_global_arguments() 969 # These override per-project arguments 970 for l, args in self.build.global_args[target.for_machine].items(): 971 if l in file_args: 972 file_args[l] += args 973 # Compile args added from the env or cross file: CFLAGS/CXXFLAGS, etc. We want these 974 # to override all the defaults, but not the per-target compile args. 975 for l in file_args.keys(): 976 opts = self.environment.coredata.options[OptionKey('args', machine=target.for_machine, lang=l)] 977 file_args[l] += opts.value 978 for args in file_args.values(): 979 # This is where Visual Studio will insert target_args, target_defines, 980 # etc, which are added later from external deps (see below). 981 args += ['%(AdditionalOptions)', '%(PreprocessorDefinitions)', '%(AdditionalIncludeDirectories)'] 982 # Add custom target dirs as includes automatically, but before 983 # target-specific include dirs. See _generate_single_compile() in 984 # the ninja backend for caveats. 985 args += ['-I' + arg for arg in generated_files_include_dirs] 986 # Add include dirs from the `include_directories:` kwarg on the target 987 # and from `include_directories:` of internal deps of the target. 988 # 989 # Target include dirs should override internal deps include dirs. 990 # This is handled in BuildTarget.process_kwargs() 991 # 992 # Include dirs from internal deps should override include dirs from 993 # external deps and must maintain the order in which they are 994 # specified. Hence, we must reverse so that the order is preserved. 995 # 996 # These are per-target, but we still add them as per-file because we 997 # need them to be looked in first. 998 for d in reversed(target.get_include_dirs()): 999 # reversed is used to keep order of includes 1000 for i in reversed(d.get_incdirs()): 1001 curdir = os.path.join(d.get_curdir(), i) 1002 args.append('-I' + self.relpath(curdir, target.subdir)) # build dir 1003 args.append('-I' + os.path.join(proj_to_src_root, curdir)) # src dir 1004 for i in d.get_extra_build_dirs(): 1005 curdir = os.path.join(d.get_curdir(), i) 1006 args.append('-I' + self.relpath(curdir, target.subdir)) # build dir 1007 # Add per-target compile args, f.ex, `c_args : ['/DFOO']`. We set these 1008 # near the end since these are supposed to override everything else. 1009 for l, args in target.extra_args.items(): 1010 if l in file_args: 1011 file_args[l] += args 1012 # The highest priority includes. In order of directory search: 1013 # target private dir, target build dir, target source dir 1014 for args in file_args.values(): 1015 t_inc_dirs = [self.relpath(self.get_target_private_dir(target), 1016 self.get_target_dir(target))] 1017 if target.implicit_include_directories: 1018 t_inc_dirs += ['.', proj_to_src_dir] 1019 args += ['-I' + arg for arg in t_inc_dirs] 1020 1021 # Split preprocessor defines and include directories out of the list of 1022 # all extra arguments. The rest go into %(AdditionalOptions). 1023 for l, args in file_args.items(): 1024 for arg in args[:]: 1025 if arg.startswith(('-D', '/D')) or arg == '%(PreprocessorDefinitions)': 1026 file_args[l].remove(arg) 1027 # Don't escape the marker 1028 if arg == '%(PreprocessorDefinitions)': 1029 define = arg 1030 else: 1031 define = arg[2:] 1032 # De-dup 1033 if define not in file_defines[l]: 1034 file_defines[l].append(define) 1035 elif arg.startswith(('-I', '/I')) or arg == '%(AdditionalIncludeDirectories)': 1036 file_args[l].remove(arg) 1037 # Don't escape the marker 1038 if arg == '%(AdditionalIncludeDirectories)': 1039 inc_dir = arg 1040 else: 1041 inc_dir = arg[2:] 1042 # De-dup 1043 if inc_dir not in file_inc_dirs[l]: 1044 file_inc_dirs[l].append(inc_dir) 1045 # Add include dirs to target as well so that "Go to Document" works in headers 1046 if inc_dir not in target_inc_dirs: 1047 target_inc_dirs.append(inc_dir) 1048 1049 # Split compile args needed to find external dependencies 1050 # Link args are added while generating the link command 1051 for d in reversed(target.get_external_deps()): 1052 # Cflags required by external deps might have UNIX-specific flags, 1053 # so filter them out if needed 1054 if isinstance(d, dependencies.OpenMPDependency): 1055 ET.SubElement(clconf, 'OpenMPSupport').text = 'true' 1056 else: 1057 d_compile_args = compiler.unix_args_to_native(d.get_compile_args()) 1058 for arg in d_compile_args: 1059 if arg.startswith(('-D', '/D')): 1060 define = arg[2:] 1061 # De-dup 1062 if define in target_defines: 1063 target_defines.remove(define) 1064 target_defines.append(define) 1065 elif arg.startswith(('-I', '/I')): 1066 inc_dir = arg[2:] 1067 # De-dup 1068 if inc_dir not in target_inc_dirs: 1069 target_inc_dirs.append(inc_dir) 1070 else: 1071 target_args.append(arg) 1072 1073 languages += gen_langs 1074 if '/Gw' in build_args: 1075 target_args.append('/Gw') 1076 if len(target_args) > 0: 1077 target_args.append('%(AdditionalOptions)') 1078 ET.SubElement(clconf, "AdditionalOptions").text = ' '.join(target_args) 1079 ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(target_inc_dirs) 1080 target_defines.append('%(PreprocessorDefinitions)') 1081 ET.SubElement(clconf, 'PreprocessorDefinitions').text = ';'.join(target_defines) 1082 ET.SubElement(clconf, 'FunctionLevelLinking').text = 'true' 1083 # Warning level 1084 warning_level = self.get_option_for_target(OptionKey('warning_level'), target) 1085 ET.SubElement(clconf, 'WarningLevel').text = 'Level' + str(1 + int(warning_level)) 1086 if self.get_option_for_target(OptionKey('werror'), target): 1087 ET.SubElement(clconf, 'TreatWarningAsError').text = 'true' 1088 # Optimization flags 1089 o_flags = split_o_flags_args(build_args) 1090 if '/Ox' in o_flags: 1091 ET.SubElement(clconf, 'Optimization').text = 'Full' 1092 elif '/O2' in o_flags: 1093 ET.SubElement(clconf, 'Optimization').text = 'MaxSpeed' 1094 elif '/O1' in o_flags: 1095 ET.SubElement(clconf, 'Optimization').text = 'MinSpace' 1096 elif '/Od' in o_flags: 1097 ET.SubElement(clconf, 'Optimization').text = 'Disabled' 1098 if '/Oi' in o_flags: 1099 ET.SubElement(clconf, 'IntrinsicFunctions').text = 'true' 1100 if '/Ob1' in o_flags: 1101 ET.SubElement(clconf, 'InlineFunctionExpansion').text = 'OnlyExplicitInline' 1102 elif '/Ob2' in o_flags: 1103 ET.SubElement(clconf, 'InlineFunctionExpansion').text = 'AnySuitable' 1104 # Size-preserving flags 1105 if '/Os' in o_flags: 1106 ET.SubElement(clconf, 'FavorSizeOrSpeed').text = 'Size' 1107 else: 1108 ET.SubElement(clconf, 'FavorSizeOrSpeed').text = 'Speed' 1109 # Note: SuppressStartupBanner is /NOLOGO and is 'true' by default 1110 pch_sources = {} 1111 if self.environment.coredata.options.get(OptionKey('b_pch')): 1112 for lang in ['c', 'cpp']: 1113 pch = target.get_pch(lang) 1114 if not pch: 1115 continue 1116 if compiler.id == 'msvc': 1117 if len(pch) == 1: 1118 # Auto generate PCH. 1119 src = os.path.join(down, self.create_msvc_pch_implementation(target, lang, pch[0])) 1120 pch_header_dir = os.path.dirname(os.path.join(proj_to_src_dir, pch[0])) 1121 else: 1122 src = os.path.join(proj_to_src_dir, pch[1]) 1123 pch_header_dir = None 1124 pch_sources[lang] = [pch[0], src, lang, pch_header_dir] 1125 else: 1126 # I don't know whether its relevant but let's handle other compilers 1127 # used with a vs backend 1128 pch_sources[lang] = [pch[0], None, lang, None] 1129 1130 resourcecompile = ET.SubElement(compiles, 'ResourceCompile') 1131 ET.SubElement(resourcecompile, 'PreprocessorDefinitions') 1132 1133 # Linker options 1134 link = ET.SubElement(compiles, 'Link') 1135 extra_link_args = compiler.compiler_args() 1136 # FIXME: Can these buildtype linker args be added as tags in the 1137 # vcxproj file (similar to buildtype compiler args) instead of in 1138 # AdditionalOptions? 1139 extra_link_args += compiler.get_buildtype_linker_args(self.buildtype) 1140 # Generate Debug info 1141 if self.debug: 1142 self.generate_debug_information(link) 1143 else: 1144 ET.SubElement(link, 'GenerateDebugInformation').text = 'false' 1145 if not isinstance(target, build.StaticLibrary): 1146 if isinstance(target, build.SharedModule): 1147 options = self.environment.coredata.options 1148 extra_link_args += compiler.get_std_shared_module_link_args(options) 1149 # Add link args added using add_project_link_arguments() 1150 extra_link_args += self.build.get_project_link_args(compiler, target.subproject, target.for_machine) 1151 # Add link args added using add_global_link_arguments() 1152 # These override per-project link arguments 1153 extra_link_args += self.build.get_global_link_args(compiler, target.for_machine) 1154 # Link args added from the env: LDFLAGS, or the cross file. We want 1155 # these to override all the defaults but not the per-target link 1156 # args. 1157 extra_link_args += self.environment.coredata.get_external_link_args(target.for_machine, compiler.get_language()) 1158 # Only non-static built targets need link args and link dependencies 1159 extra_link_args += target.link_args 1160 # External deps must be last because target link libraries may depend on them. 1161 for dep in target.get_external_deps(): 1162 # Extend without reordering or de-dup to preserve `-L -l` sets 1163 # https://github.com/mesonbuild/meson/issues/1718 1164 if isinstance(dep, dependencies.OpenMPDependency): 1165 ET.SubElement(clconf, 'OpenMPSuppport').text = 'true' 1166 else: 1167 extra_link_args.extend_direct(dep.get_link_args()) 1168 for d in target.get_dependencies(): 1169 if isinstance(d, build.StaticLibrary): 1170 for dep in d.get_external_deps(): 1171 if isinstance(dep, dependencies.OpenMPDependency): 1172 ET.SubElement(clconf, 'OpenMPSuppport').text = 'true' 1173 else: 1174 extra_link_args.extend_direct(dep.get_link_args()) 1175 # Add link args for c_* or cpp_* build options. Currently this only 1176 # adds c_winlibs and cpp_winlibs when building for Windows. This needs 1177 # to be after all internal and external libraries so that unresolved 1178 # symbols from those can be found here. This is needed when the 1179 # *_winlibs that we want to link to are static mingw64 libraries. 1180 extra_link_args += compiler.get_option_link_args(self.environment.coredata.options) 1181 (additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args.to_native()) 1182 1183 # Add more libraries to be linked if needed 1184 for t in target.get_dependencies(): 1185 if isinstance(t, build.CustomTargetIndex): 1186 # We don't need the actual project here, just the library name 1187 lobj = t 1188 else: 1189 lobj = self.build.targets[t.get_id()] 1190 linkname = os.path.join(down, self.get_target_filename_for_linking(lobj)) 1191 if t in target.link_whole_targets: 1192 if compiler.id == 'msvc' and version_compare(compiler.version, '<19.00.23918'): 1193 # Expand our object lists manually if we are on pre-Visual Studio 2015 Update 2 1194 l = t.extract_all_objects(False) 1195 1196 # Unforunately, we can't use self.object_filename_from_source() 1197 gensrclist: T.List[File] = [] 1198 for gen in l.genlist: 1199 for src in gen.get_outputs(): 1200 if self.environment.is_source(src) and not self.environment.is_header(src): 1201 path = self.get_target_generated_dir(t, gen, src) 1202 gen_src_ext = '.' + os.path.splitext(path)[1][1:] 1203 extra_link_args.append(path[:-len(gen_src_ext)] + '.obj') 1204 1205 for src in l.srclist: 1206 obj_basename = None 1207 if self.environment.is_source(src) and not self.environment.is_header(src): 1208 obj_basename = self.object_filename_from_source(t, src) 1209 target_private_dir = self.relpath(self.get_target_private_dir(t), 1210 self.get_target_dir(t)) 1211 rel_obj = os.path.join(target_private_dir, obj_basename) 1212 extra_link_args.append(rel_obj) 1213 1214 extra_link_args.extend(self.flatten_object_list(t)) 1215 else: 1216 # /WHOLEARCHIVE:foo must go into AdditionalOptions 1217 extra_link_args += compiler.get_link_whole_for(linkname) 1218 # To force Visual Studio to build this project even though it 1219 # has no sources, we include a reference to the vcxproj file 1220 # that builds this target. Technically we should add this only 1221 # if the current target has no sources, but it doesn't hurt to 1222 # have 'extra' references. 1223 trelpath = self.get_target_dir_relative_to(t, target) 1224 tvcxproj = os.path.join(trelpath, t.get_id() + '.vcxproj') 1225 tid = self.environment.coredata.target_guids[t.get_id()] 1226 self.add_project_reference(root, tvcxproj, tid, link_outputs=True) 1227 # Mark the dependency as already handled to not have 1228 # multiple references to the same target. 1229 self.handled_target_deps[target.get_id()].append(t.get_id()) 1230 else: 1231 # Other libraries go into AdditionalDependencies 1232 if linkname not in additional_links: 1233 additional_links.append(linkname) 1234 for lib in self.get_custom_target_provided_libraries(target): 1235 additional_links.append(self.relpath(lib, self.get_target_dir(target))) 1236 additional_objects = [] 1237 for o in self.flatten_object_list(target, down): 1238 assert isinstance(o, str) 1239 additional_objects.append(o) 1240 for o in custom_objs: 1241 additional_objects.append(o) 1242 1243 if len(extra_link_args) > 0: 1244 extra_link_args.append('%(AdditionalOptions)') 1245 ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args) 1246 if len(additional_libpaths) > 0: 1247 additional_libpaths.insert(0, '%(AdditionalLibraryDirectories)') 1248 ET.SubElement(link, 'AdditionalLibraryDirectories').text = ';'.join(additional_libpaths) 1249 if len(additional_links) > 0: 1250 additional_links.append('%(AdditionalDependencies)') 1251 ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links) 1252 ofile = ET.SubElement(link, 'OutputFile') 1253 ofile.text = '$(OutDir)%s' % target.get_filename() 1254 subsys = ET.SubElement(link, 'SubSystem') 1255 subsys.text = subsystem 1256 if (isinstance(target, build.SharedLibrary) or isinstance(target, build.Executable)) and target.get_import_filename(): 1257 # DLLs built with MSVC always have an import library except when 1258 # they're data-only DLLs, but we don't support those yet. 1259 ET.SubElement(link, 'ImportLibrary').text = target.get_import_filename() 1260 if isinstance(target, build.SharedLibrary): 1261 # Add module definitions file, if provided 1262 if target.vs_module_defs: 1263 relpath = os.path.join(down, target.vs_module_defs.rel_to_builddir(self.build_to_src)) 1264 ET.SubElement(link, 'ModuleDefinitionFile').text = relpath 1265 if self.debug: 1266 pdb = ET.SubElement(link, 'ProgramDataBaseFileName') 1267 pdb.text = '$(OutDir}%s.pdb' % target_name 1268 targetmachine = ET.SubElement(link, 'TargetMachine') 1269 if target.for_machine is MachineChoice.BUILD: 1270 targetplatform = platform 1271 else: 1272 targetplatform = self.platform.lower() 1273 if targetplatform == 'win32': 1274 targetmachine.text = 'MachineX86' 1275 elif targetplatform == 'x64': 1276 targetmachine.text = 'MachineX64' 1277 elif targetplatform == 'arm': 1278 targetmachine.text = 'MachineARM' 1279 elif targetplatform == 'arm64': 1280 targetmachine.text = 'MachineARM64' 1281 elif targetplatform == 'arm64ec': 1282 targetmachine.text = 'MachineARM64EC' 1283 else: 1284 raise MesonException('Unsupported Visual Studio target machine: ' + targetplatform) 1285 # /nologo 1286 ET.SubElement(link, 'SuppressStartupBanner').text = 'true' 1287 # /release 1288 if not self.environment.coredata.get_option(OptionKey('debug')): 1289 ET.SubElement(link, 'SetChecksum').text = 'true' 1290 1291 meson_file_group = ET.SubElement(root, 'ItemGroup') 1292 ET.SubElement(meson_file_group, 'None', Include=os.path.join(proj_to_src_dir, build_filename)) 1293 1294 # Visual Studio can't load projects that present duplicated items. Filter them out 1295 # by keeping track of already added paths. 1296 def path_normalize_add(path, lis): 1297 normalized = os.path.normcase(os.path.normpath(path)) 1298 if normalized not in lis: 1299 lis.append(normalized) 1300 return True 1301 else: 1302 return False 1303 1304 previous_includes = [] 1305 if len(headers) + len(gen_hdrs) + len(target.extra_files) + len(pch_sources) > 0: 1306 inc_hdrs = ET.SubElement(root, 'ItemGroup') 1307 for h in headers: 1308 relpath = os.path.join(down, h.rel_to_builddir(self.build_to_src)) 1309 if path_normalize_add(relpath, previous_includes): 1310 ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath) 1311 for h in gen_hdrs: 1312 if path_normalize_add(h, previous_includes): 1313 ET.SubElement(inc_hdrs, 'CLInclude', Include=h) 1314 for h in target.extra_files: 1315 relpath = os.path.join(down, h.rel_to_builddir(self.build_to_src)) 1316 if path_normalize_add(relpath, previous_includes): 1317 ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath) 1318 for lang in pch_sources: 1319 h = pch_sources[lang][0] 1320 path = os.path.join(proj_to_src_dir, h) 1321 if path_normalize_add(path, previous_includes): 1322 ET.SubElement(inc_hdrs, 'CLInclude', Include=path) 1323 1324 previous_sources = [] 1325 if len(sources) + len(gen_src) + len(pch_sources) > 0: 1326 inc_src = ET.SubElement(root, 'ItemGroup') 1327 for s in sources: 1328 relpath = os.path.join(down, s.rel_to_builddir(self.build_to_src)) 1329 if path_normalize_add(relpath, previous_sources): 1330 inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath) 1331 lang = Vs2010Backend.lang_from_source_file(s) 1332 self.add_pch(pch_sources, lang, inc_cl) 1333 self.add_additional_options(lang, inc_cl, file_args) 1334 self.add_preprocessor_defines(lang, inc_cl, file_defines) 1335 self.add_include_dirs(lang, inc_cl, file_inc_dirs) 1336 ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + self.object_filename_from_source(target, s) 1337 for s in gen_src: 1338 if path_normalize_add(s, previous_sources): 1339 inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=s) 1340 lang = Vs2010Backend.lang_from_source_file(s) 1341 self.add_pch(pch_sources, lang, inc_cl) 1342 self.add_additional_options(lang, inc_cl, file_args) 1343 self.add_preprocessor_defines(lang, inc_cl, file_defines) 1344 self.add_include_dirs(lang, inc_cl, file_inc_dirs) 1345 s = File.from_built_file(target.get_subdir(), s) 1346 ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + self.object_filename_from_source(target, s) 1347 for lang in pch_sources: 1348 impl = pch_sources[lang][1] 1349 if impl and path_normalize_add(impl, previous_sources): 1350 inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=impl) 1351 self.create_pch(pch_sources, lang, inc_cl) 1352 self.add_additional_options(lang, inc_cl, file_args) 1353 self.add_preprocessor_defines(lang, inc_cl, file_defines) 1354 pch_header_dir = pch_sources[lang][3] 1355 if pch_header_dir: 1356 inc_dirs = copy.deepcopy(file_inc_dirs) 1357 inc_dirs[lang] = [pch_header_dir] + inc_dirs[lang] 1358 else: 1359 inc_dirs = file_inc_dirs 1360 self.add_include_dirs(lang, inc_cl, inc_dirs) 1361 #XXX: Do we need to set the object file name name here too? 1362 1363 previous_objects = [] 1364 if self.has_objects(objects, additional_objects, gen_objs): 1365 inc_objs = ET.SubElement(root, 'ItemGroup') 1366 for s in objects: 1367 relpath = os.path.join(down, s.rel_to_builddir(self.build_to_src)) 1368 if path_normalize_add(relpath, previous_objects): 1369 ET.SubElement(inc_objs, 'Object', Include=relpath) 1370 for s in additional_objects: 1371 if path_normalize_add(s, previous_objects): 1372 ET.SubElement(inc_objs, 'Object', Include=s) 1373 self.add_generated_objects(inc_objs, gen_objs) 1374 1375 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') 1376 self.add_regen_dependency(root) 1377 self.add_target_deps(root, target) 1378 self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) 1379 1380 def gen_regenproj(self, project_name, ofname): 1381 root = ET.Element('Project', {'DefaultTargets': 'Build', 1382 'ToolsVersion': '4.0', 1383 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) 1384 confitems = ET.SubElement(root, 'ItemGroup', {'Label': 'ProjectConfigurations'}) 1385 prjconf = ET.SubElement(confitems, 'ProjectConfiguration', 1386 {'Include': self.buildtype + '|' + self.platform}) 1387 p = ET.SubElement(prjconf, 'Configuration') 1388 p.text = self.buildtype 1389 pl = ET.SubElement(prjconf, 'Platform') 1390 pl.text = self.platform 1391 globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') 1392 guidelem = ET.SubElement(globalgroup, 'ProjectGuid') 1393 guidelem.text = '{%s}' % self.environment.coredata.regen_guid 1394 kw = ET.SubElement(globalgroup, 'Keyword') 1395 kw.text = self.platform + 'Proj' 1396 p = ET.SubElement(globalgroup, 'Platform') 1397 p.text = self.platform 1398 pname = ET.SubElement(globalgroup, 'ProjectName') 1399 pname.text = project_name 1400 if self.windows_target_platform_version: 1401 ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version 1402 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.Default.props') 1403 type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') 1404 ET.SubElement(type_config, 'ConfigurationType').text = "Utility" 1405 ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' 1406 ET.SubElement(type_config, 'UseOfMfc').text = 'false' 1407 if self.platform_toolset: 1408 ET.SubElement(type_config, 'PlatformToolset').text = self.platform_toolset 1409 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.props') 1410 direlem = ET.SubElement(root, 'PropertyGroup') 1411 fver = ET.SubElement(direlem, '_ProjectFileVersion') 1412 fver.text = self.project_file_version 1413 outdir = ET.SubElement(direlem, 'OutDir') 1414 outdir.text = '.\\' 1415 intdir = ET.SubElement(direlem, 'IntDir') 1416 intdir.text = 'regen-temp\\' 1417 tname = ET.SubElement(direlem, 'TargetName') 1418 tname.text = project_name 1419 1420 action = ET.SubElement(root, 'ItemDefinitionGroup') 1421 midl = ET.SubElement(action, 'Midl') 1422 ET.SubElement(midl, "AdditionalIncludeDirectories").text = '%(AdditionalIncludeDirectories)' 1423 ET.SubElement(midl, "OutputDirectory").text = '$(IntDir)' 1424 ET.SubElement(midl, 'HeaderFileName').text = '%(Filename).h' 1425 ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' 1426 ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' 1427 ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' 1428 regen_command = self.environment.get_build_command() + ['--internal', 'regencheck'] 1429 cmd_templ = '''call %s > NUL 1430"%s" "%s"''' 1431 regen_command = cmd_templ % \ 1432 (self.get_vcvars_command(), '" "'.join(regen_command), self.environment.get_scratch_dir()) 1433 self.add_custom_build(root, 'regen', regen_command, deps=self.get_regen_filelist(), 1434 outputs=[Vs2010Backend.get_regen_stampfile(self.environment.get_build_dir())], 1435 msg='Checking whether solution needs to be regenerated.') 1436 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') 1437 ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets') 1438 self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) 1439 1440 def gen_testproj(self, target_name, ofname): 1441 project_name = target_name 1442 root = ET.Element('Project', {'DefaultTargets': "Build", 1443 'ToolsVersion': '4.0', 1444 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) 1445 confitems = ET.SubElement(root, 'ItemGroup', {'Label': 'ProjectConfigurations'}) 1446 prjconf = ET.SubElement(confitems, 'ProjectConfiguration', 1447 {'Include': self.buildtype + '|' + self.platform}) 1448 p = ET.SubElement(prjconf, 'Configuration') 1449 p.text = self.buildtype 1450 pl = ET.SubElement(prjconf, 'Platform') 1451 pl.text = self.platform 1452 globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') 1453 guidelem = ET.SubElement(globalgroup, 'ProjectGuid') 1454 guidelem.text = '{%s}' % self.environment.coredata.test_guid 1455 kw = ET.SubElement(globalgroup, 'Keyword') 1456 kw.text = self.platform + 'Proj' 1457 p = ET.SubElement(globalgroup, 'Platform') 1458 p.text = self.platform 1459 pname = ET.SubElement(globalgroup, 'ProjectName') 1460 pname.text = project_name 1461 if self.windows_target_platform_version: 1462 ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version 1463 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.Default.props') 1464 type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') 1465 ET.SubElement(type_config, 'ConfigurationType') 1466 ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' 1467 ET.SubElement(type_config, 'UseOfMfc').text = 'false' 1468 if self.platform_toolset: 1469 ET.SubElement(type_config, 'PlatformToolset').text = self.platform_toolset 1470 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.props') 1471 direlem = ET.SubElement(root, 'PropertyGroup') 1472 fver = ET.SubElement(direlem, '_ProjectFileVersion') 1473 fver.text = self.project_file_version 1474 outdir = ET.SubElement(direlem, 'OutDir') 1475 outdir.text = '.\\' 1476 intdir = ET.SubElement(direlem, 'IntDir') 1477 intdir.text = 'test-temp\\' 1478 tname = ET.SubElement(direlem, 'TargetName') 1479 tname.text = target_name 1480 1481 action = ET.SubElement(root, 'ItemDefinitionGroup') 1482 midl = ET.SubElement(action, 'Midl') 1483 ET.SubElement(midl, "AdditionalIncludeDirectories").text = '%(AdditionalIncludeDirectories)' 1484 ET.SubElement(midl, "OutputDirectory").text = '$(IntDir)' 1485 ET.SubElement(midl, 'HeaderFileName').text = '%(Filename).h' 1486 ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' 1487 ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' 1488 ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' 1489 # FIXME: No benchmarks? 1490 test_command = self.environment.get_build_command() + ['test', '--no-rebuild'] 1491 if not self.environment.coredata.get_option(OptionKey('stdsplit')): 1492 test_command += ['--no-stdsplit'] 1493 if self.environment.coredata.get_option(OptionKey('errorlogs')): 1494 test_command += ['--print-errorlogs'] 1495 self.serialize_tests() 1496 self.add_custom_build(root, 'run_tests', '"%s"' % ('" "'.join(test_command))) 1497 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') 1498 self.add_regen_dependency(root) 1499 self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) 1500 1501 def gen_installproj(self, target_name, ofname): 1502 self.create_install_data_files() 1503 project_name = target_name 1504 root = ET.Element('Project', {'DefaultTargets': "Build", 1505 'ToolsVersion': '4.0', 1506 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) 1507 confitems = ET.SubElement(root, 'ItemGroup', {'Label': 'ProjectConfigurations'}) 1508 prjconf = ET.SubElement(confitems, 'ProjectConfiguration', 1509 {'Include': self.buildtype + '|' + self.platform}) 1510 p = ET.SubElement(prjconf, 'Configuration') 1511 p.text = self.buildtype 1512 pl = ET.SubElement(prjconf, 'Platform') 1513 pl.text = self.platform 1514 globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') 1515 guidelem = ET.SubElement(globalgroup, 'ProjectGuid') 1516 guidelem.text = '{%s}' % self.environment.coredata.install_guid 1517 kw = ET.SubElement(globalgroup, 'Keyword') 1518 kw.text = self.platform + 'Proj' 1519 p = ET.SubElement(globalgroup, 'Platform') 1520 p.text = self.platform 1521 pname = ET.SubElement(globalgroup, 'ProjectName') 1522 pname.text = project_name 1523 if self.windows_target_platform_version: 1524 ET.SubElement(globalgroup, 'WindowsTargetPlatformVersion').text = self.windows_target_platform_version 1525 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.Default.props') 1526 type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') 1527 ET.SubElement(type_config, 'ConfigurationType') 1528 ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' 1529 ET.SubElement(type_config, 'UseOfMfc').text = 'false' 1530 if self.platform_toolset: 1531 ET.SubElement(type_config, 'PlatformToolset').text = self.platform_toolset 1532 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.props') 1533 direlem = ET.SubElement(root, 'PropertyGroup') 1534 fver = ET.SubElement(direlem, '_ProjectFileVersion') 1535 fver.text = self.project_file_version 1536 outdir = ET.SubElement(direlem, 'OutDir') 1537 outdir.text = '.\\' 1538 intdir = ET.SubElement(direlem, 'IntDir') 1539 intdir.text = 'install-temp\\' 1540 tname = ET.SubElement(direlem, 'TargetName') 1541 tname.text = target_name 1542 1543 action = ET.SubElement(root, 'ItemDefinitionGroup') 1544 midl = ET.SubElement(action, 'Midl') 1545 ET.SubElement(midl, "AdditionalIncludeDirectories").text = '%(AdditionalIncludeDirectories)' 1546 ET.SubElement(midl, "OutputDirectory").text = '$(IntDir)' 1547 ET.SubElement(midl, 'HeaderFileName').text = '%(Filename).h' 1548 ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' 1549 ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' 1550 ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' 1551 install_command = self.environment.get_build_command() + ['install', '--no-rebuild'] 1552 self.add_custom_build(root, 'run_install', '"%s"' % ('" "'.join(install_command))) 1553 ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') 1554 self.add_regen_dependency(root) 1555 self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) 1556 1557 def add_custom_build(self, node, rulename, command, deps=None, outputs=None, msg=None, verify_files=True): 1558 igroup = ET.SubElement(node, 'ItemGroup') 1559 rulefile = os.path.join(self.environment.get_scratch_dir(), rulename + '.rule') 1560 if not os.path.exists(rulefile): 1561 with open(rulefile, 'w', encoding='utf-8') as f: 1562 f.write("# Meson regen file.") 1563 custombuild = ET.SubElement(igroup, 'CustomBuild', Include=rulefile) 1564 if msg: 1565 message = ET.SubElement(custombuild, 'Message') 1566 message.text = msg 1567 if not verify_files: 1568 ET.SubElement(custombuild, 'VerifyInputsAndOutputsExist').text = 'false' 1569 cmd_templ = '''setlocal 1570%s 1571if %%errorlevel%% neq 0 goto :cmEnd 1572:cmEnd 1573endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone 1574:cmErrorLevel 1575exit /b %%1 1576:cmDone 1577if %%errorlevel%% neq 0 goto :VCEnd''' 1578 ET.SubElement(custombuild, 'Command').text = cmd_templ % command 1579 if not outputs: 1580 # Use a nonexistent file to always consider the target out-of-date. 1581 outputs = [self.nonexistent_file(os.path.join(self.environment.get_scratch_dir(), 1582 'outofdate.file'))] 1583 ET.SubElement(custombuild, 'Outputs').text = ';'.join(outputs) 1584 if deps: 1585 ET.SubElement(custombuild, 'AdditionalInputs').text = ';'.join(deps) 1586 1587 @staticmethod 1588 def nonexistent_file(prefix): 1589 i = 0 1590 file = prefix 1591 while os.path.exists(file): 1592 file = '%s%d' % (prefix, i) 1593 return file 1594 1595 def generate_debug_information(self, link): 1596 # valid values for vs2015 is 'false', 'true', 'DebugFastLink' 1597 ET.SubElement(link, 'GenerateDebugInformation').text = 'true' 1598 1599 def add_regen_dependency(self, root): 1600 regen_vcxproj = os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj') 1601 self.add_project_reference(root, regen_vcxproj, self.environment.coredata.regen_guid) 1602