# Generate vcxproj and vcxproj.filters files for browsing code in Visual Studio 2015.
# To build mongodb, you must use scons. You can use this project to navigate code during debugging.
#
# HOW TO USE
#
# First, you need a compile_commands.json file, to generate run the following command:
# scons compiledb
#
# Next, run the following command
# python buildscripts/make_vcxproj.py FILE_NAME
#
# where FILE_NAME is the of the file to generate e.g., "mongod"
#
import json
import os
import re
import sys
import uuid
VCXPROJ_FOOTER = r"""
"""
def get_defines(args):
"""Parse a compiler argument list looking for defines"""
ret = set()
for arg in args:
if arg.startswith('/D'):
ret.add(arg[2:])
return ret
def get_includes(args):
"""Parse a compiler argument list looking for includes"""
ret = set()
for arg in args:
if arg.startswith('/I'):
ret.add(arg[2:])
return ret
class ProjFileGenerator(object):
"""Generate a .vcxproj and .vcxprof.filters file"""
def __init__(self, target):
# we handle DEBUG in the vcxproj header:
self.common_defines = set()
self.common_defines.add("DEBUG")
self.common_defines.add("_DEBUG")
self.includes = set()
self.target = target
self.compiles = []
self.files = set()
self.all_defines = set()
self.vcxproj = None
self.filters = None
self.all_defines = set(self.common_defines)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.vcxproj = open(self.target + ".vcxproj", "wb")
with open('buildscripts/vcxproj.header', 'r') as header_file:
header_str = header_file.read()
header_str = header_str.replace("%_TARGET_%", self.target)
header_str = header_str.replace("%AdditionalIncludeDirectories%",
';'.join(sorted(self.includes)))
self.vcxproj.write(header_str)
common_defines = self.all_defines
for c in self.compiles:
common_defines = common_defines.intersection(c['defines'])
self.vcxproj.write("\n")
self.vcxproj.write(""
+ ';'.join(common_defines) + ";%(PreprocessorDefinitions)\n")
self.vcxproj.write("\n")
self.vcxproj.write(" \n")
for command in self.compiles:
defines = command["defines"].difference(common_defines)
if len(defines) > 0:
self.vcxproj.write(" " +
';'.join(defines) +
";%(PreprocessorDefinitions)" +
"\n")
else:
self.vcxproj.write(" \n")
self.vcxproj.write(" \n")
self.filters = open(self.target + ".vcxproj.filters", "wb")
self.filters.write("\n")
self.filters.write("\n")
self.__write_filters()
self.vcxproj.write(VCXPROJ_FOOTER)
self.vcxproj.close()
self.filters.write("\n")
self.filters.close()
def parse_line(self, line):
"""Parse a build line"""
if line.startswith("cl"):
self.__parse_cl_line(line[3:])
def __parse_cl_line(self, line):
"""Parse a compiler line"""
# Get the file we are compilong
file_name = re.search(r"/c ([\w\\.-]+) ", line).group(1)
# Skip files made by scons for configure testing
if "sconf_temp" in file_name:
return
self.files.add(file_name)
args = line.split(' ')
file_defines = set()
for arg in get_defines(args):
if arg not in self.common_defines:
file_defines.add(arg)
self.all_defines = self.all_defines.union(file_defines)
for arg in get_includes(args):
self.includes.add(arg)
self.compiles.append({"file" : file_name, "defines" : file_defines})
def __is_header(self, name):
"""Is this a header file?"""
headers = [".h", ".hpp", ".hh", ".hxx"]
for header in headers:
if name.endswith(header):
return True
return False
def __write_filters(self):
"""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
# 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()
scons_files = set()
for file_name in self.files:
dirs.add(os.path.dirname(file_name))
base_dirs = set()
for directory in dirs:
if not os.path.exists(directory):
print(("Warning: skipping include file scan for directory '%s'" +
" because it does not exist.") % str(directory))
continue
# Get all the header files
for file_name in os.listdir(directory):
if self.__is_header(file_name):
self.files.add(directory + "\\" + file_name)
# Make sure the set also includes the base directories
# (i.e. src/mongo and src as examples)
base_name = os.path.dirname(directory)
while base_name:
base_dirs.add(base_name)
base_name = os.path.dirname(base_name)
dirs = dirs.union(base_dirs)
# Get all the scons files
for directory in dirs:
if os.path.exists(directory):
for file_name in os.listdir(directory):
if "SConstruct" == file_name or "SConscript" in file_name:
scons_files.add(directory + "\\" + file_name)
scons_files.add("SConstruct")
# Write a list of directory entries with unique guids
self.filters.write(" \n")
for file_name in sorted(dirs):
self.filters.write(" \n" % file_name)
self.filters.write(" {%s}\n" % uuid.uuid4())
self.filters.write(" \n")
self.filters.write(" \n")
# Write a list of files to compile
self.filters.write(" \n")
for file_name in sorted(self.files):
if not self.__is_header(file_name):
self.filters.write(" \n" % file_name)
self.filters.write(" %s\n" % os.path.dirname(file_name))
self.filters.write(" \n")
self.filters.write(" \n")
# Write a list of headers
self.filters.write(" \n")
for file_name in sorted(self.files):
if self.__is_header(file_name):
self.filters.write(" \n" % file_name)
self.filters.write(" %s\n" % os.path.dirname(file_name))
self.filters.write(" \n")
self.filters.write(" \n")
# Write a list of scons files
self.filters.write(" \n")
for file_name in sorted(scons_files):
self.filters.write(" \n" % file_name)
self.filters.write(" %s\n" % os.path.dirname(file_name))
self.filters.write(" \n")
self.filters.write(" \n")
# Write a list of headers into the vcxproj
self.vcxproj.write(" \n")
for file_name in sorted(self.files):
if self.__is_header(file_name):
self.vcxproj.write(" \n" % file_name)
self.vcxproj.write(" \n")
# Write a list of scons files into the vcxproj
self.vcxproj.write(" \n")
for file_name in sorted(scons_files):
self.vcxproj.write(" \n" % file_name)
self.vcxproj.write(" \n")
def main():
if len(sys.argv) != 2:
print r"Usage: python buildscripts\make_vcxproj.py FILE_NAME"
return
with ProjFileGenerator(sys.argv[1]) as projfile:
with open("compile_commands.json", "rb") as sjh:
contents = sjh.read().decode('utf-8')
commands = json.loads(contents)
for command in commands:
command_str = command["command"]
projfile.parse_line(command_str)
main()