diff --git a/install_for_msbuild/Readme.md b/install_for_msbuild/Readme.md new file mode 100644 index 00000000..fd104680 --- /dev/null +++ b/install_for_msbuild/Readme.md @@ -0,0 +1,137 @@ +# install_clcache_msbuild + +Since the integration of `clcache` with `msbuild` is rather cumbersome, +this script provide an helper in order to simplify the process. + +It is compatible with MSVC 2010, 2015 and 2017 (tested with MSVC 2015 and 2017). + +## What this script does: + +* Check that python3 and pip3 are installed and are in the PATH +* Check that the pip installed scripts are in the PATH (PYTHONHOME\Scripts) +* Call `pip install .` from the repo and check that clcache is then in the PATH. + `clcache` will subsequently be used from the PYTHONHOME\\Scripts directory. +* Modify the user msbuild preference files inside `%AppData%\..\Local\Microsoft\MSBuild\v4.0` + so that clcache becomes the default compiler. (These prefs are shared between MSVC 2010 to 2017). +* Find all cl.exes version on your computer (for MSVC 2010 to MSVC 2017), and allows you + to select the correct one, by showing a detailed list of their version and target architecture. +* Set the env variable `CLCACHE_CL` with the correct path to cl.exe + +As additional options, this script can also +* change the cache location +* change the cache size +* change the timeout CLCACHE_OBJECT_CACHE_TIMEOUT_MS + +## Caveat +Since the msbuild preference files inside `%AppData%\..\Local\Microsoft\MSBuild\v4.0` are shared +between different MSVC installations, clcache will be activated for all instances of MSVC. + +## Note +`vswhere.exe` is a tool provided by Microsoft in order to locate installations of MSVC >= 2017. + +## Usage + +```` +usage: install_clcache_msbuild.py [-h] [--cachedir CACHEDIR] + [--cache_size CACHE_SIZE] + [--clcache_timeout CLCACHE_TIMEOUT] + {status,install,enable,disable,enable_logs,disable_logs,show_cl_list,select_cl} +```` + +Sample usage session: + +````bash +> python install_clcache_msbuild.py install +Looking for python in PATH +C:\Python36-32\python.exe +Looking for pip in PATH +C:\Python36-32\Scripts\pip.exe + +###################################################################### +Installing clcache (installClcache) +###################################################################### +====> pip install .(in folder F:\dvp\OpenSource\clcache) +Processing f:\dvp\opensource\clcache +Requirement already satisfied: pymemcache in c:\python36-32\lib\site-packages (from clcache==4.1.1.dev65+g105e486.d20181119) (2.0.0) +Requirement already satisfied: pyuv in c:\python36-32\lib\site-packages (from clcache==4.1.1.dev65+g105e486.d20181119) (1.4.0) +Requirement already satisfied: six in c:\python36-32\lib\site-packages (from pymemcache->clcache==4.1.1.dev65+g105e486.d20181119) (1.11.0) +Installing collected packages: clcache + Found existing installation: clcache 4.1.1.dev65+g105e486.d20181119 + Uninstalling clcache-4.1.1.dev65+g105e486.d20181119: + Successfully uninstalled clcache-4.1.1.dev65+g105e486.d20181119 + Running setup.py install for clcache ... done +Successfully installed clcache-4.1.1.dev65+g105e486.d20181119 +You are using pip version 10.0.1, however version 18.1 is available. +You should consider upgrading via the 'python -m pip install --upgrade pip' command. +Looking for clcache in PATH +C:\Python36-32\Scripts\clcache.exe + +###################################################################### +Force clcache via Msbbuild user settings (copyMsvcPrefClcache) +###################################################################### +Wrote pref in C:\Users\pascal\AppData\Local\Microsoft\MSBuild\v4.0\Microsoft.Cpp.ARM.user.props +Wrote pref in C:\Users\pascal\AppData\Local\Microsoft\MSBuild\v4.0\Microsoft.Cpp.Win32.user.props +Wrote pref in C:\Users\pascal\AppData\Local\Microsoft\MSBuild\v4.0\Microsoft.Cpp.x64.user.props + +###################################################################### +Select cl compiler: (selectCl) +###################################################################### + # version targetArch hostArch folder (shortened) + 1 14.0 amd64 amd64 C:\ProgX86\MSVC 14.0\vc\bin\amd64 + 2 14.0 arm amd64 C:\ProgX86\MSVC 14.0\vc\bin\amd64_arm + 3 14.0 x86 amd64 C:\ProgX86\MSVC 14.0\vc\bin\amd64_x86 + 4 14.0 amd64 x86 C:\ProgX86\MSVC 14.0\vc\bin\x86_amd64 + 5 14.0 arm x86 C:\ProgX86\MSVC 14.0\vc\bin\x86_arm + 6 14.0 x86 x86 C:\ProgX86\MSVC 14.0\vc\bin + 7 15.7.27703.2047 x64 x64 C:\ProgX86\MSVC\2017\Enterprise\VC\Tools\MSVC\14.14.26428\bin\Hostx64\x64 + 8 15.7.27703.2047 x86 x64 C:\ProgX86\MSVC\2017\Enterprise\VC\Tools\MSVC\14.14.26428\bin\Hostx64\x86 + 9 15.7.27703.2047 x64 x86 C:\ProgX86\MSVC\2017\Enterprise\VC\Tools\MSVC\14.14.26428\bin\Hostx86\x64 + 10 15.7.27703.2047 x86 x86 C:\ProgX86\MSVC\2017\Enterprise\VC\Tools\MSVC\14.14.26428\bin\Hostx86\x86 +Enter the number corresponding to the desired compiler: 6 +Selected : C:\Program Files (x86)\Microsoft Visual Studio 14.0\vc\bin\cl.exe +====> SETX CLCACHE_CL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\vc\bin\cl.exe" + +SUCCESS: Specified value was saved. + +###################################################################### +Note about clcache usage: (showClCacheUsage) +###################################################################### +====> clcache --help +clcache.py v4.1.0-dev + --help : show this help + -s : print cache statistics + -c : clean cache + -C : clear cache + -z : reset cache statistics + -M : set maximum cache size (in bytes) +```` + +# Caveats with msbuild and clcache : + +## incremental builds with clcache and msbuild + +clcache has serious isses with incremental builds. After a full build, you will always get a full rebuild even if you modify only one file ! + +There is a pull request that succesfully correct this here: https://github.com/frerich/clcache/pull/319/commits + + + +## clcache is not compatible with `/Zi` debug information format : use `/Z7` instead. + +See +https://github.com/frerich/clcache/issues/30 +and https://stackoverflow.com/questions/284778/what-are-the-implications-of-using-zi-vs-z7-for-visual-studio-c-projects + +With cmake, you can do the following: + + + ```cmake + message("msvc_clccache_force_z7_debug_format use /Z7 debug format") + if(MSVC) + string(REGEX REPLACE "/Z[iI7]" "" + CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Z7") + endif() + ```` + diff --git a/install_for_msbuild/env_utils.py b/install_for_msbuild/env_utils.py new file mode 100644 index 00000000..7a10f9f3 --- /dev/null +++ b/install_for_msbuild/env_utils.py @@ -0,0 +1,196 @@ +import ctypes +import winreg +import os +import os.path +import sys +import subprocess +import platform +# import winshell +from win32com.client import Dispatch + +def createShortcut(src_file, dst_shortcut_path): + path = os.path.join(dst_shortcut_path) + wDir = os.path.dirname(src_file) + icon = src_file + shell = Dispatch('WScript.Shell') + shortcut = shell.CreateShortCut(path) + shortcut.Targetpath = src_file + shortcut.WorkingDirectory = wDir + shortcut.IconLocation = icon + shortcut.save() + print("Created shortcut : {} points to {}".format(dst_shortcut_path, src_file)) + + +def removeFile(filename): + if os.path.exists(filename): + os.remove(filename) + print("Removed file " + filename) + else: + print("Cannot remove " + filename + " (does not exist)") + + +def runProcessDetached(command): + pid = subprocess.Popen(command).pid + print("Launched detached process with pid={} for command {}".format(pid, command)) + return pid + + +def shortDirectoryName(folder): + result = folder + result = result.replace("Program Files (x86)", "ProgX86") + result = result.replace("Program Files", "Prog") + result = result.replace("Microsoft Visual Studio", "MSVC") + return result + + +def isOs64bit(): + return platform.machine().endswith('64') + +def dirNameAbsolute(folder: str) -> str: + return os.path.abspath(os.path.realpath(folder)) + + +def fileDirNameAbsolute(file: str) -> str: + return dirNameAbsolute(os.path.dirname(file)) + + +def appDataPathLocal() -> str: + result = dirNameAbsolute(os.getenv('APPDATA') + "\\..\\Local") + return result + +def isAdmin(): + return ctypes.windll.shell32.IsUserAnAdmin() + + +def currentFuncName(n=0): + return sys._getframe(n + 1).f_code.co_name #pylint: disable=W0212 + + +def showFunctionIntro(details=""): + print() + print("######################################################################") + print(details + " (" + currentFuncName(1) + ")") + print("######################################################################") + + +def hasProgramInPath(prog): + print("Looking for " + prog + " in PATH") + result = subprocess.call("where " + prog) + if result != 0: + print(prog + " not found in PATH") + return result == 0 + + +def whereProgram(prog: str) -> str: + allProgrs = subprocess.check_output("where " + prog).decode("utf-8") + firstProg = allProgrs.split("\r")[0] + return firstProg + + +def whereProgramFolder(prog: str) -> str: + progLocation = whereProgram(prog) + return os.path.dirname(progLocation) + + +def pipScriptsDir(): + return whereProgramFolder("python") + "\\Scripts" + + +def showCmd(cmd): + print("====> " + cmd) + + +def callAndShowCmd(command: str, cwd: str = None) -> bool: + if cwd is not None: + print("====> " + command + "(in folder " + cwd + ")") + else: + print("====> " + command) + if cwd is not None: + return subprocess.call(command, cwd=cwd) == 0 + else: + return subprocess.call(command) == 0 + + +def callCmdGetOutput(command: str, cwd: str = None) -> str: + return subprocess.check_output(command, cwd=cwd).decode("utf-8") + + +def implSetAndStoreEnvVariable(name, value, allUsers=False): + if allUsers: + cmd = "SETX {0} \"{1}\" /M".format(name, value) + else: + cmd = "SETX {0} \"{1}\"".format(name, value) + if not callAndShowCmd(cmd): + return False + os.environ[name] = value + return True + + +def setAndStoreEnvVariable(name, value): + return implSetAndStoreEnvVariable(name, value, allUsers=isAdmin()) + + +def implRemoveEnvVariable(name, allUsers=False): + if allUsers: + command = "REG DELETE HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment /F /V " + name + else: + command = "REG DELETE HKCU\\Environment /F /V " + name + if not callAndShowCmd(command): + return False + if name is os.environ: + os.environ.pop(name, None) + return True + + +def removeEnvVariable(name): + return implRemoveEnvVariable(name, allUsers=isAdmin()) + + +def implReadRegistryValue(registryKind: int, keyName: str, valueName: str) -> str: + reg = winreg.ConnectRegistry(None, registryKind) + try: + key = winreg.OpenKey(reg, keyName) + result = winreg.QueryValueEx(key, valueName) + return result[0] + except FileNotFoundError: + return None + + +def readRegistryValueLocalMachine(keyName: str, valueName: str) -> str: + return implReadRegistryValue(winreg.HKEY_LOCAL_MACHINE, keyName, valueName) + + +def implReadEnvVariableFromRegistry(valueName, allUsers=False) -> str: + if allUsers: + keyName = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" + return implReadRegistryValue(winreg.HKEY_LOCAL_MACHINE, keyName, valueName) + else: + return implReadRegistryValue(winreg.HKEY_CURRENT_USER, "Environment", valueName) + + +def readEnvVariableFromRegistry(name): + return implReadEnvVariableFromRegistry(name, allUsers=isAdmin()) + + +def readPathFromEnvironment() -> str: + return os.environ["PATH"] + + +def readPathFromRegistry() -> str: + result = implReadEnvVariableFromRegistry("PATH", allUsers=False) + result = result + ";" + implReadEnvVariableFromRegistry("PATH", allUsers=True) + return result + + +def listFiles(folder, appendFolder=True): + files = [f for f in os.listdir(folder) if os.path.isfile(os.path.join(folder, f))] + if appendFolder: + files = [os.path.join(folder, f) for f in files] + return files + + +def listSubdirs(folder, appendFolder=True): + files = [f for f in os.listdir(folder) if os.path.isdir(os.path.join(folder, f))] + if appendFolder: + files = [os.path.join(folder, f) for f in files] + return files diff --git a/install_for_msbuild/env_utils_tests.py b/install_for_msbuild/env_utils_tests.py new file mode 100644 index 00000000..4ad56e8b --- /dev/null +++ b/install_for_msbuild/env_utils_tests.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +import os.path +import time +import unittest +import winshell +import env_utils +import locate_cl_exe + +class EnvUtilsTests(unittest.TestCase): + def testEnvVars(self): + env_utils.setAndStoreEnvVariable("DUMMYVAR", "dummy") + result = env_utils.readEnvVariableFromRegistry("DUMMYVAR") + self.assertEqual(result, "dummy") + env_utils.removeEnvVariable("DUMMYVAR") + result = env_utils.readEnvVariableFromRegistry("DUMMYVAR") + self.assertTrue(result is None) + def testShortcut(self): + pythonProg = env_utils.whereProgram("python") + dstFolder = winshell.startup() + dst = dstFolder + "\\python.lnk" + env_utils.createShortcut(pythonProg, dst) + self.assertTrue(os.path.exists(dst)) + os.remove(dst) + self.assertTrue(not os.path.exists(dst)) + # def testRunProcessDetached(self): + # prog = env_utils.whereProgram("Calc.exe") + # pid = env_utils.runProcessDetached(prog) + # time.sleep(1) + # env_utils.callAndShowCmd("taskkill /PID {}".format(pid)) + + + +class LocateClTests(unittest.TestCase): + def testFindfindMsvcUpTo2015(self): + result = locate_cl_exe.implFindMsvcUpTo2015() + self.assertTrue(len(result) > 0) + def testFindMsvc2017(self): + aux = locate_cl_exe.implFindMsvc2017() + self.assertTrue(len(aux) >= 0) + def testFindMsvc(self): + aux = locate_cl_exe.findMsvc() + self.assertTrue(len(aux) > 0) + def testFindCl(self): + aux = locate_cl_exe.findClExesList() + self.assertTrue(len(aux) > 0) + + +if __name__ == '__main__': + unittest.TestCase.longMessage = True + unittest.main() diff --git a/install_for_msbuild/install_clcache_msbuild.py b/install_for_msbuild/install_clcache_msbuild.py new file mode 100644 index 00000000..7a836088 --- /dev/null +++ b/install_for_msbuild/install_clcache_msbuild.py @@ -0,0 +1,321 @@ + +import sys +import argparse +import winshell +import env_utils +import locate_cl_exe + + +THIS_DIR = env_utils.fileDirNameAbsolute(__file__) +CLCACHE_REPO_DIR = env_utils.dirNameAbsolute(THIS_DIR + "\\..") +MSBUILD_USER_SETTINGS_DIR = env_utils.appDataPathLocal() + "\\Microsoft\\MSBuild\\v4.0" +MSBUILD_SETTING_FILE_CONTENT_CLCACHE = """ + + + + + + + clcache.exe + PIP_SCRIPTS_DIR + + + + +""" +MSBUILD_SETTING_FILE_CONTENT_NO_CLCACHE = """ + + + + + + + + +""" + + +def installClcache(): + env_utils.showFunctionIntro("Installing clcache") + status = env_utils.callAndShowCmd("pip install .", cwd=CLCACHE_REPO_DIR) + if not status: + return False + if not env_utils.hasProgramInPath("clcache"): + print("Humm. its seems that the install failed") + return False + return True + + +def implCopyMsvcPref(prefContent): + files = env_utils.listFiles(MSBUILD_USER_SETTINGS_DIR) + for file in files: + with open(file, 'w') as f: + f.write(prefContent) + print("Wrote pref in " + file) + return True + + +def copyMsvcPrefClcache(): + env_utils.showFunctionIntro("Force clcache via Msbbuild user settings") + prefContent = MSBUILD_SETTING_FILE_CONTENT_CLCACHE.replace( + "PIP_SCRIPTS_DIR", env_utils.pipScriptsDir()) + return implCopyMsvcPref(prefContent) + + +def copyMsvcPrefOriginal(): + env_utils.showFunctionIntro("Disable clcache via Msbbuild user settings") + return implCopyMsvcPref(MSBUILD_SETTING_FILE_CONTENT_NO_CLCACHE) + + +def showClCacheUsage(): + env_utils.showFunctionIntro("Note about clcache usage:") + env_utils.callAndShowCmd("clcache --help") + return True + + +def showStatus(): + if env_utils.hasProgramInPath("clcache"): + print("clcache is in your PATH") + else: + print("clcache is not installed") + + if env_utils.readEnvVariableFromRegistry("CLCACHE_LOG") is not None: + print("logs are enabled") + else: + print("logs are disabled") + + if env_utils.readEnvVariableFromRegistry("CLCACHE_SERVER") is not None: + print("clcache-server is enabled") + else: + print("clcache-server is disabled") + + prefFile = MSBUILD_USER_SETTINGS_DIR + "\\Microsoft.Cpp.Win32.user.props" + isEnabled = False + with open(prefFile, "r") as f: + lines = f.readlines() + for line in lines: + if "clcache.exe" in line: + isEnabled = True + if isEnabled: + print("clcache is *ENABLED* in " + MSBUILD_USER_SETTINGS_DIR) + else: + print("clcache is *NOT ENABLED* in " + MSBUILD_USER_SETTINGS_DIR) + + cl = env_utils.readEnvVariableFromRegistry("CLCACHE_CL") + print("CLCACHE_CL (real compiler) is :" + cl) + print("call clcache -s for statistics") + return True + + +def makeInitialChecks(): + if not env_utils.hasProgramInPath("python"): + print("This program needs python 3") + return False + if not env_utils.hasProgramInPath("pip"): + print("This program needs pip 3") + return False + + if not "python 3" in env_utils.callCmdGetOutput("python --version").lower(): + print("Bad python version : this program needs python 3") + return False + + if not "python 3" in env_utils.callCmdGetOutput("pip --version").lower(): + print("Bad pip version : this program needs pip for python 3") + return False + + pipScriptsDir = env_utils.pipScriptsDir() + if pipScriptsDir.lower() not in env_utils.readPathFromRegistry().lower(): + print("Can't find pip_scripts_dir in your PATH. pip_scripts_dir=" + pipScriptsDir) + print("Please add this to your PATH") + return False + return True + + +def selectCl(): + env_utils.showFunctionIntro("Select cl compiler:") + clExesList = locate_cl_exe.findClExesList() + locate_cl_exe.printClList(clExesList) + while True: + answer = input("Enter the number corresponding to the desired compiler: ") + try: + nb = int(answer) + except ValueError: + print("Enter a number between 1 and " + str(len(clExesList))) + continue + if nb >= 1 and nb <= len(clExesList): + clExe = clExesList[nb - 1].installDir + "\\cl.exe" + print("Selected : " + clExe) + env_utils.setAndStoreEnvVariable("CLCACHE_CL", clExe) + return True + else: + print("Enter a number between 1 and " + str(len(clExesList))) + return False + + +def fullClcacheSetup(): + if not installClcache(): + return False + if not selectCl(): + return False + if not copyMsvcPrefClcache(): + return False + if not showClCacheUsage(): + return False + return True + + +def enableServer(): + """ + will enable the clache-server, start it, + and make sure that it starts when your computer starts. + """ + env_utils.setAndStoreEnvVariable("CLCACHE_SERVER", "1") + + env_utils.runProcessDetached(env_utils.pipScriptsDir() + "\\clcache-server.exe") + + dstFolder = winshell.startup() + dst = dstFolder + "\\clache-server.lnk" + src = env_utils.pipScriptsDir() + "\\clcache-server.exe" + env_utils.createShortcut(src, dst) + return True + + +def disableServer(): + """ + will disable the clcache-server and remove it from the startup programs. + Note : this does not kil the clcache-server.exe process, + however subsequent builds will not use it. + """ + if env_utils.readEnvVariableFromRegistry("CLCACHE_SERVER") is not None: + env_utils.removeEnvVariable("CLCACHE_SERVER") + + dstFolder = winshell.startup() + dst = dstFolder + "\\clache-server.lnk" + env_utils.removeFile(dst) + return True + + +def main(): + epilog = r"""Actions summary: + status : Show the install status and tells if clcache is enabled + install: : Install and enable clcache for msbuild integration + (will let you choose between the available cl.exe) + enable : : Enable clcache for msbuild: + Modifies the user msbuild preference files + inside APPDATAPATHLOCAL\Microsoft\MSBuild\v4.0 + disable: : Disable clcache + Modifies the user msbuild preference files + inside APPDATAPATHLOCAL\Microsoft\MSBuild\v4.0 + enable_server : will enable the clache-server, start it, + and make sure that it starts when your computer starts. + disable_server : will disable the clcache-server and remove it from the startup programs. + Note : this does not kil the clcache-server.exe process, + however subsequent builds will not use it. + enable_logs : Activate clcache logs during builds + disable_logs : Disable clcache logs during builds + show_cl_list : List available cl.exe compilers + select_cl : Choose which cl.exe to activate + +What this script does: +********************** + +* Check that python3 and pip3 are installed and are in the PATH +* Check that the pip installed scripts are in the PATH (PYTHONHOME\Scripts) +* Call `pip install .` from the repo and check that clcache is then in the PATH. + `clcache` will subsequently be used from the PYTHONHOME\\Scripts directory. +* Modify the user msbuild preference files inside `APPDATAPATHLOCAL\Microsoft\MSBuild\v4.0` + so that clcache becomes the default compiler. (These prefs are shared between MSVC 2010 to 2017). +* Find all cl.exes version on your computer (for MSVC 2010 to MSVC 2017), and allows you + to select the correct one, by showing a detailed list of their version and target architecture. +* Set the env variable `CLCACHE_CL` with the correct path to cl.exe + +As additional options, this script can also +* change the cache location +* change the cache size +* change the timeout CLCACHE_OBJECT_CACHE_TIMEOUT_MS + +Caveat +****** +since the msbuild preference files inside `APPDATAPATHLOCAL\Microsoft\MSBuild\v4.0` are shared +between different MSVC installations, clcache will be activated for all instances of MSVC. + + """ + epilog = epilog.replace("MSBUILD_USER_SETTINGS_DIR", MSBUILD_USER_SETTINGS_DIR) + epilog = epilog.replace("APPDATAPATHLOCAL", env_utils.appDataPathLocal()) + helpTimeout = """clcache object cache timeout in seconds + (increase if you have failures during your build) + """ + parser = argparse.ArgumentParser( + description="Configure clcache for use with msbuild", + epilog=epilog, + formatter_class=argparse.RawDescriptionHelpFormatter + ) + choices = ["status", "install", + "enable", "disable", + "enable_server", "disable_server", + "enable_logs", "disable_logs", + "show_cl_list", "select_cl"] + parser.add_argument("action", choices=choices, help="action") + parser.add_argument("--cachedir", help="clcache directory") + parser.add_argument("--cache_size", help="clcache size in Go", type=int, default=0) + parser.add_argument("--clcache_timeout", help=helpTimeout, type=int, default=0) + + if sys.argv[0][-3:] == ".py": + argv = sys.argv[1:] + else: + argv = sys.argv + args = parser.parse_args(argv) + + if args.action == "status": + if not showStatus(): + return False + elif args.action == "install": + if not makeInitialChecks(): + return False + if not fullClcacheSetup(): + return False + elif args.action == "enable": + if not copyMsvcPrefClcache(): + return False + elif args.action == "disable": + if not copyMsvcPrefOriginal(): + return False + elif args.action == "enable_server": + if not enableServer(): + return False + elif args.action == "disable_server": + if not disableServer(): + return False + elif args.action == "enable_logs": + if not env_utils.setAndStoreEnvVariable("CLCACHE_LOG", "1"): + return False + elif args.action == "disable_logs": + if not env_utils.removeEnvVariable("CLCACHE_LOG"): + return False + elif args.action == "show_cl_list": + locate_cl_exe.printClList(locate_cl_exe.findClExesList()) + return True + elif args.action == "select_cl": + selectCl() + return True + + if args.cachedir is not None: + env_utils.setAndStoreEnvVariable("CLCACHE_DIR", args.cachedir) + + if args.cache_size > 0: + giga = 1024 * 1024 * 1024 + byteSize = giga * args.cache_size + if not env_utils.callAndShowCmd("clcache -M " +str(byteSize)): + return False + + if args.clcache_timeout > 0: + timeMs = args.clcache_timeout * 1000 + env_utils.setAndStoreEnvVariable("CLCACHE_OBJECT_CACHE_TIMEOUT_MS", str(timeMs)) + + return True + + +if __name__ == "__main__": + if not main(): + print("FAILURE") + sys.exit(1) diff --git a/install_for_msbuild/locate_cl_exe.py b/install_for_msbuild/locate_cl_exe.py new file mode 100644 index 00000000..b01c1eef --- /dev/null +++ b/install_for_msbuild/locate_cl_exe.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + +import json +import typing +import os.path +import os +import env_utils + +THIS_DIR = env_utils.fileDirNameAbsolute(__file__) + +class MsvcInstall(object): + version: str + installDir: str + def __init__(self, version, installDir): + self.version = version + self.installDir = installDir + + +class ClInfo(object): + msvcInstall: MsvcInstall + installDir: str + hostArch: str + targetArch: str + def __init__(self, msvcInstall, installDir, hostArch, targetArch): + self.msvcInstall = msvcInstall + self.installDir = installDir + self.hostArch = hostArch + self.targetArch = targetArch + def __str__(self): + result = "version: " + self.msvcInstall.version + " hostArch:" \ + + self.hostArch + " targetArch:" + self.targetArch + " (" + self.installDir + ")" + return result + + +def implFindMsvcUpTo2015() -> typing.List[MsvcInstall]: + versions = ["8.0", "9.0", "10.0", "11.0", "12.0", "13.0", "14.0", "15.0"] + if env_utils.isOs64bit(): + softwareSubkey = "SOFTWARE\\Wow6432Node" + else: + softwareSubkey = "SOFTWARE" + keyTemplate = softwareSubkey + "\\Microsoft\\VisualStudio\\__VERSION__\\" + + result = [] + for version in versions: + valueName = "InstallDir" + key = keyTemplate.replace("__VERSION__", version) + installDir = env_utils.readRegistryValueLocalMachine(key, valueName) + if installDir is not None and os.path.isdir(installDir): + result.append(MsvcInstall(version, installDir)) + return result + + +def implFindMsvc2017() -> typing.List[MsvcInstall]: + jsonStr = env_utils.callCmdGetOutput(THIS_DIR + "\\vswhere.exe -format json") + jsonData = json.loads(jsonStr) + result = [] + for entry in jsonData: + result.append( + MsvcInstall(entry["installationVersion"], entry["installationPath"])) + return result + + +def findMsvc() -> typing.List[MsvcInstall]: + return implFindMsvcUpTo2015() + implFindMsvc2017() + + +def implfindClExesForOldMsvc(msvcInstall: MsvcInstall) -> typing.List[ClInfo]: + result = [] + mainClPath = env_utils.dirNameAbsolute(msvcInstall.installDir + "\\..\\..\\vc\\bin") + if not os.path.isdir(mainClPath): + return [] + subDirs = env_utils.listSubdirs(mainClPath, appendFolder=False) + ["."] + def hasClExe(subdir): + return os.path.isfile(mainClPath + "\\" + subdir + "\\cl.exe") + dirsWithCl = [dir for dir in subDirs if hasClExe(dir)] + # dirWithCl is something like + # ['amd64', 'amd64_arm', 'amd64_x86', 'x86_amd64', 'x86_arm', '.'] + # in this ist : "." = x86_x86 and amd64 = amd64_amd64 + for dirWithCl in dirsWithCl: + fullDir = env_utils.dirNameAbsolute(mainClPath + "\\" + dirWithCl) + if dirWithCl == ".": + hostArch = "x86" + targetArch = "x86" + elif dirWithCl == "amd64": + hostArch = "amd64" + targetArch = "amd64" + else: + tokens = dirWithCl.split("_") + hostArch = tokens[0] + targetArch = tokens[1] + result.append(ClInfo(msvcInstall, fullDir, hostArch, targetArch)) + return result + + +def implfindClExesForMsvc2017(msvcInstall: MsvcInstall) -> typing.List[ClInfo]: + result = [] + topDir = msvcInstall.installDir + "\\VC\\Tools\\MSVC" + for dirpath, _, filenames in os.walk(topDir): + for file in filenames: + if file == "cl.exe": + # dirpath looks like + # c:\Program Files (x86)\...\bin\Hostx64\x64 + dirTokens = dirpath.split("\\") + hostArch = dirTokens[-2].replace("Host", "") + targetArch = dirTokens[-1] + result.append(ClInfo(msvcInstall, dirpath, hostArch, targetArch)) + return result + + +def findClExesList() -> typing.List[ClInfo]: + result = [] + msvcInstallOld = implFindMsvcUpTo2015() + for msvcInstall in msvcInstallOld: + result = result + implfindClExesForOldMsvc(msvcInstall) + + msvcInstallNew = implFindMsvc2017() + for msvcInstall in msvcInstallNew: + result = result + implfindClExesForMsvc2017(msvcInstall) + + return result + + +def printClList(clInfoList: typing.List[ClInfo]) -> str: + def clInfoToData(clInfo: ClInfo): + return [clInfo.msvcInstall.version, clInfo.targetArch, + clInfo.hostArch, env_utils.shortDirectoryName(clInfo.installDir)] + headers = ["#", "version", "targetArch", "hostArch", "folder (shortened)"] + data = [clInfoToData(clInfo) for clInfo in clInfoList] + rowFormat = "{:>4}{:>18}{:>11}{:>11}{:>80}" + + print(rowFormat.format(*headers)) + rowId = 1 + for version in data: + print(rowFormat.format(rowId, *version)) + rowId = rowId + 1 diff --git a/install_for_msbuild/requirements.txt b/install_for_msbuild/requirements.txt new file mode 100644 index 00000000..66238fe7 --- /dev/null +++ b/install_for_msbuild/requirements.txt @@ -0,0 +1,2 @@ +winshell +pywin32 diff --git a/install_for_msbuild/vswhere.exe b/install_for_msbuild/vswhere.exe new file mode 100644 index 00000000..ecfb3bfb Binary files /dev/null and b/install_for_msbuild/vswhere.exe differ