diff --git a/bin/CraftDebug.py b/bin/CraftDebug.py index 6a8ec4e74..1c485b00f 100644 --- a/bin/CraftDebug.py +++ b/bin/CraftDebug.py @@ -1,185 +1,196 @@ import functools import inspect import logging import logging.handlers import os import re import shutil import sys from CraftCore import CraftCore -import CraftConfig + +try: + import coloredlogs + _SUPPORTS_COLORED_LOGS = True +except: + _SUPPORTS_COLORED_LOGS = False class CraftDebug(object): def __init__(self): self.seenDeprecatedFunctions = set() - self._handler = logging.StreamHandler(sys.stdout) self._log = logging.getLogger("craft") self._log.setLevel(logging.DEBUG) - self._log.addHandler(self._handler) - self._handler.setLevel(logging.INFO) logDir = CraftCore.settings.get("CraftDebug", "LogDir", os.path.expanduser("~/.craft/")) if not os.path.exists(logDir): os.makedirs(logDir) - cleanNameRe = re.compile(r":?\\+|/+|:|;") - logfileName = os.path.join(logDir, "log-%s.txt" % cleanNameRe.sub("_", CraftCore.settings._craftRoot())) - try: - fileHandler = logging.handlers.RotatingFileHandler(logfileName, mode="at+", maxBytes=10000000, + logfileName = os.path.join(logDir, "log-%s.txt" % re.compile(r":?\\+|/+|:|;").sub("_", CraftCore.settings._craftRoot())) + fileHandler = logging.handlers.RotatingFileHandler(logfileName, mode="at", maxBytes=10000000, backupCount=20) fileHandler.doRollover() fileHandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) self._log.addHandler(fileHandler) - fileHandler.setLevel(logging.DEBUG) except Exception as e: print(f"Failed to setup log file: {e}", file=sys.stderr) print(f"Right now we don't support running multiple Craft instances with the same configuration.", file=sys.stderr) + if _SUPPORTS_COLORED_LOGS and CraftCore.settings.getboolean("General", "AllowAnsiColor"): + coloredlogs.install(logger=self._log, fmt="%(message)s", stream=sys.stdout) + self._handler = self._log.handlers[-1] + else: + self._handler = logging.StreamHandler(sys.stdout) + self._log.addHandler(self._handler) + + self._handler.setLevel(logging.INFO) + fileHandler.setLevel(logging.DEBUG) + self.log.debug("#" * self.lineWidth) self.log.debug("New log started: %s" % " ".join(sys.argv)) self.log.debug("Log is saved to: %s" % fileHandler.baseFilename) self.logEnv() self.setVerbose(0) @property def lineWidth(self): width, _ = shutil.get_terminal_size((80, 20)) return width def verbose(self): """return the value of the verbose level""" return self._verbosity def setVerbose(self, _verbose): self._verbosity = _verbose lvl = logging.INFO if 0 <= _verbose < 2: lvl = logging.INFO elif _verbose >= 2: lvl = logging.DEBUG elif _verbose == 0: lvl = logging.WARNING elif _verbose <= -1: lvl = logging.CRITICAL self._handler.setLevel(lvl) def step(self, message): self.log.info("*** %s ***" % message) def new_line(self): self.log.info("\n") def debug_line(self): self.log.info("=" * self.lineWidth) @property def log(self): return self._log def print(self, msg, file=sys.stdout, stack_info=False): if 0 <= self.verbose() < 2: print(msg, file=file if not CraftCore.settings.getboolean("ContinuousIntegration", "Enabled", False) else sys.stdout) self.log.debug(msg, stack_info=stack_info) else: self.log.debug(msg, stack_info=stack_info) def printOut(self, msg, file=sys.stdout): """ Should only be used to report independent of the verbosity level for example to print the installed files etc """ if self.verbose() < 2: print(msg, file=file if not CraftCore.settings.getboolean("ContinuousIntegration", "Enabled", False) else sys.stdout) self.log.debug(msg) else: self.log.debug(msg) def logEnv(self, env=None): if CraftCore.settings.getboolean("CraftDebug", "LogEnvironment", True): if not env: env = os.environ self.log.debug( "Environment: \n" + "\n".join(f" {key}={value}" for key, value in env.items())) def trace(self, message): self.log.debug("craft trace: %s" % message) class TemporaryVerbosity(object): """Context handler for temporarily different verbosity""" def __init__(self, tempLevel): self.prevLevel = CraftCore.debug.verbose() CraftCore.debug.setVerbose(tempLevel) def __enter__(self): return self def __exit__(self, exc_type, exc_value, trback): CraftCore.debug.setVerbose(self.prevLevel) def deprecated(replacement=None): """ http://code.activestate.com/recipes/577819-deprecated-decorator/ Deprecated decorator. Author: Giampaolo Rodola' License: MIT A decorator which can be used to mark functions as deprecated. replacement is a callable that will be called with the same args as the decorated function. >>> @deprecated() ... def foo(x): ... return x ... >>> ret = foo(1) DeprecationWarning: foo is deprecated >>> ret 1 >>> >>> >>> def newfun(x): ... return 0 ... >>> @deprecated(newfun) ... def foo(x): ... return x ... >>> ret = foo(1) DeprecationWarning: foo is deprecated; use newfun instead >>> ret 0 >>> """ def outer(fun): msg = f"{fun.__name__} is deprecated" if replacement is not None: msg += f", use {replacement} instead" if fun.__doc__ is None: fun.__doc__ = msg @functools.wraps(fun) def inner(*args, **kwargs): _info = inspect.stack()[1] if not (_info.filename, _info.lineno) in CraftCore.debug.seenDeprecatedFunctions: CraftCore.debug.seenDeprecatedFunctions.add((_info.filename, _info.lineno)) if CraftCore.settings.getboolean("CraftDebug", "LogDeprecated", False): CraftCore.debug.print(msg, stack_info=True) else: CraftCore.log.debug(msg, stack_info=True) return fun(*args, **kwargs) return inner return outer if __name__ == "__main__": CraftCore.log.debug("debug: foo") CraftCore.log.info("info: foo") + CraftCore.log.warning("warning: foo") + CraftCore.log.critical("critical: foo") diff --git a/bin/CraftOS/win/osutils.py b/bin/CraftOS/win/osutils.py index 899dee345..bd99e18cb 100644 --- a/bin/CraftOS/win/osutils.py +++ b/bin/CraftOS/win/osutils.py @@ -1,94 +1,95 @@ import tempfile import ctypes import os import platform import subprocess import CraftOS.OsUtilsBase from CraftCore import CraftCore class FileAttributes(): # https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx FILE_ATTRIBUTE_READONLY = 0x1 FILE_ATTRIBUTE_REPARSE_POINT = 0x400 class OsUtils(CraftOS.OsUtilsBase.OsUtilsBase): @staticmethod def rm(path, force=False): CraftCore.log.debug("deleting file %s" % path) if force: OsUtils.removeReadOnlyAttribute(path) return ctypes.windll.kernel32.DeleteFileW(path) != 0 @staticmethod def rmDir(path, force=False): CraftCore.log.debug("deleting directory %s" % path) if force: OsUtils.removeReadOnlyAttribute(path) with os.scandir(path) as scan: for f in scan: if f.is_dir(): if not OsUtils.rmDir(f.path, force): return False else: if not OsUtils.rm(f.path, force): return False os. rmdir(path) return True @staticmethod def isLink(path): return os.path.islink(path) \ | OsUtils.getFileAttributes(path) & FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT # Detect a Junction @staticmethod def getFileAttributes(path): return ctypes.windll.kernel32.GetFileAttributesW(path) @staticmethod def removeReadOnlyAttribute(path): CraftCore.log.debug(f"Remove readonly flag of {path}") attributes = OsUtils.getFileAttributes(path) return ctypes.windll.kernel32.SetFileAttributesW(path, attributes & ~ FileAttributes.FILE_ATTRIBUTE_READONLY) != 0 @staticmethod def setConsoleTitle(title): return ctypes.windll.kernel32.SetConsoleTitleW(title) != 0 @staticmethod def supportsSymlinks(): with tempfile.TemporaryDirectory() as tmp: testFile = os.path.join(tmp, "CRAFT_LINK_TEST") return CraftCore.cache.getCommandOutput(f"cmd", f"/C mklink {testFile} {__file__}", testName="CRAFT_LINK_TEST")[0] == 0 @staticmethod def toNativePath(path : str) -> str: return OsUtils.toWindowsPath(path) @staticmethod def enableAnsiColors(): # tell Windows 10 that we do ansi ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-11), 7) + ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-12), 7) @staticmethod def killProcess(name : str="*", prefix : str=None) -> bool: if not prefix: prefix = CraftCore.standardDirs.craftRoot() powershell = None if platform.architecture()[0] == "32bit": # try to find the x64 powershell to be able to kill x64 processes too powershell = CraftCore.cache.findApplication("powershell", os.path.join(os.environ["WINDIR"], "sysnative", "WindowsPowerShell", "v1.0" )) if not powershell: powershell = CraftCore.cache.findApplication("powershell") if not powershell: CraftCore.log.warning("Failed to detect powershell") return False out = subprocess.run(f"{powershell} -NoProfile -ExecutionPolicy ByPass -Command \"& {{" + f"Get-Process '{name}' | Where-Object {{$_.Path -like '{prefix}*'}} |" f" %{{ Write-Output $_.Path; Stop-Process $_;}} }}\"", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) CraftCore.log.info(f"Killing processes {name} in {prefix}: {out.stdout}") return out.returncode == 0 diff --git a/bin/CraftSetupHelper.py b/bin/CraftSetupHelper.py index 6dd897195..5dc1548de 100644 --- a/bin/CraftSetupHelper.py +++ b/bin/CraftSetupHelper.py @@ -1,414 +1,416 @@ # -*- coding: utf-8 -*- # Copyright Hannah von Reth # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. import argparse import collections import os import platform import shutil import subprocess import sys from CraftCore import CraftCore from CraftOS.osutils import OsUtils from Utils.CaseInsensitiveDict import CaseInsensitiveDict # The minimum python version for craft please edit here # if you add code that changes this requirement MIN_PY_VERSION = (3, 6, 0) def log(msg, critical=False): if critical or not CraftCore.settings.getboolean("ContinuousIntegration", "Enabled", False): CraftCore.debug.print(msg, sys.stderr) else: CraftCore.log.debug(msg) if critical: exit(1) if sys.version_info[0:3] < MIN_PY_VERSION: log("Error: Python too old!\n" "Craft needs at least Python Version %s.%s.%s\n" "Please install it and adapt your CraftSettings.ini" % MIN_PY_VERSION, critical=True) if not platform.machine().endswith("64"): log(f"Craft requires a 64bit operating system. Your are using: {platform.machine()}", critical=True) class SetupHelper(object): CraftVersion = "master" NeedsSetup = "KDEROOT" not in os.environ def __init__(self, args=None): self.args = args if CraftCore.settings.getboolean("General", "AllowAnsiColor", False): OsUtils.enableAnsiColors() if SetupHelper.NeedsSetup: SetupHelper.NeedsSetup = False self.checkForEvilApplication() self.setupEnvironment() @staticmethod def _getOutput(command, shell=False): CraftCore.log.debug(f"SetupHelper._getOutput: {command}") p = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=shell, universal_newlines=True, errors="backslashreplace") out = p.stdout.strip() CraftCore.log.debug(f"SetupHelper._getOutput: return {p.returncode} {out}") return p.returncode, out def run(self): parser = argparse.ArgumentParser() parser.add_argument("--get", action="store_true") parser.add_argument("--print-banner", action="store_true") parser.add_argument("--getenv", action="store_true") parser.add_argument("--setup", action="store_true") parser.add_argument("rest", nargs=argparse.REMAINDER) args = parser.parse_args() if args.get: default = "" if len(args.rest) == 3: default = args.rest[2] CraftCore.log.info(CraftCore.settings.get(args.rest[0], args.rest[1], default)) elif args.print_banner: self.printBanner() elif args.getenv: self.printEnv() elif args.setup: self.printEnv() self.printBanner() def checkForEvilApplication(self): blackList = [] if OsUtils.isWin(): blackList += ["sh", "gcc", "g++", "cpp"] for app in blackList: location = shutil.which(app) if location: location = os.path.dirname(location) if not CraftCore.settings.getboolean("ContinuousIntegration", "Enabled", False): log( f"Found \"{app}\" in your PATH: \"{location}\"\n" f"This application is known to cause problems with your configuration of Craft.\n" f"Please remove it from PATH or manually set a value for PATH in your CraftSettings.ini:\n" f"\n" f"[Environment]\n" f"PATH=" f"\n") else: path = collections.OrderedDict.fromkeys(os.environ["Path"].split(os.path.pathsep)) del path[location] self.addEnvVar("Path", os.path.pathsep.join(path)) @staticmethod def printBanner(): def printRow(name, value): log(f"{name:20}: {value}") printRow("Craft", CraftCore.standardDirs.craftRoot()) printRow("Version", SetupHelper.CraftVersion) printRow("ABI", CraftCore.compiler) printRow("Download directory", CraftCore.standardDirs.downloadDir()) def addEnvVar(self, key, val): os.environ[key] = val def prependEnvVar(self, key : str, var : str, sep : str=os.path.pathsep) -> None: if not type(var) == list: var = [var] if key in os.environ: env = var + os.environ[key].split(sep) var = list(collections.OrderedDict.fromkeys(env)) val = sep.join(var) CraftCore.log.debug(f"Setting {key}={val}") os.environ[key] = val @staticmethod def stringToEnv(string : str): for line in string.strip().split("\n"): kv = line.strip().split("=", 1) if len(kv) != 2: log(f"Failed to parse environment variable: {line}\n{string}") continue # TODO: why? if kv[0] == "Path": kv[0] = "PATH" os.environ[kv[0]] = kv[1] return os.environ @staticmethod def _callVCVER(version : int, args : []=None, native : bool=True) -> str: if not args: args = [] vswhere = os.path.join(CraftCore.standardDirs.craftBin(), "3rdparty", "vswhere", "vswhere.exe") command = [vswhere, "-property", "installationPath", "-nologo", "-latest"] if version: command += ["-version", f"[{version},{version+1})"] if version < 15: command.append("-legacy") else: if not args: args = ["-products", "*"] if native: # this fails with express versions args += ["-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"] return SetupHelper._getOutput(command + args)[1] @staticmethod def getMSVCEnv(version=None, architecture="x86", toolset=None, native=True) -> str: if native: architectures = {"x86": "amd64_x86", "x64": "amd64"} else: architectures = {"x86": "x86", "x64": "x86_amd64"} args = architectures[architecture] path = "" if version == 14: # are we using msvc2017 with "VC++ 2015.3 v14.00 (v140) toolset for desktop" path = SetupHelper._callVCVER(15, args=["-products", "*", "-requires", "Microsoft.VisualStudio.Component.VC.140"], native=native) if path and not toolset: toolset = "14.0" if toolset: args += f" -vcvars_ver={toolset}" if not path: path = SetupHelper._callVCVER(version, native=native) if not path: log("Please ensure that you have installed the C++ component", critical=True) path = os.path.join(path, "VC") if not os.path.exists(os.path.join(path, "vcvarsall.bat")): path = os.path.join(path, "Auxiliary", "Build") path = os.path.join(path, "vcvarsall.bat") if not os.path.isfile(path): log(f"Failed to setup msvc compiler.\n" f"{path} does not exist.", critical=True) status, result = SetupHelper._getOutput(f"\"{path}\" {args} > NUL && set", shell=True) if status != 0: log(f"Failed to setup msvc compiler.\n" f"Command: {result} ", critical=True) return SetupHelper.stringToEnv(result) def getEnv(self): if CraftCore.compiler.isMSVC(): return SetupHelper.getMSVCEnv(CraftCore.compiler.getInternalVersion(), CraftCore.compiler.architecture, CraftCore.compiler.msvcToolset, CraftCore.compiler.isNative()) elif CraftCore.compiler.isIntel(): architectures = {"x86": "ia32", "x64": "intel64"} programFiles = os.getenv("ProgramFiles(x86)") or os.getenv("ProgramFiles") status, result = SetupHelper._getOutput( "\"%s\\Intel\\Composer XE\\bin\\compilervars.bat\" %s > NUL && set" % ( programFiles, architectures[CraftCore.compiler.architecture]), shell=True) if status != 0: log("Failed to setup intel compiler", critical=True) return SetupHelper.stringToEnv(result) return os.environ def setXDG(self): self.prependEnvVar("XDG_DATA_DIRS", [os.path.join(CraftCore.standardDirs.craftRoot(), "share")]) if OsUtils.isUnix(): self.prependEnvVar("XDG_CONFIG_DIRS", [os.path.join(CraftCore.standardDirs.craftRoot(), "etc", "xdg")]) self.addEnvVar("XDG_DATA_HOME", os.path.join(CraftCore.standardDirs.craftRoot(), "home", os.getenv("USER"), ".local5", "share")) self.addEnvVar("XDG_CONFIG_HOME", os.path.join(CraftCore.standardDirs.craftRoot(), "home", os.getenv("USER"), ".config")) self.addEnvVar("XDG_CACHE_HOME", os.path.join(CraftCore.standardDirs.craftRoot(), "home", os.getenv("USER"), ".cache")) def _setupUnix(self): if CraftCore.compiler.isLinux: self.prependEnvVar("LDFLAGS", "-Wl,-rpath,'$ORIGIN/../lib'", sep=" ") self.prependEnvVar("LD_LIBRARY_PATH", [os.path.join(CraftCore.standardDirs.craftRoot(), "lib"), os.path.join(CraftCore.standardDirs.craftRoot(), "lib", "x86_64-linux-gnu")]) self.prependEnvVar("BISON_PKGDATADIR", os.path.join(CraftCore.standardDirs.craftRoot(), "share", "bison")) self.prependEnvVar("M4", os.path.join(CraftCore.standardDirs.craftRoot(), "dev-utils", "bin", "m4")) def _setupWin(self): if not "HOME" in os.environ: self.addEnvVar("HOME", os.getenv("USERPROFILE")) if CraftCore.compiler.isMinGW(): if not CraftCore.settings.getboolean("QtSDK", "Enabled", "false"): if CraftCore.compiler.isX86(): self.prependEnvVar("PATH", os.path.join(CraftCore.standardDirs.craftRoot(), "mingw", "bin")) else: self.prependEnvVar("PATH", os.path.join(CraftCore.standardDirs.craftRoot(), "mingw64", "bin")) else: compilerName = CraftCore.settings.get("QtSDK", "Compiler") compilerMap = {"mingw53_32": "mingw530_32"} self.prependEnvVar("PATH", os.path.join(CraftCore.settings.get("QtSDK", "Path"), "Tools", compilerMap.get(compilerName, compilerName), "bin")) if CraftCore.settings.getboolean("QtSDK", "Enabled", "false"): self.prependEnvVar("PATH", os.path.join(CraftCore.settings.get("QtSDK", "Path"), CraftCore.settings.get("QtSDK", "Version"), CraftCore.settings.get("QtSDK", "Compiler"), "bin")) if CraftCore.compiler.isMinGW(): if not CraftCore.settings.getboolean("QtSDK", "Enabled", "false"): if CraftCore.compiler.isX86(): self.prependEnvVar("PATH", os.path.join(CraftCore.standardDirs.craftRoot(), "mingw", "bin")) else: self.prependEnvVar("PATH", os.path.join(CraftCore.standardDirs.craftRoot(), "mingw64", "bin")) else: compilerName = CraftCore.settings.get("QtSDK", "Compiler") compilerMap = {"mingw53_32": "mingw530_32"} self.prependEnvVar("PATH", os.path.join(CraftCore.settings.get("QtSDK", "Path"), "Tools", compilerMap.get(compilerName, compilerName), "bin")) def setupEnvironment(self): originaleEnv = CaseInsensitiveDict(os.environ) for var, value in CraftCore.settings.getSection("Environment"): # set and override existing values # the ini is case insensitive so sections are lowercase.... self.addEnvVar(var.upper(), value) self.prependEnvVar("PATH", os.path.dirname(sys.executable)) os.environ = self.getEnv() self.addEnvVar("KDEROOT", CraftCore.standardDirs.craftRoot()) self.addEnvVar("SSL_CERT_FILE", os.path.join(CraftCore.standardDirs.etcDir(), "cacert.pem")) self.addEnvVar("REQUESTS_CA_BUNDLE", os.path.join(CraftCore.standardDirs.etcDir(), "cacert.pem")) if CraftCore.settings.getboolean("Compile", "UseCCache", False): self.addEnvVar("CCACHE_DIR", CraftCore.settings.get("Paths", "CCACHE_DIR", os.path.join(CraftCore.standardDirs.craftRoot(), "build", "CCACHE"))) if CraftCore.settings.getboolean("QtSDK", "Enabled", "false"): sdkPath = os.path.join(CraftCore.settings.get("QtSDK", "Path"), CraftCore.settings.get("QtSDK", "Version"), CraftCore.settings.get("QtSDK", "Compiler"), "bin") if not os.path.exists(sdkPath): log(f"Please ensure that you have installed the Qt SDK in {sdkPath}", critical=True) self.prependEnvVar("PATH", sdkPath) if OsUtils.isWin(): self._setupWin() else: self._setupUnix() PKG_CONFIG_PATH = collections.OrderedDict.fromkeys([os.path.join(CraftCore.standardDirs.craftRoot(), "lib", "pkgconfig")]) if "PKG_CONFIG_PATH" in originaleEnv: PKG_CONFIG_PATH.update(collections.OrderedDict.fromkeys(originaleEnv["PKG_CONFIG_PATH"].split(os.path.pathsep))) else: pkgCOnfig = shutil.which("pkg-config", path=originaleEnv["PATH"]) if pkgCOnfig: out = self._getOutput("pkg-config --variable pc_path pkg-config", shell=True) if out[0] == 0: PKG_CONFIG_PATH.update(collections.OrderedDict.fromkeys(out[1].split(os.path.pathsep))) self.prependEnvVar("PKG_CONFIG_PATH", os.path.pathsep.join(PKG_CONFIG_PATH.keys())) self.prependEnvVar("QT_PLUGIN_PATH", [os.path.join(CraftCore.standardDirs.craftRoot(), "plugins"), os.path.join(CraftCore.standardDirs.craftRoot(), "lib", "plugins"), os.path.join(CraftCore.standardDirs.craftRoot(), "lib64", "plugins"), os.path.join(CraftCore.standardDirs.craftRoot(), "lib", "x86_64-linux-gnu", "plugins"), os.path.join(CraftCore.standardDirs.craftRoot(), "lib", "plugin") ]) self.prependEnvVar("QML2_IMPORT_PATH", [os.path.join(CraftCore.standardDirs.craftRoot(), "qml"), os.path.join(CraftCore.standardDirs.craftRoot(), "lib", "qml"), os.path.join(CraftCore.standardDirs.craftRoot(), "lib64", "qml"), os.path.join(CraftCore.standardDirs.craftRoot(), "lib", "x86_64-linux-gnu", "qml") ]) self.prependEnvVar("QML_IMPORT_PATH", os.environ["QML2_IMPORT_PATH"]) self.prependEnvVar("QT_DATA_DIRS", CraftCore.standardDirs.locations.data) self.setXDG() self.prependEnvVar("PATH", CraftCore.standardDirs.craftBin()) # make sure thate craftroot bin is the first to look for dlls etc self.prependEnvVar("PATH", os.path.join(CraftCore.standardDirs.craftRoot(), "bin")) self.prependEnvVar("PATH", os.path.join(CraftCore.standardDirs.craftRoot(), "dev-utils", "bin")) # add python site packages to pythonpath self.prependEnvVar("PYTHONPATH", os.path.join(CraftCore.standardDirs.craftRoot(), "lib", "site-packages")) if CraftCore.compiler.isClang(): if OsUtils.isUnix(): self.addEnvVar("CC", "/usr/bin/clang") self.addEnvVar("CXX", "/usr/bin/clang++") else: if CraftCore.compiler.isMSVC(): self.addEnvVar("CC", "clang-cl") self.addEnvVar("CXX", "clang-cl") else: self.addEnvVar("CC", "clang") self.addEnvVar("CXX", "clang") elif CraftCore.compiler.isGCC(): if not CraftCore.compiler.isNative() and CraftCore.compiler.isX86(): self.addEnvVar("CC", "gcc -m32") self.addEnvVar("CXX", "g++ -m32") self.addEnvVar("AS", "gcc -c -m32") else: self.addEnvVar("CC", "gcc") self.addEnvVar("CXX", "g++") if CraftCore.settings.getboolean("General", "AllowAnsiColor", False): + # different non standard env switches self.addEnvVar("CLICOLOR_FORCE", "1") self.addEnvVar("CLICOLOR", "1") + self.addEnvVar("ANSICON", "1") if CraftCore.compiler.isClang() and CraftCore.compiler.isMSVC(): self.prependEnvVar("CFLAGS", "-fcolor-diagnostics -fansi-escape-codes", sep=" ") self.prependEnvVar("CXXFLAGS", "-fcolor-diagnostics -fansi-escape-codes", sep=" ") elif CraftCore.compiler.isGCCLike(): self.prependEnvVar("CFLAGS", "-fdiagnostics-color=always", sep=" ") self.prependEnvVar("CXXFLAGS", "-fdiagnostics-color=always", sep=" ") if OsUtils.isWin(): os.environ["TERM"] = "xterm" # pretend to be a common smart terminal def printEnv(self): self.setupEnvironment() for key, val in os.environ.items(): if "\n" in val: log(f"Not adding ${key} to environment since it contains " "a newline character and that breaks craftenv.sh") continue if key.startswith("BASH_FUNC_"): continue # weird protected env vars if key in {"PROFILEREAD"}: continue CraftCore.log.info(f"{key}={val}") @property def version(self): return CraftCore.settings.version helper = SetupHelper() if __name__ == '__main__': helper.run() diff --git a/blueprints/python-modules/coloredlogs/coloredlogs.py b/blueprints/python-modules/coloredlogs/coloredlogs.py new file mode 100644 index 000000000..8e5b2074c --- /dev/null +++ b/blueprints/python-modules/coloredlogs/coloredlogs.py @@ -0,0 +1,20 @@ +import info + +from Package.PipPackageBase import PipPackageBase + + +class subinfo(info.infoclass): + def setTargets(self): + self.svnTargets["master"] = f"https://github.com/TheOneRing/python-coloredlogs.git|winansi" + self.defaultTarget = "master" + + def setDependencies(self): + self.runtimeDependencies["virtual/bin-base"] = None + self.buildDependencies["python-modules/pip"] = None + + +class Package(PipPackageBase): + def __init__(self, **args): + PipPackageBase.__init__(self) + + diff --git a/blueprints/python-modules/pip/pip.py b/blueprints/python-modules/pip/pip.py new file mode 100644 index 000000000..d525c19d5 --- /dev/null +++ b/blueprints/python-modules/pip/pip.py @@ -0,0 +1,41 @@ +import info +from Package.MaybeVirtualPackageBase import * + +from pathlib import Path +import sys + +from Package.PipPackageBase import PipPackageBase + + +class subinfo(info.infoclass): + def setTargets(self): + self.targets["master"] = f"https://bootstrap.pypa.io/get-pip.py" + self.defaultTarget = "master" + + def setDependencies(self): + self.runtimeDependencies["virtual/bin-base"] = None + + +from Package.BinaryPackageBase import * + + +class PPackage(BinaryPackageBase): + def __init__(self): + BinaryPackageBase.__init__(self) + + def unpack(self): + get_pip = self.localFilePathe()[0] + return (utils.system([sys.executable, get_pip]) + and utils.deleteFile(get_pip)) + +class PipPackage(PipPackageBase): + def __init__(self, **args): + PipPackageBase.__init__(self) + + +class Package(MaybeVirtualPackageBase): + def __init__(self): + root = Path(CraftCore.standardDirs.craftRoot()) + py = Path(sys.executable) + MaybeVirtualPackageBase.__init__(self, condition=root in py.parents, classA=PPackage, classB=PipPackage) + diff --git a/blueprints/virtual/base/base.py b/blueprints/virtual/base/base.py index 9784839b9..08800e187 100644 --- a/blueprints/virtual/base/base.py +++ b/blueprints/virtual/base/base.py @@ -1,53 +1,56 @@ import info from Package.VirtualPackageBase import * class subinfo(info.infoclass): def setTargets(self): self.targets['0.2'] = "" self.defaultTarget = '0.2' def setDependencies(self): # The order is important self.buildDependencies["core/cacert"] = None if CraftCore.compiler.isWindows: self.buildDependencies["dev-utils/7zip"] = None self.buildDependencies["dev-utils/wget"] = None self.buildDependencies["dev-utils/git"] = None self.buildDependencies["dev-utils/kshim"] = None self.buildDependencies["dev-utils/cmake"] = None else: self.buildDependencies["dev-utils/cmake"] = None self.buildDependencies["dev-utils/7zip"] = None self.buildDependencies["dev-utils/patch"] = None self.buildDependencies["dev-utils/sed"] = None self.buildDependencies["dev-utils/automake"] = None self.buildDependencies["dev-utils/libtool"] = None if CraftCore.compiler.isMacOS: self.buildDependencies["dev-utils/create-dmg"] = None if CraftCore.compiler.isMinGW(): self.buildDependencies["dev-utils/mingw-w64"] = None if CraftCore.settings.get("Compile", "MakeProgram", "") == "jom": self.buildDependencies["dev-utils/jom"] = None if CraftCore.settings.getboolean("Compile", "UseNinja", False): self.buildDependencies["dev-utils/ninja"] = None if CraftCore.settings.getboolean("Compile", "UseCCache", False): self.buildDependencies["dev-utils/ccache"] = None # needed by CollectionPackagerBase if (CraftCore.settings.getboolean("QtSDK", "Enabled", False) and - CraftCore.settings.getboolean("QtSDK","PackageQtSDK",True)): + CraftCore.settings.getboolean("QtSDK","PackageQtSDK",True)): self.buildDependencies["dev-utils/dependencies"] = None self.buildDependencies["craft/craft-blueprints-kde"] = None self.buildDependencies["craft/craft-core"] = None self.runtimeDependencies["libs/runtime"] = None + if CraftCore.settings.getboolean("General", "AllowAnsiColor", False): + self.buildDependencies["python-modules/coloredlogs"] = None + class Package(VirtualPackageBase): def __init__(self): VirtualPackageBase.__init__(self)