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