From 48e43bc02081e55eacded7d8f612e24b3f94f70e Mon Sep 17 00:00:00 2001 From: Martin Bauer <martin.bauer@fau.de> Date: Wed, 1 Mar 2017 16:42:55 +0100 Subject: [PATCH] pystencils: enhanced windows support --- cpu/cpujit.py | 23 ++++++------ cpu/msvc_detection.py | 81 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 cpu/msvc_detection.py diff --git a/cpu/cpujit.py b/cpu/cpujit.py index 7027fd987..75fc5c193 100644 --- a/cpu/cpujit.py +++ b/cpu/cpujit.py @@ -124,8 +124,9 @@ def readConfig(): elif platform.system().lower() == 'windows': defaultCompilerConfig = OrderedDict([ ('os', 'windows'), + ('msvcVersion', 'latest'), ('arch', 'x64'), - ('flags', '/Ox /fp:fast /openmp'), + ('flags', '/Ox /fp:fast /openmp /arch:avx'), ('restrictQualifier', '__restrict') ]) defaultCacheConfig = OrderedDict([ @@ -143,28 +144,26 @@ def readConfig(): if configExists: loadedConfig = json.load(open(configPath, 'r')) config = _recursiveDictUpdate(config, loadedConfig) + else: + createFolder(configPath, True) + json.dump(config, open(configPath, 'w'), indent=4) config['cache']['sharedLibrary'] = os.path.expanduser(config['cache']['sharedLibrary']) config['cache']['objectCache'] = os.path.expanduser(config['cache']['objectCache']) - # create folders if they don't exist yet - createFolder(configPath, True) - if config['cache']['clearCacheOnStart']: shutil.rmtree(config['cache']['objectCache'], ignore_errors=True) createFolder(config['cache']['objectCache'], False) createFolder(config['cache']['sharedLibrary'], True) - json.dump(config, open(configPath, 'w'), indent=4) - if 'env' not in config['compiler']: config['compiler']['env'] = {} if config['compiler']['os'] == 'windows': - from setuptools.msvc import msvc14_get_vc_env - msvcEnv = msvc14_get_vc_env(config['compiler']['arch']) - config['compiler']['env'].update({k.upper(): v for k, v in msvcEnv.items()}) + from pystencils.cpu.msvc_detection import getEnvironment + msvcEnv = getEnvironment(config['compiler']['msvcVersion'], config['compiler']['arch']) + config['compiler']['env'].update(msvcEnv) return config @@ -325,10 +324,12 @@ def compileAndLoad(ast): else: if getCompilerConfig()['os'].lower() == 'windows': libFile = os.path.join(cacheConfig['objectCache'], codeHashStr + ".dll") - compileWindows(ast, codeHashStr, srcFile, libFile) + if not os.path.exists(libFile): + compileWindows(ast, codeHashStr, srcFile, libFile) else: libFile = os.path.join(cacheConfig['objectCache'], codeHashStr + ".so") - compileLinux(ast, codeHashStr, srcFile, libFile) + if not os.path.exists(libFile): + compileLinux(ast, codeHashStr, srcFile, libFile) return cdll.LoadLibrary(libFile)[ast.functionName] diff --git a/cpu/msvc_detection.py b/cpu/msvc_detection.py new file mode 100644 index 000000000..61cfe4aed --- /dev/null +++ b/cpu/msvc_detection.py @@ -0,0 +1,81 @@ +import subprocess +import os + + +def getEnvironment(versionSpecifier, arch='x64'): + """ + Returns an environment dictionary, for activating the Visual Studio compiler + :param versionSpecifier: either a version number, year number, 'auto' or 'latest' for automatic detection of latest + installed version or 'setuptools' for setuptools-based detection + :param arch: x86 or x64 + """ + if versionSpecifier == 'setuptools': + return getEnvironmentFromSetupTools(arch) + else: + if versionSpecifier in ('auto', 'latest'): + versionNr = findLatestMsvcVersionUsingEnvironmentVariables() + else: + versionNr = normalizeMsvcVersion(versionSpecifier) + vcVarsPath = getVcVarsPath(versionNr) + return getEnvironmentFromVcVarsFile(vcVarsPath, arch) + + +def findLatestMsvcVersionUsingEnvironmentVariables(): + import re + regex = re.compile('VS(\d\d)\dCOMNTOOLS') + versions = [] + for key, value in os.environ.items(): + match = regex.match(key) + if match: + versions.append(int(match.group(1))) + if len(versions) == 0: + raise ValueError("Visual Studio not found.") + versions.sort() + return versions[-1] + + +def normalizeMsvcVersion(version): + """ + Takes version specifiers in the following form: + - year: 2012, 2013, 2015, either as int or string + - version numbers with or without dot i.e. 11.0 or 11 + :return: integer version number + """ + if isinstance(version, str) and '.' in version: + version = version.split('.')[0] + + version = int(version) + mapping = { + 2015: 14, + 2013: 12, + 2012: 11 + } + if version in mapping: + return mapping[version] + else: + return version + + +def getEnvironmentFromVcVarsFile(vcVarsFile, arch): + out = subprocess.check_output( + 'cmd /u /c "{}" {} && set'.format(vcVarsFile, arch), + stderr=subprocess.STDOUT, + ).decode('utf-16le', errors='replace') + + env = {key.upper(): value for key, _, value in (line.partition('=') for line in out.splitlines()) if key and value} + return env + + +def getVcVarsPath(versionNr): + environmentVarName = 'VS%d0COMNTOOLS' % (versionNr,) + vcPath = os.environ[environmentVarName] + path = os.path.join(vcPath, '..', '..', 'VC', 'vcvarsall.bat') + return os.path.abspath(path) + + +def getEnvironmentFromSetupTools(arch): + from setuptools.msvc import msvc14_get_vc_env + msvcEnv = msvc14_get_vc_env(arch) + return {k.upper(): v for k, v in msvcEnv.items()} + + -- GitLab