87815216 |
import sys
import os.path |
2820c61a |
from PackageUtils import PackageUtils
from Logger import Logger
from ChrootUtils import ChrootUtils
from ToolChainUtils import ToolChainUtils
from CommandUtils import CommandUtils
from constants import constants |
45c9260c |
from SpecData import SPECS |
56c77555 |
import docker |
4a93976f |
from distutils.version import LooseVersion |
2820c61a |
|
56c77555 |
class PackageBuilderBase(object): |
326d5ca8 |
def __init__(self, mapPackageToCycles, pkgBuildType): |
f3cc9fa8 |
# will be initialized in buildPackageFunction() |
87815216 |
self.logName = None
self.logPath = None
self.logger = None
self.package = None |
2820c61a |
self.mapPackageToCycles = mapPackageToCycles |
87815216 |
self.listNodepsPackages = ["glibc", "gmp", "zlib", "file", "binutils", "mpfr",
"mpc", "gcc", "ncurses", "util-linux", "groff", "perl",
"texinfo", "rpm", "openssl", "go"] |
56c77555 |
self.pkgBuildType = pkgBuildType |
97a9151c |
|
326d5ca8 |
def buildPackageFunction(self, package):
self._buildPackagePrepareFunction(package) |
5d822c2d |
versions = self.getNumOfVersions(package)
if(versions < 1):
raise Exception("No package exists")
for version in range(0, versions):
try:
self._buildPackage(version)
except Exception as e:
# TODO: self.logger might be None
self.logger.exception(e)
raise e |
326d5ca8 |
def _buildPackagePrepareFunction(self, package): |
87815216 |
self.package = package
self.logName = "build-" + package
self.logPath = constants.logPath + "/build-" + package |
56c77555 |
if not os.path.isdir(self.logPath):
cmdUtils = CommandUtils() |
87815216 |
cmdUtils.runCommandInShell("mkdir -p " + self.logPath)
self.logger = Logger.getLogger(self.logName, self.logPath) |
97a9151c |
|
326d5ca8 |
def _findPackageNameFromRPMFile(self, rpmfile): |
87815216 |
rpmfile = os.path.basename(rpmfile)
releaseindex = rpmfile.rfind("-") |
2820c61a |
if releaseindex == -1: |
87815216 |
self.logger.error("Invalid rpm file:" + rpmfile) |
2820c61a |
return None |
87815216 |
versionindex = rpmfile[0:releaseindex].rfind("-") |
2820c61a |
if versionindex == -1: |
87815216 |
self.logger.error("Invalid rpm file:" + rpmfile) |
2820c61a |
return None |
87815216 |
packageName = rpmfile[0:versionindex] |
2820c61a |
return packageName |
97a9151c |
|
326d5ca8 |
def _findInstalledPackages(self, instanceID): |
56c77555 |
pkgUtils = PackageUtils(self.logName, self.logPath)
if self.pkgBuildType == "chroot":
listInstalledRPMs = pkgUtils.findInstalledRPMPackages(instanceID)
elif self.pkgBuildType == "container":
listInstalledRPMs = pkgUtils.findInstalledRPMPackagesInContainer(instanceID) |
87815216 |
listInstalledPackages = [] |
2820c61a |
for installedRPM in listInstalledRPMs: |
326d5ca8 |
packageName = self._findPackageNameFromRPMFile(installedRPM) |
2820c61a |
if packageName is not None:
listInstalledPackages.append(packageName) |
56c77555 |
return listInstalledPackages, listInstalledRPMs |
adf248d5 |
|
5d822c2d |
def _checkIfPackageIsAlreadyBuilt(self, index=0): |
87815216 |
basePkg = SPECS.getData().getSpecName(self.package) |
5d822c2d |
listRPMPackages = SPECS.getData().getRPMPackages(basePkg, index) |
87815216 |
packageIsAlreadyBuilt = True
pkgUtils = PackageUtils(self.logName, self.logPath) |
adf248d5 |
for pkg in listRPMPackages: |
4a93976f |
if pkgUtils.findRPMFileForGivenPackage(pkg,"*", index) is None: |
87815216 |
packageIsAlreadyBuilt = False |
adf248d5 |
break
return packageIsAlreadyBuilt
|
5d822c2d |
def _findRunTimeRequiredRPMPackages(self, rpmPackage, index=0):
return SPECS.getData().getRequiresForPackage(rpmPackage, index) |
97a9151c |
|
5d822c2d |
def _findBuildTimeRequiredPackages(self, index=0):
return SPECS.getData().getBuildRequiresForPackage(self.package, index) |
97a9151c |
|
5d822c2d |
def _findBuildTimeCheckRequiredPackages(self, index=0):
return SPECS.getData().getCheckBuildRequiresForPackage(self.package, index) |
5f40784b |
|
4a93976f |
def _findRunTimeRequiredRPMPackagesParseObj(self,rpmPackage):
listRequiredPackages=SPECS.getData().getRequiresParseObjForPackage(rpmPackage)
return listRequiredPackages
def _findBuildTimeRequiredPackagesParseObj(self, index):
listRequiredPackages=SPECS.getData().getBuildRequiresParseObjForPackage(self.package, index)
return listRequiredPackages
def _findBuildTimeCheckRequiredPackagesParseObj(self, index):
listRequiredPackages=SPECS.getData().getCheckBuildRequiresParseObjForPackage(self.package, index)
return listRequiredPackages
def _getProperVersion(self,package,parseSpecObj):
listOfVersionObjs=self.getSpecObj(package)
for num in listOfVersionObjs:
if parseSpecObj.compare == 'gte':
if LooseVersion(num.version) >= LooseVersion(parseSpecObj.version):
return num.version
elif parseSpecObj.compare == 'lte':
if LooseVersion(num.version) <= LooseVersion(parseSpecObj.version):
return num.version
elif parseSpecObj.compare == 'eq':
if LooseVersion(num.version) == LooseVersion(parseSpecObj.version):
return num.version
elif parseSpecObj.compare == 'lt':
if LooseVersion(num.version) < LooseVersion(parseSpecObj.version):
return num.version
elif parseSpecObj.compare == 'gt':
if LooseVersion(num.version) > LooseVersion(parseSpecObj.version):
return num.version
return "*"
def _installPackage(self, pkgUtils, package,packageVersion, instanceID, destLogPath, |
326d5ca8 |
listInstalledPackages, listInstalledRPMs): |
87815216 |
latestRPM = os.path.basename(
pkgUtils.findRPMFileForGivenPackage(package)).replace(".rpm", "") |
56c77555 |
if package in listInstalledPackages and latestRPM in listInstalledRPMs: |
518d6a6f |
return |
75cb675a |
# mark it as installed - to avoid cyclic recursion
listInstalledPackages.append(package) |
56c77555 |
listInstalledRPMs.append(latestRPM) |
326d5ca8 |
self._installDependentRunTimePackages(pkgUtils, package, instanceID, destLogPath,
listInstalledPackages, listInstalledRPMs) |
87815216 |
noDeps = False |
326d5ca8 |
if (package in self.mapPackageToCycles or
package in self.listNodepsPackages or
package in constants.noDepsPackageList): |
87815216 |
noDeps = True |
56c77555 |
if self.pkgBuildType == "chroot": |
4a93976f |
pkgUtils.installRPM(package, packageVersion,instanceID, noDeps, destLogPath) |
56c77555 |
elif self.pkgBuildType == "container": |
4a93976f |
pkgUtils.prepRPMforInstallInContainer(package,packageVersion, instanceID, noDeps, destLogPath) |
2820c61a |
|
326d5ca8 |
def _installDependentRunTimePackages(self, pkgUtils, package, instanceID, destLogPath,
listInstalledPackages, listInstalledRPMs):
listRunTimeDependentPackages = self._findRunTimeRequiredRPMPackages(package) |
4a93976f |
listRunTimeDependentPackagesParseObj=self._findRunTimeRequiredRPMPackagesParseObj(package) |
326d5ca8 |
if listRunTimeDependentPackages: |
2820c61a |
for pkg in listRunTimeDependentPackages: |
326d5ca8 |
if pkg in self.mapPackageToCycles: |
2820c61a |
continue |
87815216 |
latestPkgRPM = os.path.basename(
pkgUtils.findRPMFileForGivenPackage(pkg)).replace(".rpm", "") |
56c77555 |
if pkg in listInstalledPackages and latestPkgRPM in listInstalledRPMs: |
2820c61a |
continue |
4a93976f |
flag = False
for objName in listRunTimeDependentPackagesParseObj:
if objName.package == pkg:
properVersion=self._getProperVersion(pkg,objName)
self._installPackage(pkgUtils, pkg,properVersion, instanceID, destLogPath,listInstalledPackages, listInstalledRPMs)
flag = True
break;
if flag == False:
self._installPackage(pkgUtils, pkg,"*", instanceID, destLogPath,listInstalledPackages, listInstalledRPMs) |
326d5ca8 |
|
5d822c2d |
def _findDependentPackagesAndInstalledRPM(self, instanceID, index=0): |
326d5ca8 |
listInstalledPackages, listInstalledRPMs = self._findInstalledPackages(instanceID)
self.logger.info(listInstalledPackages) |
5d822c2d |
listDependentPackages = self._findBuildTimeRequiredPackages(index) |
4a93976f |
listDependentPackagesParseObj=self._findBuildTimeRequiredPackagesParseObj(index) |
326d5ca8 |
if constants.rpmCheck and self.package in constants.testForceRPMS: |
5d822c2d |
listDependentPackages.extend(self._findBuildTimeCheckRequiredPackages(index)) |
4a93976f |
listDependentPackagesParseObj.extend(self._findBuildTimeCheckRequiredPackagesParseObj(index)) |
326d5ca8 |
testPackages = (set(constants.listMakeCheckRPMPkgtoInstall) -
set(listInstalledPackages) -
set([self.package]))
listDependentPackages.extend(testPackages)
listDependentPackages = list(set(listDependentPackages)) |
4a93976f |
listDependentPackagesParseObj=list(set(listDependentPackagesParseObj))
return listDependentPackages, listDependentPackagesParseObj,listInstalledPackages, listInstalledRPMs |
326d5ca8 |
|
5d822c2d |
@staticmethod
def getNumOfVersions(package):
return SPECS.getData().getNumberOfVersions(package) |
326d5ca8 |
|
4a93976f |
@staticmethod
def getSpecObj(package):
return SPECS.getData().getSpecObj(package)
|
326d5ca8 |
class PackageBuilderContainer(PackageBuilderBase):
def __init__(self, mapPackageToCycles, pkgBuildType): |
56c77555 |
self.buildContainerImage = "photon_build_container:latest"
self.dockerClient = docker.from_env(version="auto")
|
326d5ca8 |
PackageBuilderBase.__init__(self, mapPackageToCycles, pkgBuildType) |
56c77555 |
|
326d5ca8 |
def _prepareBuildContainer(self, containerTaskName, packageName,
isToolChainPackage=False): |
56c77555 |
# Prepare an empty chroot environment to let docker use the BUILD folder.
# This avoids docker using overlayFS which will cause make check failure. |
87815216 |
chrootName = "build-" + packageName |
326d5ca8 |
chrUtils = ChrootUtils(self.logName, self.logPath) |
87815216 |
returnVal, chrootID = chrUtils.createChroot(chrootName) |
56c77555 |
if not returnVal:
raise Exception("Unable to prepare build root")
cmdUtils = CommandUtils()
cmdUtils.runCommandInShell("mkdir -p " + chrootID + constants.topDirPath)
cmdUtils.runCommandInShell("mkdir -p " + chrootID + constants.topDirPath + "/BUILD")
containerID = None
mountVols = { |
87815216 |
constants.prevPublishRPMRepo: {'bind': '/publishrpms', 'mode': 'ro'},
constants.prevPublishXRPMRepo: {'bind': '/publishxrpms', 'mode': 'ro'},
constants.tmpDirPath: {'bind': '/tmp', 'mode': 'rw'},
constants.rpmPath: {'bind': constants.topDirPath + "/RPMS", 'mode': 'rw'},
constants.sourceRpmPath: {'bind': constants.topDirPath + "/SRPMS", 'mode': 'rw'}, |
326d5ca8 |
constants.logPath + "/" + self.logName: {'bind': constants.topDirPath + "/LOGS",
'mode': 'rw'}, |
87815216 |
chrootID + constants.topDirPath + "/BUILD": {'bind': constants.topDirPath + "/BUILD",
'mode': 'rw'},
constants.dockerUnixSocket: {'bind': constants.dockerUnixSocket, 'mode': 'rw'}
} |
56c77555 |
containerName = containerTaskName
containerName = containerName.replace("+", "p")
try:
oldContainerID = self.dockerClient.containers.get(containerName)
if oldContainerID is not None:
oldContainerID.remove(force=True)
except docker.errors.NotFound: |
87815216 |
try:
sys.exc_clear()
except:
pass |
56c77555 |
try: |
326d5ca8 |
self.logger.info("BuildContainer-prepareBuildContainer: " +
"Starting build container: " + containerName) |
56c77555 |
#TODO: Is init=True equivalent of --sig-proxy?
privilegedDocker = False
cap_list = ['SYS_PTRACE']
if packageName in constants.listReqPrivilegedDockerForTest:
privilegedDocker = True
containerID = self.dockerClient.containers.run(self.buildContainerImage, |
87815216 |
detach=True,
cap_add=cap_list,
privileged=privilegedDocker,
name=containerName,
network_mode="host",
volumes=mountVols,
command="/bin/bash -l -c /wait.sh")
|
326d5ca8 |
self.logger.debug("Started Photon build container for task " + containerTaskName +
" ID: " + containerID.short_id) |
56c77555 |
if not containerID: |
87815216 |
raise Exception("Unable to start Photon build container for task " +
containerTaskName) |
56c77555 |
except Exception as e: |
326d5ca8 |
self.logger.debug("Unable to start Photon build container for task " +
containerTaskName) |
56c77555 |
raise e
return containerID, chrootID |
b5e09fac |
|
5d822c2d |
def _buildPackage(self, index=0): |
56c77555 |
#do not build if RPM is already built
#test only if the package is in the testForceRPMS with rpmCheck
#build only if the package is not in the testForceRPMS with rpmCheck |
5d822c2d |
if self._checkIfPackageIsAlreadyBuilt(index): |
56c77555 |
if not constants.rpmCheck: |
326d5ca8 |
self.logger.info("Skipping building the package:" + self.package) |
56c77555 |
return |
326d5ca8 |
elif constants.rpmCheck and self.package not in constants.testForceRPMS:
self.logger.info("Skipping testing the package:" + self.package) |
56c77555 |
return
#should initialize a logger based on package name |
326d5ca8 |
containerTaskName = "build-" + self.package |
56c77555 |
containerID = None
chrootID = None
isToolChainPackage = False |
326d5ca8 |
if self.package in constants.listToolChainPackages: |
56c77555 |
isToolChainPackage = True |
326d5ca8 |
destLogPath = constants.logPath + "/build-" + self.package |
56c77555 |
try: |
326d5ca8 |
containerID, chrootID = self._prepareBuildContainer(
containerTaskName, self.package, isToolChainPackage) |
56c77555 |
|
326d5ca8 |
tcUtils = ToolChainUtils(self.logName, self.logPath)
if self.package in constants.perPackageToolChain:
self.logger.debug(constants.perPackageToolChain[self.package]) |
87815216 |
tcUtils.installCustomToolChainRPMSinContainer(
containerID, |
8a2c2c4e |
constants.perPackageToolChain[self.package].get(platform.machine(), []), |
326d5ca8 |
self.package)
|
4a93976f |
listDependentPackages,listDependentPackagesParseObj, listInstalledPackages, listInstalledRPMs = ( |
5d822c2d |
self._findDependentPackagesAndInstalledRPM(containerID, index)) |
326d5ca8 |
pkgUtils = PackageUtils(self.logName, self.logPath)
if listDependentPackages:
self.logger.info("BuildContainer-buildPackage: " +
"Installing dependent packages..")
self.logger.info(listDependentPackages) |
56c77555 |
for pkg in listDependentPackages: |
4a93976f |
flag = False
for objName in listDependentPackagesParseObj:
if objName.package == pkg:
properVersion=self._getProperVersion(pkg,objName)
self._installPackage(pkgUtils, pkg,properVersion, containerID, destLogPath,listInstalledPackages, listInstalledRPMs)
flag = True
break;
if flag == False:
self._installPackage(pkgUtils, pkg,"*", containerID, destLogPath,listInstalledPackages, listInstalledRPMs) |
56c77555 |
pkgUtils.installRPMSInAOneShotInContainer(containerID, destLogPath) |
326d5ca8 |
self.logger.info("Finished installing the build time dependent packages....") |
56c77555 |
|
326d5ca8 |
self.logger.info("BuildContainer-buildPackage: Start building the package: " +
self.package) |
5d822c2d |
pkgUtils.adjustGCCSpecsInContainer(self.package, containerID, destLogPath, index) |
56c77555 |
pkgUtils.buildRPMSForGivenPackageInContainer( |
326d5ca8 |
self.package, |
87815216 |
containerID, |
5d822c2d |
destLogPath,
index) |
326d5ca8 |
self.logger.info("BuildContainer-buildPackage: Successfully built the package: " +
self.package) |
56c77555 |
except Exception as e: |
326d5ca8 |
self.logger.error("Failed while building package:" + self.package) |
56c77555 |
if containerID is not None: |
326d5ca8 |
self.logger.debug("Container " + containerID.short_id +
" retained for debugging.")
logFileName = os.path.join(destLogPath, self.package + ".log") |
56c77555 |
fileLog = os.popen('tail -n 20 ' + logFileName).read() |
326d5ca8 |
self.logger.debug(fileLog) |
56c77555 |
raise e
# Remove the container
if containerID is not None:
containerID.remove(force=True)
# Remove the dummy chroot
if chrootID is not None: |
326d5ca8 |
chrUtils = ChrootUtils(self.logName, self.logPath) |
56c77555 |
chrUtils.destroyChroot(chrootID)
|
326d5ca8 |
class PackageBuilderChroot(PackageBuilderBase):
def __init__(self, mapPackageToCycles, pkgBuildType):
PackageBuilderBase.__init__(self, mapPackageToCycles, pkgBuildType) |
56c77555 |
|
326d5ca8 |
def _prepareBuildRoot(self): |
87815216 |
chrootID = None |
326d5ca8 |
chrootName = "build-" + self.package |
56c77555 |
try: |
326d5ca8 |
chrUtils = ChrootUtils(self.logName, self.logPath) |
87815216 |
returnVal, chrootID = chrUtils.createChroot(chrootName) |
326d5ca8 |
self.logger.debug("Created new chroot: " + chrootID) |
56c77555 |
if not returnVal:
raise Exception("Unable to prepare build root") |
326d5ca8 |
tUtils = ToolChainUtils(self.logName, self.logPath)
tUtils.installToolChainRPMS(chrootID, self.package, self.logPath) |
56c77555 |
except Exception as e:
if chrootID is not None: |
326d5ca8 |
self.logger.debug("Deleting chroot: " + chrootID) |
56c77555 |
chrUtils.destroyChroot(chrootID)
raise e
return chrootID
|
5d822c2d |
def _buildPackage(self, index=0): |
56c77555 |
#do not build if RPM is already built
#test only if the package is in the testForceRPMS with rpmCheck
#build only if the package is not in the testForceRPMS with rpmCheck |
5d822c2d |
if self._checkIfPackageIsAlreadyBuilt(index): |
56c77555 |
if not constants.rpmCheck: |
326d5ca8 |
self.logger.info("Skipping building the package:" + self.package) |
56c77555 |
return |
326d5ca8 |
elif constants.rpmCheck and self.package not in constants.testForceRPMS:
self.logger.info("Skipping testing the package:" + self.package) |
56c77555 |
return
|
326d5ca8 |
chrUtils = ChrootUtils(self.logName, self.logPath) |
87815216 |
chrootID = None |
56c77555 |
try: |
326d5ca8 |
chrootID = self._prepareBuildRoot() |
4a93976f |
listDependentPackages,listDependentPackagesParseObj, listInstalledPackages, listInstalledRPMs = ( |
5d822c2d |
self._findDependentPackagesAndInstalledRPM(chrootID, index)) |
326d5ca8 |
pkgUtils = PackageUtils(self.logName, self.logPath) |
5d822c2d |
|
326d5ca8 |
if listDependentPackages:
self.logger.info("Installing the build time dependent packages......") |
56c77555 |
for pkg in listDependentPackages: |
4a93976f |
flag = False
for objName in listDependentPackagesParseObj:
if objName.package == pkg:
properVersion=self._getProperVersion(pkg,objName)
self._installPackage(pkgUtils, pkg,properVersion, chrootID, self.logPath,listInstalledPackages, listInstalledRPMs)
flag = True
break;
if flag == False:
self._installPackage(pkgUtils, pkg,"*", chrootID, self.logPath,listInstalledPackages, listInstalledRPMs) |
326d5ca8 |
pkgUtils.installRPMSInAOneShot(chrootID, self.logPath)
self.logger.info("Finished installing the build time dependent packages....")
|
5d822c2d |
pkgUtils.adjustGCCSpecs(self.package, chrootID, self.logPath, index) |
326d5ca8 |
pkgUtils.buildRPMSForGivenPackage(self.package, chrootID, |
5d822c2d |
self.logPath, index) |
326d5ca8 |
self.logger.info("Successfully built the package:" + self.package) |
56c77555 |
except Exception as e: |
326d5ca8 |
self.logger.error("Failed while building package:" + self.package)
self.logger.debug("Chroot with ID: " + chrootID +
" not deleted for debugging.")
logFileName = os.path.join(self.logPath, self.package + ".log") |
56c77555 |
fileLog = os.popen('tail -n 100 ' + logFileName).read() |
326d5ca8 |
self.logger.debug(fileLog) |
56c77555 |
raise e
if chrootID is not None:
chrUtils.destroyChroot(chrootID) |