summaryrefslogtreecommitdiff
path: root/buildscripts/make_vcxproj.py
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2018-07-27 16:10:49 -0400
committerMark Benvenuto <mark.benvenuto@mongodb.com>2018-07-27 17:01:25 -0400
commitb42420caf1018835da58ace37b51099931b904c0 (patch)
tree19f63f1ed27ccbd03db306394e9204676e113eb3 /buildscripts/make_vcxproj.py
parent077a5b00937a539ef004f986ac08f9c51cf0e92e (diff)
downloadmongo-b42420caf1018835da58ace37b51099931b904c0.tar.gz
SERVER-36192 Change .vcxproj file generator to generate Makefile project
Diffstat (limited to 'buildscripts/make_vcxproj.py')
-rw-r--r--buildscripts/make_vcxproj.py95
1 files changed, 89 insertions, 6 deletions
diff --git a/buildscripts/make_vcxproj.py b/buildscripts/make_vcxproj.py
index 730f4e20726..72a37755af3 100644
--- a/buildscripts/make_vcxproj.py
+++ b/buildscripts/make_vcxproj.py
@@ -12,12 +12,16 @@ To build mongodb, you must use scons. You can use this project to navigate code
where FILE_NAME is the of the file to generate e.g., "mongod"
"""
+from __future__ import absolute_import, print_function
+import io
import json
import os
import re
+import StringIO
import sys
import uuid
+import xml.etree.ElementTree as ET
VCXPROJ_FOOTER = r"""
@@ -34,6 +38,16 @@ VCXPROJ_FOOTER = r"""
</Project>
"""
+VCXPROJ_NAMESPACE = 'http://schemas.microsoft.com/developer/msbuild/2003'
+
+# We preserve certain fields by saving them and restoring between file generations
+VCXPROJ_FIELDS_TO_PRESERVE = [
+ "NMakeBuildCommandLine",
+ "NMakeOutput",
+ "NMakeCleanCommandLine",
+ "NMakeReBuildCommandLine",
+]
+
def get_defines(args):
"""Parse a compiler argument list looking for defines."""
@@ -45,7 +59,7 @@ def get_defines(args):
def get_includes(args):
- """Parse a compiler argument list looking for includes."""
+ """Parse a compiler argument list looking for includes."""
ret = set()
for arg in args:
if arg.startswith('/I'):
@@ -53,6 +67,60 @@ def get_includes(args):
return ret
+def _read_vcxproj(file_name):
+ """Parse a vcxproj file and look for "NMake" prefixed elements in PropertyGroups."""
+
+ # Skip if this the first run
+ if not os.path.exists(file_name):
+ return None
+
+ tree = ET.parse(file_name)
+
+ interesting_tags = ['{%s}%s' % (VCXPROJ_NAMESPACE, tag) for tag in VCXPROJ_FIELDS_TO_PRESERVE]
+
+ save_elements = {}
+
+ for parent in tree.getroot():
+ for child in parent:
+ if child.tag in interesting_tags:
+ cond = parent.attrib['Condition']
+ save_elements[(parent.tag, child.tag, cond)] = child.text
+
+ return save_elements
+
+
+def _replace_vcxproj(file_name, restore_elements):
+ """Parse a vcxproj file, and replace elememts text nodes with values saved before."""
+ # Skip if this the first run
+ if not restore_elements:
+ return
+
+ tree = ET.parse(file_name)
+
+ interesting_tags = ['{%s}%s' % (VCXPROJ_NAMESPACE, tag) for tag in VCXPROJ_FIELDS_TO_PRESERVE]
+
+ for parent in tree.getroot():
+ for child in parent:
+ if child.tag in interesting_tags:
+ # Match PropertyGroup elements based on their condition
+ cond = parent.attrib['Condition']
+ saved_value = restore_elements[(parent.tag, child.tag, cond)]
+ child.text = saved_value
+
+ stream = StringIO.StringIO()
+
+ tree.write(stream)
+
+ str_value = stream.getvalue().encode()
+
+ # Strip the "ns0:" namespace prefix because ElementTree does not support default namespaces.
+ str_value = str_value.replace("<ns0:", "<").replace("</ns0:", "</").replace(
+ "xmlns:ns0", "xmlns")
+
+ with io.open(file_name, mode='wb') as file_handle:
+ file_handle.write(str_value)
+
+
class ProjFileGenerator(object): # pylint: disable=too-many-instance-attributes
"""Generate a .vcxproj and .vcxprof.filters file."""
@@ -71,12 +139,15 @@ class ProjFileGenerator(object): # pylint: disable=too-many-instance-attributes
self.vcxproj = None
self.filters = None
self.all_defines = set(self.common_defines)
+ self.vcxproj_file_name = self.target + ".vcxproj"
+
+ self.existing_build_commands = _read_vcxproj(self.vcxproj_file_name)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
- self.vcxproj = open(self.target + ".vcxproj", "wb")
+ self.vcxproj = open(self.vcxproj_file_name, "wb")
with open('buildscripts/vcxproj.header', 'r') as header_file:
header_str = header_file.read()
@@ -119,6 +190,9 @@ class ProjFileGenerator(object): # pylint: disable=too-many-instance-attributes
self.filters.write("</Project>\n")
self.filters.close()
+ # Replace build commands
+ _replace_vcxproj(self.vcxproj_file_name, self.existing_build_commands)
+
def parse_line(self, line):
"""Parse a build line."""
if line.startswith("cl"):
@@ -157,10 +231,19 @@ class ProjFileGenerator(object): # pylint: disable=too-many-instance-attributes
return True
return False
+ @staticmethod
+ def __cpp_file(name):
+ """Return True if this a C++ header or source file."""
+ file_exts = [".cpp", ".c", ".cxx", ".h", ".hpp", ".hh", ".hxx"]
+ file_ext = os.path.splitext(name)[1]
+ if file_ext in file_exts:
+ return True
+ return False
+
def __write_filters(self): # pylint: disable=too-many-branches
"""Generate the vcxproj.filters file."""
# 1. get a list of directories for all the files
- # 2. get all the headers in each of these dirs
+ # 2. get all the C++ files in each of these dirs
# 3. Output these lists of files to vcxproj and vcxproj.headers
# Note: order of these lists does not matter, VS will sort them anyway
dirs = set()
@@ -176,9 +259,9 @@ class ProjFileGenerator(object): # pylint: disable=too-many-instance-attributes
" because it does not exist.") % str(directory))
continue
- # Get all the header files
+ # Get all the C++ files
for file_name in os.listdir(directory):
- if self.__is_header(file_name):
+ if self.__cpp_file(file_name):
self.files.add(directory + "\\" + file_name)
# Make sure the set also includes the base directories
@@ -249,7 +332,7 @@ class ProjFileGenerator(object): # pylint: disable=too-many-instance-attributes
def main():
"""Execute Main program."""
if len(sys.argv) != 2:
- print r"Usage: python buildscripts\make_vcxproj.py FILE_NAME"
+ print(r"Usage: python buildscripts\make_vcxproj.py FILE_NAME")
return
with ProjFileGenerator(sys.argv[1]) as projfile: