diff --git a/bin/Source/GitSource.py b/bin/Source/GitSource.py index d06bdab80..cb390f0ae 100644 --- a/bin/Source/GitSource.py +++ b/bin/Source/GitSource.py @@ -1,230 +1,237 @@ # -*- coding: utf-8 -*- # Copyright Hannah von Reth # copyright (c) 2009 Ralf Habacker # copyright (c) 2009 Patrick Spendrin # # 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. # git support import io from Source.VersionSystemSourceBase import * ## \todo requires installed git package -> add suport for installing packages class GitSource(VersionSystemSourceBase): """git support""" def __init__(self, subinfo=None): CraftCore.debug.trace('GitSource __init__') if subinfo: self.subinfo = subinfo VersionSystemSourceBase.__init__(self) def __getCurrentBranch(self): if os.path.exists(self.checkoutDir()): with io.StringIO() as tmp: self.__git("rev-parse", ["--abbrev-ref", "HEAD"], stdout=tmp) return tmp.getvalue().strip() return None def __isLocalBranch(self, branch): if os.path.exists(self.checkoutDir()): with io.StringIO() as tmp: self.__git("for-each-ref", ["--format=%(refname:short)", "refs/heads/**"], stdout=tmp) return branch in tmp.getvalue().strip().split("\n") return False def __isTag(self, _tag): if os.path.exists(self.checkoutDir()): with io.StringIO() as tmp: self.__git("tag", stdout=tmp) return _tag in tmp.getvalue().strip().split("\n") return False def __updateSubmodeule(self): if self.subinfo.options.fetch.checkoutSubmodules: return self.__git("submodule", ["update", "--init", "--recursive"]) return True def __getCurrentRevision(self): """return the revision returned by git show""" # run the command branch = self.__getCurrentBranch() if not self.__isTag(branch): with io.StringIO() as tmp: self.__git("rev-parse", ["--short", "HEAD"], stdout=tmp) return f"{branch}-{tmp.getvalue().strip()}" else: # in case this is a tag, print out the tag version return branch def __git(self, command, args=None, logCommand=False, **kwargs): """executes a git command in a shell. Default for cwd is self.checkoutDir()""" parts = ["git", command] if command in ("clone", "checkout", "fetch", "pull", "submodule"): if CraftCore.debug.verbose() < 0: parts += ["-q"] else: kwargs["displayProgress"] = True else: kwargs["logCommand"] = logCommand if args: parts += args if not kwargs.get("cwd"): kwargs["cwd"] = self.checkoutDir() return utils.system(parts, **kwargs) def fetch(self): CraftCore.debug.trace('GitSource fetch') # get the path where the repositories should be stored to repopath = self.repositoryUrl() CraftCore.log.debug("fetching %s" % repopath) # in case you need to move from a read only Url to a writeable one, here it gets replaced repopath = repopath.replace("[git]", "") repoString = utils.replaceVCSUrl(repopath) [repoUrl, repoBranch, repoTag] = utils.splitVCSUrl(repoString) # override tag if self.subinfo.options.dynamic.revision: repoTag = self.subinfo.options.dynamic.revision repoBranch = "" # override the branch if self.subinfo.options.dynamic.branch: repoBranch = self.subinfo.options.dynamic.branch if repoTag and repoBranch: CraftCore.log.error(f"Your not allowed to specify a branch and a tag: branch -> {repoBranch}, tag -> {repoTag}") return False if not repoBranch and not repoTag: repoBranch = "master" # only run if wanted (e.g. no --offline is given on the commandline) if (self.noFetch): CraftCore.log.debug("skipping git fetch (--offline)") return True else: ret = True checkoutDir = self.checkoutDir() # if we only have the checkoutdir but no .git within, # clean this up first if os.path.exists(checkoutDir) \ and not os.path.exists(os.path.join(checkoutDir, ".git")): os.rmdir(checkoutDir) if os.path.isdir(checkoutDir): + if self.subinfo.buildTarget in self.subinfo.targetUpdatedRepoUrl: + oldUrl, newUrl = self.subinfo.targetUpdatedRepoUrl[self.subinfo.buildTarget] + with io.StringIO() as tmp: + self.__git("remote", ["get-url", "origin"], stdout=tmp) + currentUrl = tmp.getvalue().strip() + if currentUrl == oldUrl: + self.__git("remote", ["set-url", "origin", newUrl]) if not repoTag: ret = (self.__git("fetch") and self.__git("checkout", [repoBranch or "master"]) and self.__git("merge")) else: args = [] # it doesn't exist so clone the repo os.makedirs(checkoutDir) # first try to replace with a repo url from etc/blueprints/crafthosts.conf if self.subinfo.options.fetch.checkoutSubmodules: args += ["--recursive"] ret = self.__git('clone', args + [repoUrl, self.checkoutDir()]) # if a branch is given, we should check first if the branch is already downloaded # locally, otherwise we can track the remote branch if ret and repoBranch and not repoTag: if not self.__isLocalBranch(repoBranch): track = ["--track", f"origin/{repoBranch}"] else: track = [repoBranch] ret = self.__git("checkout", track) # we can have tags or revisions in repoTag if ret and repoTag: if self.__isTag(repoTag): if not self.__isLocalBranch("_" + repoTag): ret = self.__git('checkout', ['-b', f"_{repoTag}", repoTag]) else: ret = self.__git('checkout', [f"_{repoTag}"]) else: # unknown tag, try to fetch it first self.__git('fetch', ['--tags']) ret = self.__git('checkout', [repoTag]) return ret and self.__updateSubmodeule() def applyPatch(self, fileName, patchdepth, unusedSrcDir=None): """apply single patch o git repository""" CraftCore.debug.trace('GitSource ') if fileName: patchfile = os.path.join(self.packageDir(), fileName) return self.__git('apply', ['--ignore-space-change', '-p', str(patchdepth), patchfile], logCommand=True) return True def createPatch(self): """create patch file from git source into the related package dir. The patch file is named autocreated.patch""" CraftCore.debug.trace('GitSource createPatch') patchFileName = os.path.join(self.packageDir(), "%s-%s.patch" % \ (self.package.name, str(datetime.date.today()).replace('-', ''))) CraftCore.log.debug("git diff %s" % patchFileName) with open(patchFileName, 'wt+') as patchFile: return self.__git('diff', stdout=patchFile) def sourceVersion(self): """print the revision returned by git show""" CraftCore.debug.trace('GitSource sourceVersion') return self.__getCurrentRevision() def checkoutDir(self, index=0): CraftCore.debug.trace('GitSource checkoutDir') return VersionSystemSourceBase.checkoutDir(self, index) def sourceDir(self, index=0): CraftCore.debug.trace('GitSource sourceDir') repopath = self.repositoryUrl() # in case you need to move from a read only Url to a writeable one, here it gets replaced repopath = repopath.replace("[git]", "") sourcedir = self.checkoutDir(index) if self.subinfo.hasTargetSourcePath(): sourcedir = os.path.join(sourcedir, self.subinfo.targetSourcePath()) CraftCore.log.debug("using sourcedir: %s" % sourcedir) parent, child = os.path.split(sourcedir) return os.path.join(CraftShortPath(parent).shortPath, child) def getUrls(self): """print the url where to clone from and the branch/tag/hash""" # in case you need to move from a read only Url to a writeable one, here it gets replaced repopath = self.repositoryUrl().replace("[git]", "") repoString = utils.replaceVCSUrl(repopath) [repoUrl, repoBranch, repoTag] = utils.splitVCSUrl(repoString) if not repoBranch and not repoTag: repoBranch = "master" print('|'.join([repoUrl, repoBranch, repoTag])) return True diff --git a/bin/info.py b/bin/info.py index 5622fe158..81eb1bf0d 100644 --- a/bin/info.py +++ b/bin/info.py @@ -1,289 +1,290 @@ ## # # @package this module contains the information class # the current work here is to access members only # by methods to be able to separate the access from # the definition from enum import Enum, unique import VersionInfo from Utils import CraftHash, CraftManifest from options import * from CraftDebug import deprecated @unique class DependencyRequirementType(Enum): Optional = 0 Required = 1 class infoclass(object): """this module contains the information class""" def __init__(self, parent): ### package options self.parent = parent self.options = Options(parent.package) self.versionInfo = VersionInfo.VersionInfo(subinfo=self) self.targets = {} self.archiveNames = {} # Specifiy that the fetched source should be placed into a # subdirectory of the default source directory self.targetInstSrc = {} # Specifiy that the default source directory should have a suffix after # the package name. This is usefull for package which needs different sources. self.targetSrcSuffix = {} self.targetConfigurePath = {} self.targetInstallPath = {} self.targetDigests = {} self.targetDigestsX64 = {} self.targetDigestUrls = {} ## \todo prelimary self.svnTargets = {} + self.targetUpdatedRepoUrl = {} self.patchLevel = {} # the build prefix, may differ for for cached files self.buildPrefix = CraftCore.standardDirs.craftRoot() self.isCachedBuild = False # runtimeDependencies and buildDependencies are not different when looking # at the build process itself, they will only make a difference when getting # output of the dependencies self.runtimeDependencies = {} self.buildDependencies = {} self.packagingDependencies = {} self.displayName = self.parent.package.name self.description = "" # tag words describing the package self.tags = "" # a url to the projects webpage self.webpage = "" self.patchToApply = {} # key: target. Value: list(['patchname', patchdepth]) or ('patchname',patchdepth) self.svnTargets = {} self.svnServer = None # this will result in the use of the default server (either anonsvn.kde.org or svn.kde.org) self._defaultTarget = None self.registerOptions() self.setTargets() self.setBuildOptions() # do this after buildTarget is set so that some dependencies can be set depending on self.buildTarget self.setDependencies() @property @deprecated("self.parent") def package(self): # -> PackageBase return self.parent @property def defaultTarget(self) -> str: target = self.options.dynamic.version # TODO: legacy behaviour if ("BlueprintVersions", self.parent.package.path) in CraftCore.settings: target = CraftCore.settings.get("BlueprintVersions", self.parent.package.path) CraftCore.log.warning(f"You are using the depreaced:\n" f"[BlueprintVersions]\n" f"{self.parent.package.path} = {target}\n\n" f"Please use CraftOptions.ini\n" f"[{self.parent.package.path}]\n" f"version = {target}") if target: if target in self.targets or target in self.svnTargets: return target else: raise BlueprintException(f"You defined an invalid target {target} for {self.parent.package.path}", self.parent.package) return self._defaultTarget @defaultTarget.setter def defaultTarget(self, value): self._defaultTarget = value @property def buildTarget(self): return self.defaultTarget def registerOptions(self): """calls to self.options.dynamic.registerOption #self.options.dynamic.registerOption("fullKDevelop", False) """ pass def setDependencies(self): """default method for setting dependencies, override to set individual targets""" def setTargets(self): """default method for setting targets, override to set individual targets""" def setBuildOptions(self): """default method for setting build options, override to set individual targets""" return def hasTarget(self) -> bool: """return true if archive targets for the currently selected build target is available""" return self.buildTarget in self.targets def target(self) -> str: """return archive target""" if self.buildTarget in self.targets: return self.targets[self.buildTarget] return "" def archiveName(self) -> [str]: """returns the archive file name""" if self.buildTarget in self.archiveNames: name = self.archiveNames[self.buildTarget] return name if isinstance(name, list) else [name] if type(self.targets[self.buildTarget]) == list: return [os.path.split(x)[-1] for x in self.targets[self.buildTarget]] else: return [os.path.split(self.targets[self.buildTarget])[-1]] def hasSvnTarget(self) -> bool: """return true if version system based target for the currently selected build target is available""" return self.buildTarget in self.svnTargets def svnTarget(self) -> str: """return version system based target for the currently selected build target""" if self.buildTarget in self.svnTargets: return self.svnTargets[self.buildTarget] return "" def targetSourceSuffix(self) -> str: """return local source path suffix for the recent target""" if self.buildTarget in self.targetSrcSuffix: return self.targetSrcSuffix[self.buildTarget] def hasTargetSourcePath(self) -> bool: """return true if relative path appendable to local source path is given for the recent target""" return self.buildTarget in self.targetInstSrc def targetSourcePath(self) -> str: """return relative path appendable to local source path for the recent target""" if self.buildTarget in self.targetInstSrc: return self.targetInstSrc[self.buildTarget] def hasConfigurePath(self) -> bool: """return true if relative path appendable to local source path is given for the recent target""" return self.buildTarget in self.targetConfigurePath def configurePath(self) -> str: """return relative path appendable to local source path for the recent target""" if (self.hasTarget() or self.hasSvnTarget()) and \ self.buildTarget in self.targetConfigurePath: return self.targetConfigurePath[self.buildTarget] def hasInstallPath(self) -> bool: """return true if relative path appendable to local install path is given for the recent target""" return self.buildTarget in self.targetInstallPath def installPath(self) -> str: """return relative path appendable to local install path for the recent target""" if self.buildTarget in self.targetInstallPath: return self.targetInstallPath[self.buildTarget] CraftCore.log.critical("no install path for this build target defined") def hasPatches(self) -> bool: """return state for having patches for the recent target""" return (self.hasTarget() or self.hasSvnTarget()) and self.buildTarget in self.patchToApply def patchesToApply(self) -> [tuple]: """return patch informations for the recent build target""" if self.hasPatches(): out = self.patchToApply[self.buildTarget] return out if type(out) == list else [out] return [("", "")] def hasTargetDigests(self) -> bool: """return state if target has digest(s) for the recent build target""" if CraftCore.compiler.isX64() and self.buildTarget in self.targetDigestsX64: return True return self.buildTarget in self.targetDigests def targetDigest(self) -> ([str], CraftHash.HashAlgorithm): """return digest(s) for the recent build target. The return value could be a string or a list""" if self.hasTargetDigests(): if CraftCore.compiler.isX64() and self.buildTarget in self.targetDigestsX64: out = self.targetDigestsX64[self.buildTarget] else: out = self.targetDigests[self.buildTarget] if type(out) == str: out = [out] if not type(out) == tuple: out = (out, CraftHash.HashAlgorithm.SHA1) return out return None def hasTargetDigestUrls(self) -> bool: """return state if target has digest url(s) for the recent build target""" return self.buildTarget in self.targetDigestUrls def targetDigestUrl(self) -> ([str], CraftHash.HashAlgorithm): """return digest url(s) for the recent build target. The return value could be a string or a list""" if self.hasTargetDigestUrls(): out = self.targetDigestUrls[self.buildTarget] if isinstance(out, str): out = [out] if not isinstance(out, tuple): out = (out, CraftHash.HashAlgorithm.getAlgorithmFromFile(out[0])) elif not isinstance(out[0], list): out = ([out[0]], out[1]) return out return None def addCachedAutotoolsBuild(self, packageName, targetInstallPath=None): if not CraftCore.compiler.isMSVC(): return self.versionInfo.setDefaultValues() package = CraftPackageObject._allLeaves.get(packageName, None) if not package: raise BlueprintException(f"Failed to find {packageName}", package) packageName = package.path self.description = package.subinfo.description for key, url in list(self.targets.items()): if url.endswith("/"): url = url[:-1] json = CraftCore.cache.cacheJsonFromUrl(f"{url}/manifest.json") if not json: raise BlueprintException("Failed to load manifest", package) manifest = CraftManifest.CraftManifest.fromJson(json) if packageName not in manifest.packages[f"windows-mingw_{CraftCore.compiler.bits}-gcc"]: del self.targets[key] CraftCore.log.warning(f"Failed to find {packageName} on {url}") continue data = manifest.packages[f"windows-mingw_{CraftCore.compiler.bits}-gcc"][packageName].latest self.targets[key] = f"{url}/{data.fileName}" self.targetDigests[key] = ([data.checksum], CraftHash.HashAlgorithm.SHA256) if targetInstallPath: self.targetInstallPath[key] = os.path.join(targetInstallPath, self.parent.package.name) def addCachedBuild(self, url, packageName = None, packagePath=None, targetInstallPath=None): if packageName: package = CraftPackageObject._allLeaves.get(packageName, None) if not package: raise BlueprintException(f"Failed to find {packageName}", package) packagePath = package.path self.description = package.subinfo.description elif not packagePath: packagePath = self.parent.package.path if url.endswith("/"): url = url[:-1] manifest = CraftManifest.CraftManifest.fromJson(CraftCore.cache.cacheJsonFromUrl(f"{url}/manifest.json")) latest = manifest.packages[f"windows-mingw_{CraftCore.compiler.bits}-gcc"][packagePath].latest self.targets[latest.version] = f"{url}/{latest.fileName}" self.targetDigests[latest.version] = ([latest.checksum], CraftHash.HashAlgorithm.SHA256) self.defaultTarget = latest.version if targetInstallPath: self.targetInstallPath[latest.version] = os.path.join(targetInstallPath, self.parent.package.name)