forked from mirrors/gecko-dev
Mach can currently only run on Python version 3.8 or higher, so it doesn't make sense to continue having dead code that provides support for Python2. Differential Revision: https://phabricator.services.mozilla.com/D209030
707 lines
25 KiB
Python
707 lines
25 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
# This file contains a build backend for generating Visual Studio project
|
|
# files.
|
|
|
|
import errno
|
|
import os
|
|
import re
|
|
import uuid
|
|
from pathlib import Path
|
|
from xml.dom import getDOMImplementation
|
|
|
|
from mozpack.files import FileFinder
|
|
|
|
from mozbuild.base import ExecutionSummary
|
|
|
|
from ..frontend.data import (
|
|
Defines,
|
|
HostProgram,
|
|
HostSources,
|
|
Library,
|
|
LocalInclude,
|
|
Program,
|
|
SandboxedWasmLibrary,
|
|
Sources,
|
|
UnifiedSources,
|
|
)
|
|
from .common import CommonBackend
|
|
|
|
MSBUILD_NAMESPACE = "http://schemas.microsoft.com/developer/msbuild/2003"
|
|
MSNATVIS_NAMESPACE = "http://schemas.microsoft.com/vstudio/debugger/natvis/2010"
|
|
|
|
|
|
def get_id(name):
|
|
return str(uuid.uuid5(uuid.NAMESPACE_URL, name)).upper()
|
|
|
|
|
|
def visual_studio_product_to_solution_version(version):
|
|
if version == "2017":
|
|
return "12.00", "15"
|
|
elif version == "2019":
|
|
return "12.00", "16"
|
|
elif version == "2022":
|
|
return "12.00", "17"
|
|
else:
|
|
raise Exception("Unknown version seen: %s" % version)
|
|
|
|
|
|
def visual_studio_product_to_platform_toolset_version(version):
|
|
if version == "2017":
|
|
return "v141"
|
|
elif version == "2019":
|
|
return "v142"
|
|
elif version == "2022":
|
|
return "v143"
|
|
else:
|
|
raise Exception("Unknown version seen: %s" % version)
|
|
|
|
|
|
class VisualStudioBackend(CommonBackend):
|
|
"""Generate Visual Studio project files.
|
|
|
|
This backend is used to produce Visual Studio projects and a solution
|
|
to foster developing Firefox with Visual Studio.
|
|
|
|
This backend is currently considered experimental. There are many things
|
|
not optimal about how it works.
|
|
"""
|
|
|
|
def _init(self):
|
|
CommonBackend._init(self)
|
|
|
|
# These should eventually evolve into parameters.
|
|
self._out_dir = os.path.join(self.environment.topobjdir, "msvc")
|
|
self._projsubdir = "projects"
|
|
|
|
self._version = self.environment.substs.get("MSVS_VERSION", "2017")
|
|
|
|
self._paths_to_sources = {}
|
|
self._paths_to_includes = {}
|
|
self._paths_to_defines = {}
|
|
self._paths_to_configs = {}
|
|
self._libs_to_paths = {}
|
|
self._progs_to_paths = {}
|
|
|
|
def summary(self):
|
|
return ExecutionSummary(
|
|
"VisualStudio backend executed in {execution_time:.2f}s\n"
|
|
"Generated Visual Studio solution at {path:s}",
|
|
execution_time=self._execution_time,
|
|
path=os.path.join(self._out_dir, "mozilla.sln"),
|
|
)
|
|
|
|
def consume_object(self, obj):
|
|
reldir = getattr(obj, "relsrcdir", None)
|
|
|
|
if hasattr(obj, "config") and reldir not in self._paths_to_configs:
|
|
self._paths_to_configs[reldir] = obj.config
|
|
|
|
if isinstance(obj, Sources):
|
|
self._add_sources(reldir, obj)
|
|
|
|
elif isinstance(obj, HostSources):
|
|
self._add_sources(reldir, obj)
|
|
|
|
elif isinstance(obj, UnifiedSources):
|
|
# XXX we should be letting CommonBackend.consume_object call this
|
|
# for us instead.
|
|
self._process_unified_sources(obj)
|
|
|
|
elif isinstance(obj, Library) and not isinstance(obj, SandboxedWasmLibrary):
|
|
self._libs_to_paths[obj.basename] = reldir
|
|
|
|
elif isinstance(obj, Program) or isinstance(obj, HostProgram):
|
|
self._progs_to_paths[obj.program] = reldir
|
|
|
|
elif isinstance(obj, Defines):
|
|
self._paths_to_defines.setdefault(reldir, {}).update(obj.defines)
|
|
|
|
elif isinstance(obj, LocalInclude):
|
|
includes = self._paths_to_includes.setdefault(reldir, [])
|
|
includes.append(obj.path.full_path)
|
|
|
|
# Just acknowledge everything.
|
|
return True
|
|
|
|
def _add_sources(self, reldir, obj):
|
|
s = self._paths_to_sources.setdefault(reldir, set())
|
|
s.update(obj.files)
|
|
|
|
def _process_unified_sources(self, obj):
|
|
reldir = getattr(obj, "relsrcdir", None)
|
|
|
|
s = self._paths_to_sources.setdefault(reldir, set())
|
|
s.update(obj.files)
|
|
|
|
def consume_finished(self):
|
|
out_dir = self._out_dir
|
|
out_proj_dir = os.path.join(self._out_dir, self._projsubdir)
|
|
|
|
projects = self._write_projects_for_sources(
|
|
self._libs_to_paths, "library", out_proj_dir
|
|
)
|
|
projects.update(
|
|
self._write_projects_for_sources(
|
|
self._progs_to_paths, "binary", out_proj_dir
|
|
)
|
|
)
|
|
|
|
# Generate projects that can be used to build common targets.
|
|
for target in ("export", "binaries", "tools", "full"):
|
|
basename = "target_%s" % target
|
|
command = "$(SolutionDir)\\mach.bat build"
|
|
if target != "full":
|
|
command += " %s" % target
|
|
|
|
project_id = self._write_vs_project(
|
|
out_proj_dir,
|
|
basename,
|
|
target,
|
|
build_command=command,
|
|
clean_command="$(SolutionDir)\\mach.bat clobber",
|
|
)
|
|
|
|
projects[basename] = (project_id, basename, target)
|
|
|
|
# A project that can be used to regenerate the visual studio projects.
|
|
basename = "target_vs"
|
|
project_id = self._write_vs_project(
|
|
out_proj_dir,
|
|
basename,
|
|
"visual-studio",
|
|
build_command="$(SolutionDir)\\mach.bat build-backend -b VisualStudio",
|
|
)
|
|
projects[basename] = (project_id, basename, "visual-studio")
|
|
|
|
# Write out a shared property file with common variables.
|
|
props_path = os.path.join(out_proj_dir, "mozilla.props")
|
|
with self._write_file(props_path, readmode="rb") as fh:
|
|
self._write_props(fh)
|
|
|
|
# Generate some wrapper scripts that allow us to invoke mach inside
|
|
# a MozillaBuild-like environment. We currently only use the batch
|
|
# script. We'd like to use the PowerShell script. However, it seems
|
|
# to buffer output from within Visual Studio (surely this is
|
|
# configurable) and the default execution policy of PowerShell doesn't
|
|
# allow custom scripts to be executed.
|
|
with self._write_file(os.path.join(out_dir, "mach.bat"), readmode="rb") as fh:
|
|
self._write_mach_batch(fh)
|
|
|
|
with self._write_file(os.path.join(out_dir, "mach.ps1"), readmode="rb") as fh:
|
|
self._write_mach_powershell(fh)
|
|
|
|
# Write out a solution file to tie it all together.
|
|
solution_path = os.path.join(out_dir, "mozilla.sln")
|
|
with self._write_file(solution_path, readmode="rb") as fh:
|
|
self._write_solution(fh, projects)
|
|
|
|
def _write_projects_for_sources(self, sources, prefix, out_dir):
|
|
projects = {}
|
|
for item, path in sorted(sources.items()):
|
|
config = self._paths_to_configs.get(path, None)
|
|
sources = self._paths_to_sources.get(path, set())
|
|
sources = set(os.path.join("$(TopSrcDir)", path, s) for s in sources)
|
|
sources = set(os.path.normpath(s) for s in sources)
|
|
|
|
finder = FileFinder(os.path.join(self.environment.topsrcdir, path))
|
|
|
|
headers = [t[0] for t in finder.find("*.h")]
|
|
headers = [
|
|
os.path.normpath(os.path.join("$(TopSrcDir)", path, f)) for f in headers
|
|
]
|
|
|
|
includes = [
|
|
os.path.join("$(TopSrcDir)", path),
|
|
os.path.join("$(TopObjDir)", path),
|
|
]
|
|
includes.extend(self._paths_to_includes.get(path, []))
|
|
includes.append("$(TopObjDir)\\dist\\include\\nss")
|
|
includes.append("$(TopObjDir)\\dist\\include")
|
|
|
|
for v in (
|
|
"NSPR_CFLAGS",
|
|
"NSS_CFLAGS",
|
|
"MOZ_JPEG_CFLAGS",
|
|
"MOZ_PNG_CFLAGS",
|
|
"MOZ_ZLIB_CFLAGS",
|
|
"MOZ_PIXMAN_CFLAGS",
|
|
):
|
|
if not config:
|
|
break
|
|
|
|
args = config.substs.get(v, [])
|
|
|
|
for i, arg in enumerate(args):
|
|
if arg.startswith("-I"):
|
|
includes.append(os.path.normpath(arg[2:]))
|
|
|
|
# Pull in system defaults.
|
|
includes.append("$(DefaultIncludes)")
|
|
|
|
includes = [os.path.normpath(i) for i in includes]
|
|
|
|
defines = []
|
|
for k, v in self._paths_to_defines.get(path, {}).items():
|
|
if v is True:
|
|
defines.append(k)
|
|
else:
|
|
defines.append("%s=%s" % (k, v))
|
|
|
|
debugger = None
|
|
if prefix == "binary":
|
|
if item.startswith(self.environment.substs["MOZ_APP_NAME"]):
|
|
app_args = "-no-remote -profile $(TopObjDir)\\tmp\\profile-default"
|
|
if self.environment.substs.get("MOZ_LAUNCHER_PROCESS", False):
|
|
app_args += " -wait-for-browser"
|
|
debugger = ("$(TopObjDir)\\dist\\bin\\%s" % item, app_args)
|
|
else:
|
|
debugger = ("$(TopObjDir)\\dist\\bin\\%s" % item, "")
|
|
|
|
basename = "%s_%s" % (prefix, item)
|
|
|
|
project_id = self._write_vs_project(
|
|
out_dir,
|
|
basename,
|
|
item,
|
|
includes=includes,
|
|
forced_includes=["$(TopObjDir)\\dist\\include\\mozilla-config.h"],
|
|
defines=defines,
|
|
headers=headers,
|
|
sources=sources,
|
|
debugger=debugger,
|
|
)
|
|
|
|
projects[basename] = (project_id, basename, item)
|
|
|
|
return projects
|
|
|
|
def _write_solution(self, fh, projects):
|
|
# Visual Studio appears to write out its current version in the
|
|
# solution file. Instead of trying to figure out what version it will
|
|
# write, try to parse the version out of the existing file and use it
|
|
# verbatim.
|
|
vs_version = None
|
|
try:
|
|
with open(fh.name, "rb") as sfh:
|
|
for line in sfh:
|
|
if line.startswith(b"VisualStudioVersion = "):
|
|
vs_version = line.split(b" = ", 1)[1].strip()
|
|
except IOError as e:
|
|
if e.errno != errno.ENOENT:
|
|
raise
|
|
|
|
format_version, comment_version = visual_studio_product_to_solution_version(
|
|
self._version
|
|
)
|
|
# This is a Visual C++ Project type.
|
|
project_type = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"
|
|
|
|
# Visual Studio seems to require this header.
|
|
fh.write(
|
|
"Microsoft Visual Studio Solution File, Format Version %s\r\n"
|
|
% format_version
|
|
)
|
|
fh.write("# Visual Studio %s\r\n" % comment_version)
|
|
|
|
if vs_version:
|
|
fh.write("VisualStudioVersion = %s\r\n" % vs_version)
|
|
|
|
# Corresponds to VS2013.
|
|
fh.write("MinimumVisualStudioVersion = 12.0.31101.0\r\n")
|
|
|
|
binaries_id = projects["target_binaries"][0]
|
|
|
|
# Write out entries for each project.
|
|
for key in sorted(projects):
|
|
project_id, basename, name = projects[key]
|
|
path = os.path.join(self._projsubdir, "%s.vcxproj" % basename)
|
|
|
|
fh.write(
|
|
'Project("{%s}") = "%s", "%s", "{%s}"\r\n'
|
|
% (project_type, name, path, project_id)
|
|
)
|
|
|
|
# Make all libraries depend on the binaries target.
|
|
if key.startswith("library_"):
|
|
fh.write("\tProjectSection(ProjectDependencies) = postProject\r\n")
|
|
fh.write("\t\t{%s} = {%s}\r\n" % (binaries_id, binaries_id))
|
|
fh.write("\tEndProjectSection\r\n")
|
|
|
|
fh.write("EndProject\r\n")
|
|
|
|
# Write out solution folders for organizing things.
|
|
|
|
# This is the UUID you use for solution folders.
|
|
container_id = "2150E333-8FDC-42A3-9474-1A3956D46DE8"
|
|
|
|
def write_container(desc):
|
|
cid = get_id(desc)
|
|
fh.write(
|
|
'Project("{%s}") = "%s", "%s", "{%s}"\r\n'
|
|
% (container_id, desc, desc, cid)
|
|
)
|
|
fh.write("EndProject\r\n")
|
|
|
|
return cid
|
|
|
|
library_id = write_container("Libraries")
|
|
target_id = write_container("Build Targets")
|
|
binary_id = write_container("Binaries")
|
|
|
|
fh.write("Global\r\n")
|
|
|
|
# Make every project a member of our one configuration.
|
|
fh.write("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n")
|
|
fh.write("\t\tBuild|Win32 = Build|Win32\r\n")
|
|
fh.write("\tEndGlobalSection\r\n")
|
|
|
|
# Set every project's active configuration to the one configuration and
|
|
# set up the default build project.
|
|
fh.write("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n")
|
|
for name, project in sorted(projects.items()):
|
|
fh.write("\t\t{%s}.Build|Win32.ActiveCfg = Build|Win32\r\n" % project[0])
|
|
|
|
# Only build the full build target by default.
|
|
# It's important we don't write multiple entries here because they
|
|
# conflict!
|
|
if name == "target_full":
|
|
fh.write("\t\t{%s}.Build|Win32.Build.0 = Build|Win32\r\n" % project[0])
|
|
|
|
fh.write("\tEndGlobalSection\r\n")
|
|
|
|
fh.write("\tGlobalSection(SolutionProperties) = preSolution\r\n")
|
|
fh.write("\t\tHideSolutionNode = FALSE\r\n")
|
|
fh.write("\tEndGlobalSection\r\n")
|
|
|
|
# Associate projects with containers.
|
|
fh.write("\tGlobalSection(NestedProjects) = preSolution\r\n")
|
|
for key in sorted(projects):
|
|
project_id = projects[key][0]
|
|
|
|
if key.startswith("library_"):
|
|
container_id = library_id
|
|
elif key.startswith("target_"):
|
|
container_id = target_id
|
|
elif key.startswith("binary_"):
|
|
container_id = binary_id
|
|
else:
|
|
raise Exception("Unknown project type: %s" % key)
|
|
|
|
fh.write("\t\t{%s} = {%s}\r\n" % (project_id, container_id))
|
|
fh.write("\tEndGlobalSection\r\n")
|
|
|
|
fh.write("EndGlobal\r\n")
|
|
|
|
def _write_props(self, fh):
|
|
impl = getDOMImplementation()
|
|
doc = impl.createDocument(MSBUILD_NAMESPACE, "Project", None)
|
|
|
|
project = doc.documentElement
|
|
project.setAttribute("xmlns", MSBUILD_NAMESPACE)
|
|
project.setAttribute("ToolsVersion", "4.0")
|
|
|
|
ig = project.appendChild(doc.createElement("ImportGroup"))
|
|
ig.setAttribute("Label", "PropertySheets")
|
|
|
|
pg = project.appendChild(doc.createElement("PropertyGroup"))
|
|
pg.setAttribute("Label", "UserMacros")
|
|
|
|
ig = project.appendChild(doc.createElement("ItemGroup"))
|
|
|
|
def add_var(k, v):
|
|
e = pg.appendChild(doc.createElement(k))
|
|
e.appendChild(doc.createTextNode(v))
|
|
|
|
e = ig.appendChild(doc.createElement("BuildMacro"))
|
|
e.setAttribute("Include", k)
|
|
|
|
e = e.appendChild(doc.createElement("Value"))
|
|
e.appendChild(doc.createTextNode("$(%s)" % k))
|
|
|
|
natvis = ig.appendChild(doc.createElement("Natvis"))
|
|
natvis.setAttribute("Include", "../../../toolkit/library/gecko.natvis")
|
|
|
|
add_var("TopObjDir", os.path.normpath(self.environment.topobjdir))
|
|
add_var("TopSrcDir", os.path.normpath(self.environment.topsrcdir))
|
|
add_var("PYTHON", "$(TopObjDir)\\_virtualenv\\Scripts\\python.exe")
|
|
add_var("MACH", "$(TopSrcDir)\\mach")
|
|
|
|
# From MozillaBuild.
|
|
add_var("DefaultIncludes", os.environ.get("INCLUDE", ""))
|
|
|
|
fh.write(b"\xef\xbb\xbf")
|
|
doc.writexml(fh, addindent=" ", newl="\r\n")
|
|
|
|
def _create_natvis_type(
|
|
self, doc, visualizer, name, displayString, stringView=None
|
|
):
|
|
t = visualizer.appendChild(doc.createElement("Type"))
|
|
t.setAttribute("Name", name)
|
|
|
|
ds = t.appendChild(doc.createElement("DisplayString"))
|
|
ds.appendChild(doc.createTextNode(displayString))
|
|
|
|
if stringView is not None:
|
|
sv = t.appendChild(doc.createElement("DisplayString"))
|
|
sv.appendChild(doc.createTextNode(stringView))
|
|
|
|
def _create_natvis_simple_string_type(self, doc, visualizer, name):
|
|
self._create_natvis_type(
|
|
doc, visualizer, name + "<char16_t>", "{mData,su}", "mData,su"
|
|
)
|
|
self._create_natvis_type(
|
|
doc, visualizer, name + "<char>", "{mData,s}", "mData,s"
|
|
)
|
|
|
|
def _create_natvis_string_tuple_type(self, doc, visualizer, chartype, formatstring):
|
|
t = visualizer.appendChild(doc.createElement("Type"))
|
|
t.setAttribute("Name", "nsTSubstringTuple<" + chartype + ">")
|
|
|
|
ds1 = t.appendChild(doc.createElement("DisplayString"))
|
|
ds1.setAttribute("Condition", "mHead != nullptr")
|
|
ds1.appendChild(
|
|
doc.createTextNode("{mHead,na} {mFragB->mData," + formatstring + "}")
|
|
)
|
|
|
|
ds2 = t.appendChild(doc.createElement("DisplayString"))
|
|
ds2.setAttribute("Condition", "mHead == nullptr")
|
|
ds2.appendChild(
|
|
doc.createTextNode(
|
|
"{mFragA->mData,"
|
|
+ formatstring
|
|
+ "} {mFragB->mData,"
|
|
+ formatstring
|
|
+ "}"
|
|
)
|
|
)
|
|
|
|
def _relevant_environment_variables(self):
|
|
# Write out the environment variables, presumably coming from
|
|
# MozillaBuild.
|
|
for k, v in sorted(os.environ.items()):
|
|
if not re.match("^[a-zA-Z0-9_]+$", k):
|
|
continue
|
|
|
|
if k in ("OLDPWD", "PS1"):
|
|
continue
|
|
|
|
if k.startswith("_"):
|
|
continue
|
|
|
|
yield k, v
|
|
|
|
yield "TOPSRCDIR", self.environment.topsrcdir
|
|
yield "TOPOBJDIR", self.environment.topobjdir
|
|
|
|
def _write_mach_powershell(self, fh):
|
|
for k, v in self._relevant_environment_variables():
|
|
fh.write(b'$env:%s = "%s"\r\n' % (k.encode("utf-8"), v.encode("utf-8")))
|
|
|
|
relpath = os.path.relpath(
|
|
self.environment.topsrcdir, self.environment.topobjdir
|
|
).replace("\\", "/")
|
|
|
|
fh.write(
|
|
b'$bashargs = "%s/mach", "--log-no-times"\r\n' % relpath.encode("utf-8")
|
|
)
|
|
fh.write(b"$bashargs = $bashargs + $args\r\n")
|
|
|
|
fh.write(b"$expanded = $bashargs -join ' '\r\n")
|
|
fh.write(b'$procargs = "-c", $expanded\r\n')
|
|
|
|
if (Path(os.environ["MOZILLABUILD"]) / "msys2").exists():
|
|
bash_path = rb"msys2\usr\bin\bash"
|
|
else:
|
|
bash_path = rb"msys\bin\bash"
|
|
|
|
fh.write(
|
|
b"Start-Process -WorkingDirectory $env:TOPOBJDIR "
|
|
b"-FilePath $env:MOZILLABUILD\\%b "
|
|
b"-ArgumentList $procargs "
|
|
b"-Wait -NoNewWindow\r\n" % bash_path
|
|
)
|
|
|
|
def _write_mach_batch(self, fh):
|
|
"""Write out a batch script that builds the tree.
|
|
|
|
The script "bootstraps" into the MozillaBuild environment by setting
|
|
the environment variables that are active in the current MozillaBuild
|
|
environment. Then, it builds the tree.
|
|
"""
|
|
for k, v in self._relevant_environment_variables():
|
|
fh.write(b'SET "%s=%s"\r\n' % (k.encode("utf-8"), v.encode("utf-8")))
|
|
|
|
fh.write(b"cd %TOPOBJDIR%\r\n")
|
|
|
|
# We need to convert Windows-native paths to msys paths. Easiest way is
|
|
# relative paths, since munging c:\ to /c/ is slightly more
|
|
# complicated.
|
|
relpath = os.path.relpath(
|
|
self.environment.topsrcdir, self.environment.topobjdir
|
|
).replace("\\", "/")
|
|
|
|
if (Path(os.environ["MOZILLABUILD"]) / "msys2").exists():
|
|
bash_path = rb"msys2\usr\bin\bash"
|
|
else:
|
|
bash_path = rb"msys\bin\bash"
|
|
|
|
# We go through mach because it has the logic for choosing the most
|
|
# appropriate build tool.
|
|
fh.write(
|
|
b'"%%MOZILLABUILD%%\\%b" '
|
|
b'-c "%s/mach --log-no-times %%1 %%2 %%3 %%4 %%5 %%6 %%7"'
|
|
% (bash_path, relpath.encode("utf-8"))
|
|
)
|
|
|
|
def _write_vs_project(self, out_dir, basename, name, **kwargs):
|
|
root = "%s.vcxproj" % basename
|
|
project_id = get_id(basename)
|
|
|
|
with self._write_file(os.path.join(out_dir, root), readmode="rb") as fh:
|
|
project_id, name = VisualStudioBackend.write_vs_project(
|
|
fh, self._version, project_id, name, **kwargs
|
|
)
|
|
|
|
with self._write_file(
|
|
os.path.join(out_dir, "%s.user" % root), readmode="rb"
|
|
) as fh:
|
|
fh.write('<?xml version="1.0" encoding="utf-8"?>\r\n')
|
|
fh.write('<Project ToolsVersion="4.0" xmlns="%s">\r\n' % MSBUILD_NAMESPACE)
|
|
fh.write("</Project>\r\n")
|
|
|
|
return project_id
|
|
|
|
@staticmethod
|
|
def write_vs_project(
|
|
fh,
|
|
version,
|
|
project_id,
|
|
name,
|
|
includes=[],
|
|
forced_includes=[],
|
|
defines=[],
|
|
build_command=None,
|
|
clean_command=None,
|
|
debugger=None,
|
|
headers=[],
|
|
sources=[],
|
|
):
|
|
impl = getDOMImplementation()
|
|
doc = impl.createDocument(MSBUILD_NAMESPACE, "Project", None)
|
|
|
|
project = doc.documentElement
|
|
project.setAttribute("DefaultTargets", "Build")
|
|
project.setAttribute("ToolsVersion", "4.0")
|
|
project.setAttribute("xmlns", MSBUILD_NAMESPACE)
|
|
|
|
ig = project.appendChild(doc.createElement("ItemGroup"))
|
|
ig.setAttribute("Label", "ProjectConfigurations")
|
|
|
|
pc = ig.appendChild(doc.createElement("ProjectConfiguration"))
|
|
pc.setAttribute("Include", "Build|Win32")
|
|
|
|
c = pc.appendChild(doc.createElement("Configuration"))
|
|
c.appendChild(doc.createTextNode("Build"))
|
|
|
|
p = pc.appendChild(doc.createElement("Platform"))
|
|
p.appendChild(doc.createTextNode("Win32"))
|
|
|
|
pg = project.appendChild(doc.createElement("PropertyGroup"))
|
|
pg.setAttribute("Label", "Globals")
|
|
|
|
n = pg.appendChild(doc.createElement("ProjectName"))
|
|
n.appendChild(doc.createTextNode(name))
|
|
|
|
k = pg.appendChild(doc.createElement("Keyword"))
|
|
k.appendChild(doc.createTextNode("MakeFileProj"))
|
|
|
|
g = pg.appendChild(doc.createElement("ProjectGuid"))
|
|
g.appendChild(doc.createTextNode("{%s}" % project_id))
|
|
|
|
rn = pg.appendChild(doc.createElement("RootNamespace"))
|
|
rn.appendChild(doc.createTextNode("mozilla"))
|
|
|
|
pts = pg.appendChild(doc.createElement("PlatformToolset"))
|
|
pts.appendChild(
|
|
doc.createTextNode(
|
|
visual_studio_product_to_platform_toolset_version(version)
|
|
)
|
|
)
|
|
|
|
i = project.appendChild(doc.createElement("Import"))
|
|
i.setAttribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props")
|
|
|
|
ig = project.appendChild(doc.createElement("ImportGroup"))
|
|
ig.setAttribute("Label", "ExtensionTargets")
|
|
|
|
ig = project.appendChild(doc.createElement("ImportGroup"))
|
|
ig.setAttribute("Label", "ExtensionSettings")
|
|
|
|
ig = project.appendChild(doc.createElement("ImportGroup"))
|
|
ig.setAttribute("Label", "PropertySheets")
|
|
i = ig.appendChild(doc.createElement("Import"))
|
|
i.setAttribute("Project", "mozilla.props")
|
|
|
|
pg = project.appendChild(doc.createElement("PropertyGroup"))
|
|
pg.setAttribute("Label", "Configuration")
|
|
ct = pg.appendChild(doc.createElement("ConfigurationType"))
|
|
ct.appendChild(doc.createTextNode("Makefile"))
|
|
|
|
pg = project.appendChild(doc.createElement("PropertyGroup"))
|
|
pg.setAttribute("Condition", "'$(Configuration)|$(Platform)'=='Build|Win32'")
|
|
|
|
if build_command:
|
|
n = pg.appendChild(doc.createElement("NMakeBuildCommandLine"))
|
|
n.appendChild(doc.createTextNode(build_command))
|
|
|
|
if clean_command:
|
|
n = pg.appendChild(doc.createElement("NMakeCleanCommandLine"))
|
|
n.appendChild(doc.createTextNode(clean_command))
|
|
|
|
if includes:
|
|
n = pg.appendChild(doc.createElement("NMakeIncludeSearchPath"))
|
|
n.appendChild(doc.createTextNode(";".join(includes)))
|
|
|
|
if forced_includes:
|
|
n = pg.appendChild(doc.createElement("NMakeForcedIncludes"))
|
|
n.appendChild(doc.createTextNode(";".join(forced_includes)))
|
|
|
|
if defines:
|
|
n = pg.appendChild(doc.createElement("NMakePreprocessorDefinitions"))
|
|
n.appendChild(doc.createTextNode(";".join(defines)))
|
|
|
|
if debugger:
|
|
n = pg.appendChild(doc.createElement("LocalDebuggerCommand"))
|
|
n.appendChild(doc.createTextNode(debugger[0]))
|
|
|
|
n = pg.appendChild(doc.createElement("LocalDebuggerCommandArguments"))
|
|
n.appendChild(doc.createTextNode(debugger[1]))
|
|
|
|
# Sets IntelliSense to use c++17 Language Standard
|
|
n = pg.appendChild(doc.createElement("AdditionalOptions"))
|
|
n.appendChild(doc.createTextNode("/std:c++17"))
|
|
|
|
i = project.appendChild(doc.createElement("Import"))
|
|
i.setAttribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props")
|
|
|
|
i = project.appendChild(doc.createElement("Import"))
|
|
i.setAttribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets")
|
|
|
|
# Now add files to the project.
|
|
ig = project.appendChild(doc.createElement("ItemGroup"))
|
|
for header in sorted(headers or []):
|
|
n = ig.appendChild(doc.createElement("ClInclude"))
|
|
n.setAttribute("Include", header)
|
|
|
|
ig = project.appendChild(doc.createElement("ItemGroup"))
|
|
for source in sorted(sources or []):
|
|
n = ig.appendChild(doc.createElement("ClCompile"))
|
|
n.setAttribute("Include", source)
|
|
|
|
fh.write(b"\xef\xbb\xbf")
|
|
doc.writexml(fh, addindent=" ", newl="\r\n")
|
|
|
|
return project_id, name
|