diff --git a/src/blade/backend.py b/src/blade/backend.py index 699ff6ae..1aa5a0f6 100644 --- a/src/blade/backend.py +++ b/src/blade/backend.py @@ -17,6 +17,7 @@ from __future__ import absolute_import from __future__ import print_function +import codecs import os import subprocess import sys @@ -730,9 +731,12 @@ def generate_cuda_rules(self): description='CUDA LINK SHARED ${out}') def _builtin_command(self, builder, args=''): - cmd = ['PYTHONPATH=%s:$$PYTHONPATH' % self.blade_path] + if os.name == 'nt': + cmd = ['cmd /c set PYTHONPATH=%s:%%PYTHONPATH%%;' % self.blade_path] + else: + cmd = ['PYTHONPATH=%s:$$PYTHONPATH' % self.blade_path] python = os.environ.get('BLADE_PYTHON_INTERPRETER') or sys.executable - cmd.append('%s -m blade.builtin_tools %s' % (python, builder)) + cmd.append('"%s" -m blade.builtin_tools %s' % (python, builder)) if args: cmd.append(args) else: @@ -789,6 +793,6 @@ def generate_build_code(self): def generate_build_script(self): """Generate build script for underlying build system.""" code = self.generate_build_code() - script = open(self.script_path, 'w') + script = codecs.open(self.script_path, 'w', 'utf-8') script.writelines(code) script.close() diff --git a/src/blade/build_manager.py b/src/blade/build_manager.py index 4fecead6..2cce3960 100644 --- a/src/blade/build_manager.py +++ b/src/blade/build_manager.py @@ -24,8 +24,8 @@ from blade import maven from blade import ninja_runner from blade import target_pattern +from blade import toolchain from blade.binary_runner import BinaryRunner -from blade.toolchain import ToolChain from blade.build_accelerator import BuildAccelerator from blade.dependency_analyzer import analyze_deps from blade.load_build_files import load_targets @@ -101,7 +101,7 @@ def __init__(self, # Indicate whether the deps list is expanded by expander or not self.__targets_expanded = False - self.__build_toolchain = ToolChain() + self.__build_toolchain = toolchain.default() self.build_accelerator = BuildAccelerator(self.__build_toolchain) self.__build_jobs_num = 0 diff --git a/src/blade/command_line.py b/src/blade/command_line.py index 018715f1..5d828cd7 100644 --- a/src/blade/command_line.py +++ b/src/blade/command_line.py @@ -25,7 +25,7 @@ from blade import console from blade import constants from blade.toolchain import BuildArchitecture -from blade.toolchain import ToolChain +from blade.toolchain import CcToolChain # The 'version.py' is generated by the dist script, then it only exists in blade.zip try: @@ -98,6 +98,11 @@ def _check_test_options(self, options, targets): def _check_plat_and_profile_options(self, options, targets): """check platform and profile options.""" + arch = 'x64' + bits = 32 + options.arch = arch + options.bits = bits + return compiler_arch = self._compiler_target_arch() arch = BuildArchitecture.get_canonical_architecture(compiler_arch) if arch is None: @@ -414,6 +419,7 @@ def _add_dump_arguments(self, parser): group.add_argument( '--all-tags', dest='dump_all_tags', default=False, action='store_true', help='Dump all tags of targets in json format') + def _build_arg_parser(self): """Add command options, add options whthin this method.""" blade_cmd_help = 'blade [options...] [targets...]' @@ -462,7 +468,7 @@ def _build_arg_parser(self): def _compiler_target_arch(self): """Compiler(gcc) target architecture.""" - arch = ToolChain.get_cc_target_arch() + arch = CcToolChain.get_cc_target_arch() pos = arch.find('-') if pos == -1: console.fatal('Unknown target architecture %s from gcc.' % arch) diff --git a/src/blade/console.py b/src/blade/console.py index c18e6d3e..29bdf6cc 100644 --- a/src/blade/console.py +++ b/src/blade/console.py @@ -17,6 +17,7 @@ import datetime import os +import subprocess import sys import time @@ -25,10 +26,34 @@ ############################################################################## -# Global color enabled or not -_color_enabled = (sys.stdout.isatty() and - os.environ.get('TERM') not in ('emacs', 'dumb')) +def _windows_console_support_ansi_color(): + from ctypes import byref, windll, wintypes + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + INVALID_HANDLE_VALUE = -1 + + handle = windll.kernel32.GetStdHandle(subprocess.STD_OUTPUT_HANDLE) + if handle == INVALID_HANDLE_VALUE: + return False + + mode = wintypes.DWORD() + if not windll.kernel32.GetConsoleMode(handle, byref(mode)): + return False + if not (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING): + if windll.kernel32.SetConsoleMode( + handle, + mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0: + print('kernel32.SetConsoleMode to enable ANSI sequences failed', + file=sys.stderr) + return True + +def _console_support_ansi_color(): + if os.name == 'nt': + return _windows_console_support_ansi_color() + return sys.stdout.isatty() and os.environ.get('TERM') not in ('emacs', 'dumb') + +# Global color enabled or not +_color_enabled = _console_support_ansi_color() # See http://en.wikipedia.org/wiki/ANSI_escape_code # colors diff --git a/src/blade/toolchain.py b/src/blade/toolchain.py index 303c07be..d03a3a20 100644 --- a/src/blade/toolchain.py +++ b/src/blade/toolchain.py @@ -104,7 +104,47 @@ def get_model_architecture(arch, bits): return None -class ToolChain(object): +class CcToolChain(object): + """The build platform handles and gets the platform information.""" + + def __init__(self): + self.cc = '' + self.cxx = '' + self.ld = '' + self.cc_version = '' + self.ar = '' + + @staticmethod + def get_cc_target_arch(): + """Get the cc target architecture.""" + cc = CcToolChain._get_cc_command('CC', 'gcc') + returncode, stdout, stderr = run_command(cc + ' -dumpmachine', shell=True) + if returncode == 0: + return stdout.strip() + return '' + + def get_cc_commands(self): + return self.cc, self.cxx, self.ld + + def get_cc(self): + return self.cc + + def get_cc_version(self): + return self.cc_version + + def get_ar(self): + return self.ar + + def cc_is(self, vendor): + """Is cc is used for C/C++ compilation match vendor.""" + return vendor in self.cc + + def filter_cc_flags(self, flag_list, language='c'): + """Filter out the unrecognized compilation flags.""" + raise NotImplementedError + + +class CcToolChainGcc(CcToolChain): """The build platform handles and gets the platform information.""" def __init__(self): @@ -132,7 +172,78 @@ def _get_cc_version(self): @staticmethod def get_cc_target_arch(): """Get the cc target architecture.""" - cc = ToolChain._get_cc_command('CC', 'gcc') + cc = CcToolChain._get_cc_command('CC', 'gcc') + returncode, stdout, stderr = run_command(cc + ' -dumpmachine', shell=True) + if returncode == 0: + return stdout.strip() + return '' + + def filter_cc_flags(self, flag_list, language='c'): + """Filter out the unrecognized compilation flags.""" + flag_list = var_to_list(flag_list) + valid_flags, unrecognized_flags = [], [] + + # Put compilation output into test.o instead of /dev/null + # because the command line with '--coverage' below exit + # with status 1 which makes '--coverage' unsupported + # echo "int main() { return 0; }" | gcc -o /dev/null -c -x c --coverage - > /dev/null 2>&1 + fd, obj = tempfile.mkstemp('.o', 'filter_cc_flags_test') + cmd = ('export LC_ALL=C; echo "int main() { return 0; }" | ' + '%s -o %s -c -x %s -Werror %s -' % ( + self.cc, obj, language, ' '.join(flag_list))) + returncode, _, stderr = run_command(cmd, shell=True) + + try: + # In case of error, the `.o` file will be deleted by the compiler + os.remove(obj) + except OSError: + pass + os.close(fd) + + if returncode == 0: + return flag_list + for flag in flag_list: + # Example error messages: + # clang: warning: unknown warning option '-Wzzz' [-Wunknown-warning-option] + # gcc: gcc: error: unrecognized command line option '-Wxxx' + if " option '%s'" % flag in stderr: + unrecognized_flags.append(flag) + else: + valid_flags.append(flag) + + if unrecognized_flags: + console.warning('config: Unrecognized %s flags: %s' % ( + language, ', '.join(unrecognized_flags))) + + return valid_flags + + +class CcToolChainMsvc(CcToolChain): + """The build platform handles and gets the platform information.""" + + def __init__(self): + self.cc = 'cl.exe' + self.cxx = 'cl.exe' + self.ld = 'link.exe' + self.ar = 'lib.exe' + self.rc = 'rc.exe' + self.cc_version = self._get_cc_version() + + def _get_cc_version(self): + version = '' + returncode, stdout, stderr = run_command(self.cc, shell=True) + if returncode == 0: + m = re.search('Compiler Version ([\d.]+)', stderr.strip()) + if m: + version = m.group(1) + if not version: + console.fatal('Failed to obtain cc toolchain.') + return version + + @staticmethod + def get_cc_target_arch(): + """Get the cc target architecture.""" + cc = CcToolChain._get_cc_command('CC', 'gcc') returncode, stdout, stderr = run_command(cc + ' -dumpmachine', shell=True) if returncode == 0: return stdout.strip() @@ -163,32 +274,36 @@ def filter_cc_flags(self, flag_list, language='c'): # because the command line with '--coverage' below exit # with status 1 which makes '--coverage' unsupported # echo "int main() { return 0; }" | gcc -o /dev/null -c -x c --coverage - > /dev/null 2>&1 - fd, obj = tempfile.mkstemp('.o', 'filter_cc_flags_test') - cmd = ('export LC_ALL=C; echo "int main() { return 0; }" | ' - '%s -o %s -c -x %s -Werror %s -' % ( - self.cc, obj, language, ' '.join(flag_list))) - returncode, _, stderr = run_command(cmd, shell=True) - - try: - # In case of error, the `.o` file will be deleted by the compiler - os.remove(obj) - except OSError: - pass + suffix = language + if suffix == 'c++': + suffix = 'cpp' + fd, src = tempfile.mkstemp('.' + suffix, 'filter_cc_flags_test') + os.write(fd, b"int main() { return 0; }\n") os.close(fd) - if returncode == 0: - return flag_list for flag in flag_list: # Example error messages: # clang: warning: unknown warning option '-Wzzz' [-Wunknown-warning-option] # gcc: gcc: error: unrecognized command line option '-Wxxx' - if " option '%s'" % flag in stderr: + cmd = ('"%s" /nologo /FoNUL /c /WX %s "%s"' % (self.cc, flag, src)) + returncode, stdout, stderr = run_command(cmd, shell=True) + message = stdout + stderr + if "'%s'" % flag in message: unrecognized_flags.append(flag) else: valid_flags.append(flag) + try: + # In case of error, the `.o` file will be deleted by the compiler + os.remove(src) + except OSError: + pass if unrecognized_flags: console.warning('config: Unrecognized %s flags: %s' % ( language, ', '.join(unrecognized_flags))) return valid_flags + + +def default(): + return CcToolChainMsvc() diff --git a/src/blade/util.py b/src/blade/util.py index 05ef1c7e..edebe08f 100644 --- a/src/blade/util.py +++ b/src/blade/util.py @@ -32,6 +32,11 @@ except ImportError: fcntl = None +try: + import msvcrt +except ImportError: + msvcrt = None + _IN_PY3 = sys.version_info[0] == 3 @@ -77,13 +82,17 @@ def md5sum(obj): def lock_file(filename): """lock file.""" try: - fd = os.open(filename, os.O_CREAT | os.O_RDWR | os.O_EXCL) + fd = os.open(filename, os.O_CREAT | os.O_RDWR) if fcntl: old_fd_flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, old_fd_flags | fcntl.FD_CLOEXEC) fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + elif msvcrt: + msvcrt.locking(fd, msvcrt.LK_NBLCK, os.stat(fd).st_size) return fd, 0 except IOError as ex_value: + if msvcrt: # msvcrt did't set errno correctly + return -1, errno.EAGAIN return -1, ex_value.errno @@ -92,6 +101,8 @@ def unlock_file(fd): try: if fcntl: fcntl.flock(fd, fcntl.LOCK_UN) + elif msvcrt: + msvcrt.locking(fd, msvcrt.LK_UNLCK, os.stat(fd).st_size) os.close(fd) except IOError: pass