diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2018-07-27 16:10:49 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2018-07-27 17:01:25 -0400 |
commit | b42420caf1018835da58ace37b51099931b904c0 (patch) | |
tree | 19f63f1ed27ccbd03db306394e9204676e113eb3 /buildscripts/make_vcxproj.py | |
parent | 077a5b00937a539ef004f986ac08f9c51cf0e92e (diff) | |
download | mongo-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.py | 95 |
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: |