aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--tools/cpp/wrapper/bin/call_python.bat26
-rw-r--r--tools/cpp/wrapper/bin/msvc_cl.bat17
-rw-r--r--tools/cpp/wrapper/bin/msvc_link.bat17
-rw-r--r--tools/cpp/wrapper/bin/msvc_nop.bat17
-rw-r--r--tools/cpp/wrapper/bin/pydir/msvc_cl.py128
-rw-r--r--tools/cpp/wrapper/bin/pydir/msvc_link.py128
-rw-r--r--tools/cpp/wrapper/bin/pydir/msvc_tools.py431
7 files changed, 764 insertions, 0 deletions
diff --git a/tools/cpp/wrapper/bin/call_python.bat b/tools/cpp/wrapper/bin/call_python.bat
new file mode 100644
index 0000000000..549917d10c
--- /dev/null
+++ b/tools/cpp/wrapper/bin/call_python.bat
@@ -0,0 +1,26 @@
+:: Copyright 2016 The Bazel Authors. All rights reserved.
+::
+:: Licensed under the Apache License, Version 2.0 (the "License");
+:: you may not use this file except in compliance with the License.
+:: You may obtain a copy of the License at
+::
+:: http://www.apache.org/licenses/LICENSE-2.0
+::
+:: Unless required by applicable law or agreed to in writing, software
+:: distributed under the License is distributed on an "AS IS" BASIS,
+:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+:: See the License for the specific language governing permissions and
+:: limitations under the License.
+
+:: Invoke the python script under pydir with the same basename
+@echo OFF
+set arg0=%1
+for %%F in ("%arg0%") do set DRIVER_BIN=%%~dpF
+
+for /F %%i in ("%arg0%") do set TOOLNAME=%%~ni
+
+set PYDIR="%DRIVER_BIN%pydir"
+
+::TODO(pcloudy): Remove hard code path, user should be able to configure
+if not defined MSVCPYTHON set MSVCPYTHON=C:\python_27_amd64\files\python.exe
+%MSVCPYTHON% "%PYDIR%\%TOOLNAME%.py" %*
diff --git a/tools/cpp/wrapper/bin/msvc_cl.bat b/tools/cpp/wrapper/bin/msvc_cl.bat
new file mode 100644
index 0000000000..50d116d5c1
--- /dev/null
+++ b/tools/cpp/wrapper/bin/msvc_cl.bat
@@ -0,0 +1,17 @@
+:: Copyright 2016 The Bazel Authors. All rights reserved.
+::
+:: Licensed under the Apache License, Version 2.0 (the "License");
+:: you may not use this file except in compliance with the License.
+:: You may obtain a copy of the License at
+::
+:: http://www.apache.org/licenses/LICENSE-2.0
+::
+:: Unless required by applicable law or agreed to in writing, software
+:: distributed under the License is distributed on an "AS IS" BASIS,
+:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+:: See the License for the specific language governing permissions and
+:: limitations under the License.
+
+:: Invoke the python script under pydir with the same basename
+@echo OFF
+call_python.bat %0 %*
diff --git a/tools/cpp/wrapper/bin/msvc_link.bat b/tools/cpp/wrapper/bin/msvc_link.bat
new file mode 100644
index 0000000000..50d116d5c1
--- /dev/null
+++ b/tools/cpp/wrapper/bin/msvc_link.bat
@@ -0,0 +1,17 @@
+:: Copyright 2016 The Bazel Authors. All rights reserved.
+::
+:: Licensed under the Apache License, Version 2.0 (the "License");
+:: you may not use this file except in compliance with the License.
+:: You may obtain a copy of the License at
+::
+:: http://www.apache.org/licenses/LICENSE-2.0
+::
+:: Unless required by applicable law or agreed to in writing, software
+:: distributed under the License is distributed on an "AS IS" BASIS,
+:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+:: See the License for the specific language governing permissions and
+:: limitations under the License.
+
+:: Invoke the python script under pydir with the same basename
+@echo OFF
+call_python.bat %0 %*
diff --git a/tools/cpp/wrapper/bin/msvc_nop.bat b/tools/cpp/wrapper/bin/msvc_nop.bat
new file mode 100644
index 0000000000..b422585831
--- /dev/null
+++ b/tools/cpp/wrapper/bin/msvc_nop.bat
@@ -0,0 +1,17 @@
+:: Copyright 2016 The Bazel Authors. All rights reserved.
+::
+:: Licensed under the Apache License, Version 2.0 (the "License");
+:: you may not use this file except in compliance with the License.
+:: You may obtain a copy of the License at
+::
+:: http://www.apache.org/licenses/LICENSE-2.0
+::
+:: Unless required by applicable law or agreed to in writing, software
+:: distributed under the License is distributed on an "AS IS" BASIS,
+:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+:: See the License for the specific language governing permissions and
+:: limitations under the License.
+
+:: Invoke the python script under pydir with the same basename
+@echo OFF
+echo IGNORING: %0 %*
diff --git a/tools/cpp/wrapper/bin/pydir/msvc_cl.py b/tools/cpp/wrapper/bin/pydir/msvc_cl.py
new file mode 100644
index 0000000000..9a867f6d07
--- /dev/null
+++ b/tools/cpp/wrapper/bin/pydir/msvc_cl.py
@@ -0,0 +1,128 @@
+# pylint: disable=g-bad-file-header
+# Copyright 2016 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Wrapper script for executing the Microsoft Compiler."""
+import os
+import sys
+import msvc_link
+import msvc_tools
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+sys.path.append(SCRIPT_DIR)
+
+GCCPATTERNS = [
+ ('-m(32|64)', ['$TARGET_ARCH']),
+ ('-Xcompilation-mode=(dbg|fastbuild|opt)', ['$COMPILATION_MODE']),
+ ('-msse', ['/arch:SSE']),
+ ('-msse2', ['/arch:SSE2']),
+ ('-c', ['/c']),
+ ('-D(.+)', ['/D$0']),
+ ('-U(.+)', ['/U$0']),
+ ('-E', ['/E']),
+ (('-o', '(.+)'), ['$COMPILE_OUTPUT0']),
+ ('-O0', ['/Od']),
+ ('-Os', ['/O1']),
+ ('-O2', ['/O2']),
+ ('-g', ['/MTd']),
+ ('-fexceptions', ['/U_HAS_EXCEPTIONS', '/D_HAS_EXCEPTIONS=1', '/EHsc']),
+ ('-fomit-frame-pointer', ['/Oy']),
+ ('-frandom-seed(.+)', []),
+ ('-fno-rtti', ['/GR-']),
+ ('-frtti', ['/GR']),
+ ('-fPIC', []),
+
+ # This is unneeded for Windows.
+ (('-isystem', '(.*)'), ['/I$PATH0']),
+ (('-iquote', '(.*)'), ['/I$PATH0']),
+ ('-I(.+)', ['/I$PATH0']),
+ (('-include', '(.+)'), ['/FI$PATH0']),
+ ('-MD', []),
+ (('-MF', '(.+)'), ['$GENERATE_DEPS0']),
+ ('-w', ['/w']),
+ ('-Wall', ['/Wall']),
+ ('-Wsign-compare', ['/we4018']),
+ ('-Wno-sign-compare', ['/wd4018']),
+ ('-Wconversion', ['/we4244', '/we4267']),
+ ('-Wno-conversion', ['/wd4244', '/wd4267']),
+ ('-Wno-sign-conversion', []),
+ ('-Wno-implicit-fallthrough', []),
+ ('-Wno-implicit-function-declaration', []),
+ ('-Wcovered-switch-default', ['/we4062']),
+ ('-Wno-covered-switch-default', ['/wd4062']),
+ ('-Wno-error', []),
+ ('-Wno-invalid-offsetof', []),
+ ('-Wno-overloaded-virtual', []),
+ ('-Wno-reorder', []),
+ ('-Wno-string-plus-int', []),
+ ('-Wl,S', []), # Stripping is unnecessary since msvc uses pdb files.
+ ('-Wl,-rpath(.+)', []),
+ (('-x', r'c\+\+-header'), ['$CREATE_PRECOMPILED_HEADER']),
+ ('-B(.+)', []),
+ ('-static', []),
+ ('-shared', []),
+]
+
+
+def _IsLink(args):
+ """Determines whether we need to link rather than compile.
+
+ A set of arguments is for linking if they contain -static, -shared, are adding
+ adding library search paths through -L, or libraries via -l.
+
+ Args:
+ args: List of arguments
+
+ Returns:
+ Boolean whether this is a link operation or not.
+ """
+ for arg in args:
+ # Certain flags indicate we are linking.
+ if (arg in ['-shared', '-static'] or arg[:2] in ['-l', '-L'] or
+ arg[:3] == '-Wl'):
+ return True
+ return False
+
+
+class MsvcCompiler(msvc_tools.WindowsRunner):
+ """Driver for the Microsoft compiler."""
+
+ def Run(self, argv):
+ """Runs the compiler using the passed clang/gcc style argument list.
+
+ Args:
+ argv: List of arguments
+
+ Returns:
+ The return code of the compilation.
+
+ Raises:
+ ValueError: if target architecture isn't specified
+ """
+ parser = msvc_tools.ArgParser(self, argv, GCCPATTERNS)
+ if not parser.target_arch:
+ raise ValueError('Must specify target architecture (-m32 or -m64)')
+
+ return self.RunBinary('cl', parser.options, parser.target_arch, parser)
+
+
+def main(argv):
+ # If we are supposed to link create a static library.
+ if _IsLink(argv[1:]):
+ return msvc_link.main(argv)
+ else:
+ return MsvcCompiler().Run(argv[1:])
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:])) # need to skip the first argument
diff --git a/tools/cpp/wrapper/bin/pydir/msvc_link.py b/tools/cpp/wrapper/bin/pydir/msvc_link.py
new file mode 100644
index 0000000000..8c61fcb383
--- /dev/null
+++ b/tools/cpp/wrapper/bin/pydir/msvc_link.py
@@ -0,0 +1,128 @@
+# pylint: disable=g-bad-file-header
+# Copyright 2016 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Wrapper script for executing the Microsoft Linker."""
+
+import os
+import sys
+import msvc_tools
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+sys.path.append(SCRIPT_DIR)
+
+LINKPATTERNS = [
+ ('-m(32|64)', ['$TARGET_ARCH']),
+ ('-Xcompilation-mode=(dbg|fastbuild|opt)', ['$COMPILATION_MODE']),
+ (('rcs.*', '(.+)'), ['/OUT:$PATH0']),
+ (('-o', '(.+)'), ['/OUT:$PATH0']),
+ ('-B(.+)', []),
+ ('-l(.+)', ['lib$0.so']),
+ ('-L(.+)', ['/LIBPATH:$PATH0']),
+ ('-static', []),
+ ('-shared', []),
+ ('-whole-archive', []),
+ ('-no-whole-archive', []),
+ ('-rdynamic', []),
+ (r'-Wl,(.+)\.lib', ['$0.lib']),
+ ('-Wl,@(.+)', ['$LOAD_PARAMS0']),
+ ('@(.+)', ['$LOAD_PARAMS0']),
+ ('-Wl,-rpath(.+)', []),
+ ('-Wl,-S', []), # Debug symbols are in pdb files.
+ ('-Wl,/SUBSYSTEM:(WINDOWS|CONSOLE)', ['/SUBSYSTEM:$0']),
+]
+
+
+class MsvcLinker(msvc_tools.WindowsRunner):
+ """Driver for the Microsoft linker."""
+
+ def Run(self, argv):
+ """Runs the linker using the passed clang/gcc style argument list.
+
+ Args:
+ argv: List of arguments
+
+ Returns:
+ The return code of the link.
+
+ Raises:
+ ValueError: if target architecture or compile mode isn't specified
+ """
+ # For now assume we are building a library.
+ tool = 'lib'
+ default_args = ['/nologo']
+
+ # Build argument list.
+ parser = msvc_tools.ArgParser(self, argv, LINKPATTERNS)
+
+ # Find the output file name.
+ name = ''
+ for arg in parser.options:
+ if '/OUT:' in arg:
+ name = arg[5:]
+ break
+ if not name:
+ raise msvc_tools.Error('No output file name specified!')
+ # Check if the library is empty, which is what happens when we create header
+ # or intermediate link-only libraries.
+ if (len(parser.options) == 2 and parser.options[0].startswith('/OUT') and
+ parser.options[1].startswith('/M')):
+ # Just "touch" the library to create the file.
+ with open(name, 'w'):
+ os.utime(name, None)
+ else:
+ # If the output name ends in .so, .lo, or .a, it is a library, otherwise
+ # we need to use link to create an executable.
+ if os.path.splitext(name)[1] not in ['.a', '.lo', '.so']:
+ tool = 'link'
+
+ if not parser.target_arch:
+ raise ValueError('Must specify target architecture (-m32 or -m64)')
+
+ # Append explicit machine type.
+ if parser.target_arch == 'x64':
+ default_args.append('/MACHINE:X64')
+ else:
+ default_args.append('/MACHINE:X86')
+
+ # Args for buildng a console application. These must appear here since
+ # blaze will not properly pass them to the linker.
+ # /SUBSYSTEM:CONSOLE: Build a console application.
+ default_args += ['/SUBSYSTEM:CONSOLE']
+ # If there is no .o on the command line, then we need to add the
+ # run-time library for the target. Without this the linker gives a
+ # LNK4001 error and cannot find an entry point.
+ for arg in parser.options:
+ if arg.endswith('.o'):
+ break
+ else:
+ if not parser.compilation_mode:
+ raise ValueError('Must specify compilation mode '
+ '(-Xcompilation-mode={dbg,fastbuild,opt})')
+
+ if parser.compilation_mode == 'dbg':
+ default_args.insert(0, 'libcmtd.lib')
+ else:
+ default_args.insert(0, 'libcmt.lib')
+
+ return self.RunBinary(tool, default_args + parser.options,
+ parser.target_arch, parser)
+
+
+def main(argv):
+ return MsvcLinker().Run(argv[1:])
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:])) # need to skip the first argument
diff --git a/tools/cpp/wrapper/bin/pydir/msvc_tools.py b/tools/cpp/wrapper/bin/pydir/msvc_tools.py
new file mode 100644
index 0000000000..cca2c68ba7
--- /dev/null
+++ b/tools/cpp/wrapper/bin/pydir/msvc_tools.py
@@ -0,0 +1,431 @@
+# pylint: disable=g-bad-file-header
+# pylint: disable=cell-var-from-loop
+# Copyright 2016 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tools for working with the Microsoft Visual C++ toolchain."""
+
+import ntpath
+import os
+import re
+import subprocess
+import sys
+
+MAX_PATH = 260 # The maximum number of characters in a Windows path.
+MAX_OPTION_LENGTH = 10 # The maximum length of a compiler/linker option.
+MAX_DRIVE_LENGTH = 3 # The maximum length of a drive.
+ASSEMBLY_AS_C_SOURCE = '/Tc'
+LIB_SUFFIX = '.lib'
+
+
+class Error(Exception):
+ """Base class for all script-specific errors."""
+ pass
+
+
+class ArgParser(object):
+ """Class that parses gcc/clang-style options for a Windows.
+
+ The particular substitutions that are needed are passed to the object.
+ """
+
+ def __init__(self, driver, argv, substitutions):
+ self.driver = driver
+ self.substitutions = substitutions
+ self.options = []
+ self.target_arch = None
+ self.compilation_mode = None
+ self.deps_file = None
+ self.output_file = None
+ self._ParseArgs(argv)
+
+ def _MatchOneArg(self, args):
+ """Finds a pattern which matches the beginning elements of args.
+
+ Args:
+ args: A list of arguments to replace.
+
+ Returns:
+ A tuple of (number of arguments parsed, action, match groups).
+ """
+ for (regex, action) in self.substitutions:
+ if isinstance(regex, str):
+ regex = [regex]
+ j = 0
+ matches = []
+ for r in regex:
+ if j < len(args):
+ match = re.compile('^' + r + '$').match(args[j])
+ else:
+ match = None
+ matches.append(match)
+ j += 1
+ if None in matches:
+ continue
+ groups = []
+ for m in matches:
+ groups.extend(m.groups())
+ return (len(regex), action, groups)
+ return (0, '', [])
+
+ def _ParseArgs(self, argv):
+ """Parses argv and replaces its elements using special tokens.
+
+ The following is a list of supported tokens. The format is $TOKEN%d, where
+ %d is the 0-based group number from regex matches of the pattern.
+ $CREATE_PATH%d: Touches a file at the path in the matching token.
+ $LOAD_PARAMS%d: Loads an ld-style params file and appends all arguments to
+ the current argument list by recursively calling
+ _ParseArgs.
+ $%d : Numeric token that just replaces the match group with
+ the value specified in the match list.
+ $PATH%d : Replaces the match with a Windows-normalized version of
+ the match; assumes that the match is a path.
+ $PATH%d_NO_EXT: Same as $PATH but strips out any file extension.
+ $TARGET_ARCH : Set self.target_arch to 'x86' or 'x64' for '-m32' and
+ '-m64', respectively.
+ $COMPILE_OUTPUT%d: Sets the output name of a compilation step.
+ $COMPILATION_MODE: Sets self.compilation_mode from the value of a
+ '-Xcompilation-mode=' flag.
+ $CREATE_PRECOMPILED_HEADER: Informs the system that we are generating a
+ precompiled header rather than an object file.
+ $GENERATE_DEPS%d: Generates a gcc-style .d file containing dependencies.
+
+ Args:
+ argv: A list of arguments to replace.
+
+ Returns:
+ A list of replaced arguments to pass to the target command.
+
+ Raises:
+ Error: if wrong arguments found
+ """
+ i = 0
+ matched = []
+ unmatched = []
+ files = []
+ is_pch = False
+ while i < len(argv):
+ num_matched, action, groups = self._MatchOneArg(argv[i:])
+ arg = argv[i]
+ if num_matched == 0:
+ # Strip out any .a's that have 0 size, they are header or intermediate
+ # dependency libraries and don't contain any code. 0-length files are
+ # considered corrupt by the linker (error LNK1136).
+ if (os.path.isfile(arg) and os.path.splitext(arg)[1] == '.a' and
+ os.path.getsize(arg) == 0):
+ i += 1
+ continue
+
+ # If the argument is an absolute path, then add it directly.
+ if arg[0] == '/':
+ self.AddOpt(arg)
+ elif os.path.isfile(arg):
+ path = self.NormPath(arg)
+ ext = os.path.splitext(arg)[1].lower()
+ if ext in ['.s']:
+ # Treat assembly files as C source files using a special option.
+ path = ASSEMBLY_AS_C_SOURCE + path
+ # If this is an actual file on disk then just pass it to the tool.
+ files.append(path)
+ elif not arg.endswith(LIB_SUFFIX):
+ # Ignore .lib files.
+ unmatched.append(arg)
+ i += 1
+ continue
+ matched += argv[i:i + num_matched]
+ # Handle special options.
+ for entry in action:
+ if entry == '$CREATE_PRECOMPILED_HEADER':
+ # The PCH flag comes _first_ on blaze-generated command-lines, so all
+ # we can do is set a flag here since we have not yet parsed any other
+ # options.
+ is_pch = True
+ continue
+
+ if entry == '$TARGET_ARCH':
+ if arg == '-m32':
+ self.target_arch = 'x86'
+ elif arg == '-m64':
+ self.target_arch = 'x64'
+ else:
+ raise Error('Unknown target arch flag: %r' % arg)
+ continue
+
+ if entry == '$COMPILATION_MODE':
+ empty, prefix, mode = arg.partition('-Xcompilation-mode=')
+ if empty or not prefix or mode not in ['dbg', 'fastbuild', 'opt']:
+ raise Error('Invalid compilation mode flag: %r' % arg)
+ self.compilation_mode = mode
+ continue
+
+ if not groups:
+ self.options.append(entry)
+ else:
+ # Substitute special tokens.
+ for g in xrange(0, len(groups)):
+ value = groups[g]
+
+ # Check for special tokens.
+ if entry == ('$CREATE_PATH%d' % g):
+ with open(value, 'a'):
+ os.utime(value, None)
+ continue
+
+ if entry == ('$LOAD_PARAMS%d' % g):
+ try:
+ # The arguments in the params file need to be processed as
+ # regular command-line arguments.
+ params = [line.rstrip() for line in open(value, 'r')]
+ self._ParseArgs(params)
+ except IOError, e:
+ print 'Could not open', value, 'for reading:', str(e)
+ exit(-1)
+ continue
+
+ # Depending on whether we are creating precompiled headers cl.exe
+ # needs different options for specifying the output file.
+ if entry == ('$COMPILE_OUTPUT%d' % g):
+ if is_pch:
+ # Just touch the PCH file so that blaze is happy.
+ with open(value, 'a'):
+ os.utime(value, None)
+ # Exit since we don't want to actually try to process a PCH.
+ sys.exit(0)
+ else:
+ self.output_file = value
+ self.options.append('/Fo%s' % self.NormPath(value))
+ self.options.append('/Fd%s.pdb' %
+ self.NormPath(os.path.splitext(value)[0]))
+ continue
+
+ if entry == ('$GENERATE_DEPS%d' % g):
+ self.options.append('/showIncludes')
+ self.deps_file = value
+ continue
+
+ # Regular substitution.
+ patterns = {
+ '$%d' % g: value,
+ '$PATH%d_NO_EXT' % g: self.NormPath(os.path.splitext(value)[0]),
+ '$PATH%d' % g: self.NormPath(value),
+ }
+ pattern = re.compile('(%s)' %
+ '|'.join(map(re.escape, patterns.keys())))
+ result = pattern.sub(lambda x: patterns[x.group(0)], entry)
+ self.options.append(result)
+ i += num_matched
+ if unmatched:
+ print 'Warning: Unmatched arguments: ' + ' '.join(unmatched)
+
+ # Use the proper runtime flag depending on compilation mode. If the
+ # compilation is happening in debug mode, this flag already exists. If not,
+ # then we must add it.
+ if '/MT' not in self.options and '/MTd' not in self.options:
+ self.AddOpt('/MT')
+ # Add in any parsed files
+ self.options += files
+
+ def NormPath(self, path):
+ """Uses the current WindowsRunner to normalize the passed path.
+
+ Args:
+ path: the path to normalize.
+
+ Returns:
+ A normalized string representing a path suitable for Windows.
+ """
+ return self.driver.NormPath(path)
+
+ def AddOpt(self, option):
+ """Adds a single option.
+
+ Args:
+ option: the option to add.
+ """
+ self.options.append(option)
+
+
+class WindowsRunner(object):
+ """Base class that encapsulates the details of running a binary."""
+
+ def NormPath(self, path):
+ """Normalizes an input unix style path to a < 260 char Windows format.
+
+ Windows paths cannot be greater than 260 characters.
+
+ Args:
+ path: A path in unix format.
+
+ Returns:
+ An absolute path in Windows format, rooted from some
+ directory.
+
+ Raises:
+ Error: if path is too long
+ """
+ abspath = os.path.abspath(path)
+ long_path = abspath.replace('\\', '\\\\')
+ # We must allow for the drive letter as well, which is three characters, and
+ # the length of any compiler option ahead of the path,
+
+ if len(long_path) + MAX_DRIVE_LENGTH + MAX_OPTION_LENGTH < MAX_PATH:
+ return long_path
+ else:
+ # TODO(pcloudy):
+ # Find a new way to deal with long path in real windows
+ print 'Error: path is too long:' + long_path
+ raise Error('path is too long: ' + long_path)
+ return None
+
+ def SetupEnvironment(self, build_arch):
+ pass
+
+ def RunBinary(self, binary, args, build_arch, parser):
+ """Runs binary under wine with the passed args.
+
+ Args:
+ binary: The binary to run.
+ args: The arguments to pass to binary.
+ build_arch: Either 'x64' or 'x86', which binary architecture to build for.
+ parser: An ArgParser that contains parsed arguments.
+
+ Returns:
+ The return code from executing binary.
+ """
+ # Filter out some not-so-useful cl windows messages.
+ filters = [
+ '.*warning LNK4006: __NULL_IMPORT_DESCRIPTOR already defined.*\n',
+ '.*warning LNK4044: unrecognized option \'/MT\'; ignored.*\n',
+ '.*warning LNK4044: unrecognized option \'/link\'; ignored.*\n',
+ '.*warning LNK4221: This object file does not define any '
+ 'previously.*\n',
+ '\r\n',
+ '\n\r',
+ ]
+
+ # Check again the arguments are within MAX_PATH.
+ for arg in args:
+ if len(arg) > MAX_PATH:
+ print('Warning: arg "' + arg + '" is > than 260 characters (' +
+ str(len(arg)) + '); programs may crash with long arguments')
+ if os.path.splitext(arg)[1].lower() in ['.c', '.cc', '.cpp', '.s']:
+ # cl.exe prints out the file name it is compiling; add that to the
+ # filter.
+ name = arg.rpartition(ntpath.sep)[2]
+ filters.append(name)
+
+ if '/w' in args:
+ args = [arg for arg in args if arg not in ['/W2', '/W3', '/W4']]
+
+ # Setup the Windows paths and the build environment.
+ # TODO(pcloudy): make these paths configurable
+ self.SetupEnvironment(build_arch)
+ path = [
+ 'C:\\Program Files\\Microsoft DNX\\Dnvm\\',
+ 'C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v10.0A\\bin\\NETFX '
+ '4.6.1 Tools\\x64\\',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\IDE',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio '
+ '14.0\\Common7\\IDE\\CommonExtensions\\Microsoft\\TestWindow',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Team '
+ 'Tools\\Performance Tools\\x64',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\amd64',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\VCPackages',
+ 'C:\\Program Files (x86)\\MSBuild\\14.0\\bin\\amd64',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x64',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\Windows Performance '
+ 'Toolkit\\',
+ 'C:\\Windows', 'C:\\Windows\\system32', 'C:\\Windows\\System32\\Wbem'
+ ]
+
+ include = [
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio '
+ '14.0\\VC\\ATLMFC\\INCLUDE',
+ 'C:\\Program Files (x86)\\Windows Kits\\10'
+ '\\include\\10.0.10240.0\\ucrt',
+ 'C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\include\\um',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\shared',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\um',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\winrt',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\INCLUDE',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio '
+ '14.0\\VC\\ATLMFC\\INCLUDE',
+ 'C:\\Program Files (x86)\\Windows Kits\\10\\'
+ 'include\\10.0.10240.0\\ucrt',
+ 'C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\include\\um',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\shared',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\um',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\\\winrt'
+ ]
+
+ lib = [
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio '
+ '14.0\\VC\\ATLMFC\\LIB\\amd64',
+ 'C:\\Program Files (x86)\\Windows Kits\\10\\lib'
+ '\\10.0.10240.0\\ucrt\\x64',
+ 'C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\lib\\um\\x64',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\lib\\winv6.3\\um\\x64',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\LIB',
+ 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0'
+ '\\VC\\ATLMFC\\LIB',
+ 'C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.10240.0'
+ '\\ucrt\\x86',
+ 'C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.6.1\\lib\\um\\x86',
+ 'C:\\Program Files (x86)\\Windows Kits\\8.1\\lib\\winv6.3\\um\\x86'
+ ]
+
+ build_env = os.environ.copy()
+ build_env['PATH'] += ';'.join[build_env['PATH'] + path]
+ build_env['INCLUDE'] = ';'.join(include)
+ build_env['LIB'] = ';'.join(lib)
+ build_env['TEMP'] = 'C:\\Windows\\Temp'
+ build_env['TMP'] = 'C:\\Windows\\Temp'
+ # Construct a large regular expression for all filters.
+ output_filter = re.compile('(' + ')|('.join(filters) + ')')
+ includes_filter = re.compile(r'Note: including file:\s+(.*)')
+ # Run the command.
+ cmd = [binary] + args
+ # Save stderr output to a temporary in case we need it.
+ proc = subprocess.Popen(cmd,
+ env=build_env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ shell=True)
+ deps = []
+ for line in proc.stdout:
+ if not output_filter.match(line):
+ includes = includes_filter.match(line)
+ if includes:
+ filename = includes.group(1).rstrip()
+ deps += [filename]
+ else:
+ print line.rstrip()
+ proc.wait()
+
+ # Generate deps file if requested.
+ if parser.deps_file:
+ with open(parser.deps_file, 'w') as deps_file:
+ # Start with the name of the output file.
+ deps_file.write(parser.output_file + ': \\\n')
+ for i, dep in enumerate(deps):
+ dep = dep.replace('\\', '/').replace(' ', '\\ ')
+ deps_file.write(' ' + dep)
+ if i < len(deps) - 1:
+ deps_file.write(' \\')
+ deps_file.write('\n')
+
+ return proc.returncode