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 |
2820c61a |
|
56c77555 |
class PackageBuilderBase(object): |
87815216 |
def __init__(self, mapPackageToCycles, listAvailableCyclicPackages,
listBuildOptionPackages, pkgBuildOptionFile, pkgBuildType): |
7f9d2e12 |
# will be initialized in buildPackageThreadAPI() |
87815216 |
self.logName = None
self.logPath = None
self.logger = None
self.package = None |
2820c61a |
self.mapPackageToCycles = mapPackageToCycles
self.listAvailableCyclicPackages = listAvailableCyclicPackages |
87815216 |
self.listNodepsPackages = ["glibc", "gmp", "zlib", "file", "binutils", "mpfr",
"mpc", "gcc", "ncurses", "util-linux", "groff", "perl",
"texinfo", "rpm", "openssl", "go"]
self.listBuildOptionPackages = listBuildOptionPackages
self.pkgBuildOptionFile = pkgBuildOptionFile |
56c77555 |
self.pkgBuildType = pkgBuildType |
97a9151c |
|
87815216 |
def buildPackageThreadAPIPrepare(self, package, outputMap, threadName):
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 |
|
87815216 |
def findPackageNameFromRPMFile(self, rpmfile):
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 |
|
56c77555 |
def findInstalledPackages(self, instanceID):
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: |
87815216 |
packageName = self.findPackageNameFromRPMFile(installedRPM) |
2820c61a |
if packageName is not None:
listInstalledPackages.append(packageName) |
56c77555 |
return listInstalledPackages, listInstalledRPMs |
adf248d5 |
|
7f9d2e12 |
def checkIfPackageIsAlreadyBuilt(self): |
87815216 |
basePkg = SPECS.getData().getSpecName(self.package)
listRPMPackages = SPECS.getData().getRPMPackages(basePkg)
packageIsAlreadyBuilt = True
pkgUtils = PackageUtils(self.logName, self.logPath) |
adf248d5 |
for pkg in listRPMPackages:
if pkgUtils.findRPMFileForGivenPackage(pkg) is None: |
87815216 |
packageIsAlreadyBuilt = False |
adf248d5 |
break
return packageIsAlreadyBuilt
|
87815216 |
def findRunTimeRequiredRPMPackages(self, rpmPackage):
listRequiredPackages = SPECS.getData().getRequiresForPackage(rpmPackage) |
518d6a6f |
return listRequiredPackages |
97a9151c |
|
7f9d2e12 |
def findBuildTimeRequiredPackages(self): |
87815216 |
listRequiredPackages = SPECS.getData().getBuildRequiresForPackage(self.package) |
518d6a6f |
return listRequiredPackages |
97a9151c |
|
7f9d2e12 |
def findBuildTimeCheckRequiredPackages(self): |
87815216 |
listRequiredPackages = SPECS.getData().getCheckBuildRequiresForPackage(self.package) |
5f40784b |
return listRequiredPackages
|
87815216 |
def installPackage(self, pkgUtils, package, instanceID, destLogPath,
listInstalledPackages, listInstalledRPMs):
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) |
87815216 |
self.installDependentRunTimePackages(pkgUtils, package, instanceID, destLogPath,
listInstalledPackages, listInstalledRPMs)
noDeps = False
if package in self.mapPackageToCycles: |
2820c61a |
noDeps = True
if package in self.listNodepsPackages: |
87815216 |
noDeps = True |
a5e4be9f |
if package in constants.noDepsPackageList: |
87815216 |
noDeps = True |
56c77555 |
if self.pkgBuildType == "chroot": |
87815216 |
pkgUtils.installRPM(package, instanceID, noDeps, destLogPath) |
56c77555 |
elif self.pkgBuildType == "container":
pkgUtils.prepRPMforInstallInContainer(package, instanceID, noDeps, destLogPath) |
2820c61a |
|
87815216 |
def installDependentRunTimePackages(self, pkgUtils, package, instanceID, destLogPath,
listInstalledPackages, listInstalledRPMs):
listRunTimeDependentPackages = self.findRunTimeRequiredRPMPackages(package) |
2820c61a |
if len(listRunTimeDependentPackages) != 0:
for pkg in listRunTimeDependentPackages: |
87815216 |
if pkg in self.mapPackageToCycles and pkg not in self.listAvailableCyclicPackages: |
2820c61a |
continue |
87815216 |
latestPkgRPM = os.path.basename(
pkgUtils.findRPMFileForGivenPackage(pkg)).replace(".rpm", "") |
56c77555 |
if pkg in listInstalledPackages and latestPkgRPM in listInstalledRPMs: |
2820c61a |
continue |
87815216 |
self.installPackage(pkgUtils, pkg, instanceID, destLogPath,
listInstalledPackages, listInstalledRPMs) |
56c77555 |
class PackageBuilderContainer(object): |
87815216 |
def __init__(self, mapPackageToCycles, listAvailableCyclicPackages, listBuildOptionPackages,
pkgBuildOptionFile, pkgBuildType): |
56c77555 |
self.buildContainerImage = "photon_build_container:latest"
self.dockerClient = docker.from_env(version="auto")
self.base = PackageBuilderBase(mapPackageToCycles, listAvailableCyclicPackages,
listBuildOptionPackages, pkgBuildOptionFile, pkgBuildType)
def buildPackageThreadAPI(self, package, outputMap, threadName):
self.base.buildPackageThreadAPIPrepare(package, outputMap, threadName)
try:
self.buildPackage() |
87815216 |
outputMap[threadName] = True |
56c77555 |
except Exception as e:
# TODO: self.logger might be None |
b8ab7fb4 |
self.base.logger.exception(e) |
87815216 |
outputMap[threadName] = False |
56c77555 |
raise e
|
87815216 |
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 |
56c77555 |
chrUtils = ChrootUtils(self.base.logName, self.base.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'},
constants.logPath + "/" + self.base.logName: {'bind': constants.topDirPath + "/LOGS",
'mode': 'rw'},
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: |
87815216 |
self.base.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")
self.base.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: |
87815216 |
self.base.logger.debug("Unable to start Photon build container for task " +
containerTaskName) |
56c77555 |
raise e
return containerID, chrootID |
b5e09fac |
|
56c77555 |
def buildPackage(self):
#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
if self.base.checkIfPackageIsAlreadyBuilt():
if not constants.rpmCheck:
self.base.logger.info("Skipping building the package:" + self.base.package)
return
elif constants.rpmCheck and self.base.package not in constants.testForceRPMS:
self.base.logger.info("Skipping testing the package:" + self.base.package)
return
#should initialize a logger based on package name
containerTaskName = "build-" + self.base.package
containerID = None
chrootID = None
isToolChainPackage = False
if self.base.package in constants.listToolChainPackages:
isToolChainPackage = True
destLogPath = constants.logPath + "/build-" + self.base.package
try: |
87815216 |
containerID, chrootID = self.prepareBuildContainer(
containerTaskName, self.base.package, isToolChainPackage) |
56c77555 |
tcUtils = ToolChainUtils(self.base.logName, self.base.logPath)
if self.base.package in constants.perPackageToolChain:
self.base.logger.debug(constants.perPackageToolChain[self.base.package]) |
87815216 |
tcUtils.installCustomToolChainRPMSinContainer(
containerID,
constants.perPackageToolChain[self.base.package],
self.base.package) |
56c77555 |
listInstalledPackages, listInstalledRPMs = self.base.findInstalledPackages(containerID)
self.base.logger.info(listInstalledPackages)
listDependentPackages = self.base.findBuildTimeRequiredPackages()
if constants.rpmCheck and self.base.package in constants.testForceRPMS:
listDependentPackages.extend(self.base.findBuildTimeCheckRequiredPackages()) |
87815216 |
testPackages = (set(constants.listMakeCheckRPMPkgtoInstall) -
set(listInstalledPackages) -
set([self.base.package])) |
56c77555 |
listDependentPackages.extend(testPackages) |
87815216 |
listDependentPackages = list(set(listDependentPackages)) |
56c77555 |
pkgUtils = PackageUtils(self.base.logName, self.base.logPath)
if len(listDependentPackages) != 0: |
87815216 |
self.base.logger.info("BuildContainer-buildPackage: " +
"Installing dependent packages..") |
56c77555 |
self.base.logger.info(listDependentPackages)
for pkg in listDependentPackages: |
87815216 |
self.base.installPackage(pkgUtils, pkg, containerID, destLogPath,
listInstalledPackages, listInstalledRPMs) |
56c77555 |
pkgUtils.installRPMSInAOneShotInContainer(containerID, destLogPath) |
87815216 |
self.base.logger.info("Finished installing the build time dependent packages....") |
56c77555 |
|
87815216 |
self.base.logger.info("BuildContainer-buildPackage: Start building the package: " +
self.base.package) |
56c77555 |
pkgUtils.adjustGCCSpecsInContainer(self.base.package, containerID, destLogPath)
pkgUtils.buildRPMSForGivenPackageInContainer( |
87815216 |
self.base.package,
containerID,
self.base.listBuildOptionPackages,
self.base.pkgBuildOptionFile,
destLogPath)
self.base.logger.info("BuildContainer-buildPackage: Successfully built the package: " +
self.base.package) |
56c77555 |
except Exception as e:
self.base.logger.error("Failed while building package:" + self.base.package)
if containerID is not None: |
87815216 |
self.base.logger.debug("Container " + containerID.short_id +
" retained for debugging.") |
56c77555 |
logFileName = os.path.join(destLogPath, self.base.package + ".log")
fileLog = os.popen('tail -n 20 ' + logFileName).read()
self.base.logger.debug(fileLog)
raise e
# Remove the container
if containerID is not None:
containerID.remove(force=True)
# Remove the dummy chroot
if chrootID is not None:
chrUtils = ChrootUtils(self.base.logName, self.base.logPath)
chrUtils.destroyChroot(chrootID)
class PackageBuilderChroot(object): |
87815216 |
def __init__(self, mapPackageToCycles, listAvailableCyclicPackages, listBuildOptionPackages,
pkgBuildOptionFile, pkgBuildType): |
56c77555 |
self.base = PackageBuilderBase(mapPackageToCycles, listAvailableCyclicPackages,
listBuildOptionPackages, pkgBuildOptionFile, pkgBuildType)
def buildPackageThreadAPI(self, package, outputMap, threadName): |
87815216 |
self.base.buildPackageThreadAPIPrepare(package, outputMap, threadName) |
56c77555 |
try:
self.buildPackage() |
87815216 |
outputMap[threadName] = True |
56c77555 |
except Exception as e:
# TODO: self.logger might be None |
b8ab7fb4 |
self.base.logger.exception(e) |
87815216 |
outputMap[threadName] = False |
56c77555 |
raise e
def prepareBuildRoot(self): |
87815216 |
chrootID = None
chrootName = "build-" + self.base.package |
56c77555 |
try: |
87815216 |
chrUtils = ChrootUtils(self.base.logName, self.base.logPath)
returnVal, chrootID = chrUtils.createChroot(chrootName) |
56c77555 |
self.base.logger.debug("Created new chroot: " + chrootID)
if not returnVal:
raise Exception("Unable to prepare build root") |
87815216 |
tUtils = ToolChainUtils(self.base.logName, self.base.logPath) |
56c77555 |
tUtils.installToolChainRPMS(chrootID, self.base.package, self.base.logPath)
except Exception as e:
if chrootID is not None:
self.base.logger.debug("Deleting chroot: " + chrootID)
chrUtils.destroyChroot(chrootID)
raise e
return chrootID
def buildPackage(self):
#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
if self.base.checkIfPackageIsAlreadyBuilt():
if not constants.rpmCheck:
self.base.logger.info("Skipping building the package:" + self.base.package)
return
elif constants.rpmCheck and self.base.package not in constants.testForceRPMS:
self.base.logger.info("Skipping testing the package:" + self.base.package)
return
|
87815216 |
chrUtils = ChrootUtils(self.base.logName, self.base.logPath)
chrootID = None |
56c77555 |
try:
chrootID = self.prepareBuildRoot()
listInstalledPackages, listInstalledRPMs = self.base.findInstalledPackages(chrootID) |
87815216 |
listDependentPackages = self.base.findBuildTimeRequiredPackages() |
56c77555 |
if constants.rpmCheck and self.base.package in constants.testForceRPMS:
listDependentPackages.extend(self.base.findBuildTimeCheckRequiredPackages()) |
87815216 |
testPackages = (set(constants.listMakeCheckRPMPkgtoInstall) -
set(listInstalledPackages) -
set([self.base.package])) |
56c77555 |
listDependentPackages.extend(testPackages) |
87815216 |
listDependentPackages = list(set(listDependentPackages)) |
56c77555 |
|
87815216 |
pkgUtils = PackageUtils(self.base.logName, self.base.logPath) |
56c77555 |
if len(listDependentPackages) != 0:
self.base.logger.info("Installing the build time dependent packages......")
for pkg in listDependentPackages: |
87815216 |
self.base.installPackage(pkgUtils, pkg, chrootID, self.base.logPath,
listInstalledPackages, listInstalledRPMs) |
56c77555 |
pkgUtils.installRPMSInAOneShot(chrootID, self.base.logPath) |
87815216 |
self.base.logger.info("Finished installing the build time dependent packages....") |
56c77555 |
pkgUtils.adjustGCCSpecs(self.base.package, chrootID, self.base.logPath) |
87815216 |
pkgUtils.buildRPMSForGivenPackage(self.base.package, chrootID,
self.base.listBuildOptionPackages,
self.base.pkgBuildOptionFile,
self.base.logPath) |
56c77555 |
self.base.logger.info("Successfully built the package:" + self.base.package)
except Exception as e:
self.base.logger.error("Failed while building package:" + self.base.package) |
87815216 |
self.base.logger.debug("Chroot with ID: " + chrootID +
" not deleted for debugging.") |
56c77555 |
logFileName = os.path.join(self.base.logPath, self.base.package + ".log")
fileLog = os.popen('tail -n 100 ' + logFileName).read()
self.base.logger.debug(fileLog)
raise e
if chrootID is not None:
chrUtils.destroyChroot(chrootID) |