- Add mock-specific build artifacts (chroot/, mock-*, mockroot/) - Include package build files (*.deb, *.changes, *.buildinfo) - Add development tools (.coverage, .pytest_cache, .tox) - Include system files (.DS_Store, Thumbs.db, ._*) - Add temporary and backup files (*.tmp, *.bak, *.backup) - Include local configuration overrides (config.local.yaml, .env.local) - Add test artifacts and documentation builds - Comprehensive coverage for Python build system project This ensures build artifacts, chroot environments, and development tools are properly ignored in version control.
1181 lines
51 KiB
Python
Executable file
1181 lines
51 KiB
Python
Executable file
#!/usr/bin/python3 -tt
|
|
# -*- coding: utf-8 -*-
|
|
# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:textwidth=0:
|
|
|
|
# Originally written by Seth Vidal
|
|
# Sections taken from Mach by Thomas Vander Stichele
|
|
# Major reorganization and adaptation by Michael Brown
|
|
# Copyright (C) 2007 Michael E Brown <mebrown@michaels-house.net>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Library General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
# pylint: disable=pointless-string-statement,wrong-import-position
|
|
"""
|
|
mock [options] {--init|--clean|--scrub=[all,chroot,cache,root-cache,c-cache,yum-cache,dnf-cache,lvm,overlayfs]}
|
|
mock [options] [--rebuild] /path/to/srpm(s)
|
|
mock [options] [--chain] /path/to/srpm(s)
|
|
mock [options] --buildsrpm {--spec /path/to/spec --sources /path/to/src|
|
|
--scm-enable [--scm-option key=value]}
|
|
mock [options] {--shell|--chroot} <cmd>
|
|
mock [options] --installdeps {SRPM|RPM}
|
|
mock [options] --install PACKAGE
|
|
mock [options] --copyin path [..path] destination
|
|
mock [options] --copyout path [..path] destination
|
|
mock [options] --scm-enable [--scm-option key=value]
|
|
mock [options] --dnf-cmd arguments
|
|
mock [options] --yum-cmd arguments
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
# library imports
|
|
import argparse
|
|
import configparser
|
|
import errno
|
|
import glob
|
|
import grp
|
|
import logging
|
|
import logging.config
|
|
|
|
from pprint import pformat
|
|
import os
|
|
import os.path
|
|
import signal
|
|
import shutil
|
|
import shlex
|
|
import sys
|
|
import time
|
|
import copy
|
|
|
|
# pylint: disable=import-error
|
|
from functools import partial
|
|
|
|
# our imports
|
|
from mockbuild import config
|
|
from mockbuild import util
|
|
from mockbuild.constants import MOCKCONFDIR, VERSION
|
|
from mockbuild.file_downloader import FileDownloader
|
|
from mockbuild.mounts import BindMountPoint, FileSystemMountPoint
|
|
import mockbuild.backend
|
|
from mockbuild.backend import Commands
|
|
from mockbuild.buildroot import Buildroot
|
|
import mockbuild.exception
|
|
from mockbuild.plugin import Plugins
|
|
import mockbuild.rebuild
|
|
from mockbuild.state import State
|
|
from mockbuild.trace_decorator import traceLog
|
|
import mockbuild.uid
|
|
from mockbuild.scrub_all import scrub_all_chroots
|
|
|
|
# set up basic logging until config file can be read
|
|
FORMAT = "%(levelname)s: %(message)s"
|
|
logging.basicConfig(format=FORMAT, level=logging.WARNING)
|
|
log = logging.getLogger()
|
|
|
|
|
|
signal_names = {1: "SIGHUP",
|
|
13: "SIGPIPE",
|
|
15: "SIGTERM"
|
|
}
|
|
|
|
# This line is replaced by spec file %install section.
|
|
_MOCK_NVR = None
|
|
|
|
|
|
class RepoCallback(argparse.Action):
|
|
""" Parse --enablerepo/--disablerepo options """
|
|
def __call__(self, parser, namespace, value, option_string=None):
|
|
if not hasattr(namespace, self.dest):
|
|
setattr(namespace, self.dest, [])
|
|
options = getattr(namespace, self.dest)
|
|
options.extend((option_string, value))
|
|
|
|
|
|
def command_parse():
|
|
"""return options and args from parsing the command line"""
|
|
plugins = config.PLUGIN_LIST
|
|
parser = argparse.ArgumentParser(usage=__doc__)
|
|
|
|
parser.add_argument('--version', action='version', version=VERSION)
|
|
|
|
# hack from optparse=>argparse migration time, use add_argument if possible
|
|
parser.add_option = parser.add_argument
|
|
|
|
# modes (basic commands)
|
|
parser.add_option("--rebuild", action="store_const", const="rebuild",
|
|
dest="mode", default='__default__',
|
|
help="rebuild the specified SRPM(s)")
|
|
parser.add_option("--calculate-build-dependencies", action="store_const",
|
|
const="calculatedeps", dest="mode",
|
|
help="Resolve and install static and dynamic build dependencies")
|
|
parser.add_option("--chain", action="store_const", const="chain",
|
|
dest="mode",
|
|
help="build multiple RPMs in chain loop")
|
|
parser.add_option("--buildsrpm", action="store_const", const="buildsrpm",
|
|
dest="mode",
|
|
help="Build a SRPM from spec (--spec ...) and sources"
|
|
"(--sources ...) or from SCM")
|
|
parser.add_option("--debug-config", action="store_const", const="debugconfig",
|
|
dest="mode",
|
|
help="Prints all options in config_opts")
|
|
parser.add_option("--debug-config-expanded", action="store_const", const="debugconfigexpand",
|
|
dest="mode",
|
|
help="Prints all options in config_opts with jinja template values already expanded")
|
|
parser.add_option("--shell", action="store_const",
|
|
const="shell", dest="mode",
|
|
help="run the specified command interactively within the chroot."
|
|
" Default command: /bin/sh")
|
|
parser.add_option("--chroot", action="store_const",
|
|
const="chroot", dest="mode",
|
|
help="run the specified command noninteractively within the chroot.")
|
|
parser.add_option("--clean", action="store_const", const="clean",
|
|
dest="mode",
|
|
help="completely remove the specified chroot")
|
|
scrub_choices = ('chroot', 'cache', 'root-cache', 'c-cache', 'yum-cache',
|
|
'dnf-cache', 'lvm', 'overlayfs', 'bootstrap', 'all')
|
|
scrub_metavar = "[all|chroot|cache|root-cache|c-cache|yum-cache|dnf-cache]"
|
|
parser.add_option("--scrub", action='append', choices=scrub_choices, default=[],
|
|
metavar=scrub_metavar,
|
|
help="completely remove the specified chroot "
|
|
"or cache dir or all of the chroot and cache")
|
|
parser.add_option(
|
|
"--scrub-all-chroots", action="store_const", dest="mode",
|
|
const="scrub-all-chroots", help=(
|
|
"Run mock --scrub=all for all chroots that appear to have been "
|
|
"used previously (see manual page for more info)."
|
|
),
|
|
|
|
)
|
|
parser.add_option("--init", action="store_const", const="init", dest="mode",
|
|
help="initialize the chroot, do not build anything")
|
|
parser.add_option("--installdeps", action="store_const", const="installdeps",
|
|
dest="mode",
|
|
help="install build dependencies for a specified SRPM or SPEC file")
|
|
parser.add_option("-i", "--install", action="store_const", const="install",
|
|
dest="mode",
|
|
help="install packages using package manager")
|
|
parser.add_option("--list-chroots", action="store_const", const="listchroots",
|
|
dest="mode",
|
|
help="List all chroot's configs")
|
|
parser.add_option("--update", action="store_const", const="update",
|
|
dest="mode",
|
|
help="update installed packages using package manager")
|
|
parser.add_option("--remove", action="store_const", const="remove",
|
|
dest="mode",
|
|
help="remove packages using package manager")
|
|
parser.add_option("--orphanskill", action="store_const", const="orphanskill",
|
|
dest="mode",
|
|
help="Kill all processes using specified buildroot.")
|
|
|
|
parser.add_option("--copyin", action="store_const", const="copyin",
|
|
dest="mode",
|
|
help="Copy file(s) into the specified chroot")
|
|
|
|
parser.add_option("--copyout", action="store_const", const="copyout",
|
|
dest="mode",
|
|
help="Copy file(s) from the specified chroot")
|
|
|
|
parser.add_option("--pm-cmd", action="store_const", const="pm-cmd",
|
|
dest="mode",
|
|
help="Execute package management command (with yum or dnf)")
|
|
|
|
parser.add_option("--yum-cmd", action="store_const", const="yum-cmd",
|
|
dest="mode",
|
|
help="Execute package management command with yum")
|
|
|
|
parser.add_option("--dnf-cmd", action="store_const", const="dnf-cmd",
|
|
dest="mode",
|
|
help="Execute package management command with dnf")
|
|
|
|
parser.add_option("--snapshot", action="store_const", const="snapshot",
|
|
dest="mode",
|
|
help="Create a new LVM/overlayfs snapshot with given name")
|
|
|
|
parser.add_option("--remove-snapshot", action="store_const", const="remove_snapshot",
|
|
dest="mode",
|
|
help="Remove LVM/overlayfs snapshot with given name")
|
|
|
|
parser.add_option("--rollback-to", action="store_const", const="rollback-to",
|
|
dest="mode",
|
|
help="Rollback to given snapshot")
|
|
|
|
parser.add_option("--umount", action="store_const", const="umount",
|
|
dest="mode", help="Umount the buildroot if it's "
|
|
"mounted from separate device (LVM/overlayfs)")
|
|
parser.add_option("--mount", action="store_const", const="mount",
|
|
dest="mode", help="Mount the buildroot if it's "
|
|
"mounted from separate device (LVM/overlayfs)")
|
|
# chain
|
|
parser.add_option('--localrepo', default=None,
|
|
help=("local path for the local repo, defaults to making "
|
|
"its own (--chain mode only)"))
|
|
parser.add_option('-c', '--continue', default=False, action='store_true',
|
|
dest='cont',
|
|
help="if a pkg fails to build, continue to the next one")
|
|
parser.add_option(
|
|
'-a', '--addrepo', default=[], action='append', dest='repos',
|
|
metavar="REPO", help=(
|
|
"Add a repo baseurl to the DNF/YUM configuration for both the "
|
|
"build chroot and the bootstrap chroot. This option can be "
|
|
"specified multiple times, allowing you to reference multiple "
|
|
"repositories in addition to the default repository set."))
|
|
parser.add_option('--recurse', default=False, action='store_true',
|
|
help="if more than one pkg and it fails to build, try to build the rest and come back to it")
|
|
parser.add_option('--tmp_prefix', default=None, dest='tmp_prefix',
|
|
help="tmp dir prefix - will default to username-pid if not specified")
|
|
# options
|
|
parser.add_option("-r", "--root", action="store", type=str, dest="chroot",
|
|
help="chroot config file name or path. Taken as a path if it ends "
|
|
"in .cfg, otherwise looked up in the configdir. default: %%default",
|
|
metavar="CONFIG",
|
|
default='default')
|
|
|
|
parser.add_option("--offline", action="store_false", dest="online",
|
|
default=True,
|
|
help="activate 'offline' mode.")
|
|
|
|
parser.add_option("-n", "--no-clean", action="store_false", dest="clean",
|
|
help="do not clean chroot before building", default=True)
|
|
parser.add_option("--cleanup-after", action="store_true",
|
|
dest="cleanup_after", default=None,
|
|
help="Clean chroot after building. Use with --resultdir."
|
|
" Only active for 'rebuild'.")
|
|
parser.add_option("-N", "--no-cleanup-after", action="store_false",
|
|
dest="cleanup_after", default=None,
|
|
help="Don't clean chroot after building. If automatic"
|
|
" cleanup is enabled, use this to disable.", )
|
|
parser.add_option("--cache-alterations", action="store_true",
|
|
dest="cache_alterations", default=False,
|
|
help="Rebuild the root cache after making alterations to the chroot"
|
|
" (i.e. --install). Only useful when using tmpfs plugin.")
|
|
parser.add_option("--nocheck", action="store_false", dest="check",
|
|
default=True, help="pass --nocheck to rpmbuild to skip 'make check' tests")
|
|
parser.add_option("--arch", action="store", dest="arch",
|
|
default=None, help="Sets kernel personality().")
|
|
parser.add_option("--forcearch", action="store", dest="forcearch",
|
|
default=None, help="Force architecture to DNF (pass --forcearch to DNF).")
|
|
parser.add_option("--target", action="store", dest="rpmbuild_arch",
|
|
default=None, help="passed to rpmbuild as --target")
|
|
parser.add_option("-D", "--define", action="append", dest="rpmmacros",
|
|
default=[], type=str, metavar="'MACRO EXPR'",
|
|
help="define an rpm macro (may be used more than once)")
|
|
parser.add_option("--macro-file", action="store", type=str, dest="macrofile",
|
|
default=[], help="Use pre-defined rpm macro file")
|
|
parser.add_option("--with", action="append", dest="rpmwith",
|
|
default=[], type=str, metavar="option",
|
|
help="enable configure option for build (may be used more than once)")
|
|
parser.add_option("--without", action="append", dest="rpmwithout",
|
|
default=[], type=str, metavar="option",
|
|
help="disable configure option for build (may be used more than once)")
|
|
parser.add_option("--resultdir", action="store", type=str,
|
|
default=None, help="path for resulting files to be put")
|
|
parser.add_option("--rootdir", action="store", type=str,
|
|
default=None, help="Path for where the chroot should be built")
|
|
parser.add_option("--uniqueext", action="store", type=str,
|
|
default=None,
|
|
help="Arbitrary, unique extension to append to buildroot"
|
|
" directory name")
|
|
parser.add_option("--configdir", action="store", dest="configdir",
|
|
default=None,
|
|
help="Change where config files are found")
|
|
parser.add_option("--config-opts", action="append", dest="cli_config_opts",
|
|
default=[], help="Override configuration option.")
|
|
parser.add_option("--rpmbuild_timeout", action="store",
|
|
dest="rpmbuild_timeout", type=int, default=None,
|
|
help="Fail build if rpmbuild takes longer than 'timeout'"
|
|
" seconds ")
|
|
parser.add_option("--unpriv", action="store_true", default=False,
|
|
help="Drop privileges before running command when using --chroot")
|
|
parser.add_option("--cwd", action="store", default=None,
|
|
metavar="DIR",
|
|
help="Change to the specified directory (relative to the chroot)"
|
|
" before running command when using --chroot")
|
|
|
|
parser.add_option("--spec", action="store",
|
|
help="Specifies spec file to use to build an SRPM")
|
|
parser.add_option("--sources", action="store",
|
|
help="Specifies sources (either a single file or a directory of files)"
|
|
"to use to build an SRPM (used only with --buildsrpm)")
|
|
parser.add_option("--symlink-dereference", action="store_true", dest="symlink_dereference",
|
|
default=False, help="Follow symlinks in sources (used only with --buildsrpm)")
|
|
|
|
parser.add_option("--short-circuit",
|
|
choices=['prep', 'install', 'build', 'binary'],
|
|
help="Pass short-circuit option to rpmbuild to skip already "
|
|
"complete stages. Warning: produced packages are unusable. "
|
|
"Implies --no-clean. Valid options: build, install, binary")
|
|
parser.add_option("--rpmbuild-opts", action="store",
|
|
help="Pass additional options to rpmbuild")
|
|
parser.add_option("--enablerepo", action=RepoCallback, type=str,
|
|
dest="enable_disable_repos", default=[],
|
|
help="Pass enablerepo option to yum/dnf", metavar='[repo]')
|
|
parser.add_option("--disablerepo", action=RepoCallback, type=str,
|
|
dest="enable_disable_repos", default=[],
|
|
help="Pass disablerepo option to yum/dnf", metavar='[repo]')
|
|
parser.add_option("--old-chroot", action="store_true", dest="old_chroot",
|
|
default=False,
|
|
help="Obsoleted. Use --isolation=simple")
|
|
parser.add_option("--new-chroot", action="store_true", dest="new_chroot",
|
|
default=False,
|
|
help="Obsoleted. Use --isolation=nspawn")
|
|
parser.add_option("--isolation", action="store", dest="isolation",
|
|
help="what level of isolation to use. Valid option: simple, nspawn")
|
|
parser.add_option("--enable-network", action="store_true", dest="enable_network",
|
|
default=False,
|
|
help="enable networking.")
|
|
parser.add_option("--postinstall", action="store_true", dest="post_install",
|
|
default=False, help="Try to install built packages in "
|
|
"the same buildroot right after build")
|
|
|
|
# verbosity
|
|
parser.add_option("-v", "--verbose", action="store_const", const=2,
|
|
dest="verbose", default=1, help="verbose build")
|
|
parser.add_option("-q", "--quiet", action="store_const", const=0,
|
|
dest="verbose", help="quiet build")
|
|
parser.add_option("--trace", action="store_true", default=False,
|
|
dest="trace", help="Enable internal mock tracing output.")
|
|
|
|
# plugins
|
|
parser.add_option("--enable-plugin", action="append",
|
|
dest="enabled_plugins", type=str, default=[],
|
|
help="Enable plugin. Currently-available plugins: %s"
|
|
% repr(plugins))
|
|
parser.add_option("--disable-plugin", action="append",
|
|
dest="disabled_plugins", type=str, default=[],
|
|
help="Disable plugin. Currently-available plugins: %s"
|
|
% repr(plugins))
|
|
parser.add_option("--plugin-option", action="append", dest="plugin_opts",
|
|
default=[], type=str,
|
|
metavar="PLUGIN:KEY=VALUE",
|
|
help="define an plugin option (may be used more than once)")
|
|
|
|
parser.add_option("-p", "--print-root-path", help="print path to chroot root",
|
|
dest="printrootpath", action="store_true",
|
|
default=False)
|
|
|
|
parser.add_option("-l", "--list-snapshots",
|
|
help="list LVM/overlayfs snapshots associated with buildroot",
|
|
dest="list_snapshots", action="store_true",
|
|
default=False)
|
|
|
|
# SCM options
|
|
parser.add_option("--scm-enable", help="build from SCM repository",
|
|
dest="scm", action="store_true",
|
|
default=None)
|
|
parser.add_option("--scm-option", action="append", dest="scm_opts",
|
|
default=[], type=str,
|
|
help="define an SCM option (may be used more than once)")
|
|
|
|
# Package management options
|
|
parser.add_option("--yum", help="use yum as package manager",
|
|
dest="pkg_manager", action="store_const", const="yum")
|
|
parser.add_option("--dnf", help="use dnf as package manager",
|
|
dest="pkg_manager", action="store_const", const="dnf")
|
|
|
|
# Bootstrap options
|
|
parser.add_option('--bootstrap-chroot', dest='bootstrapchroot', action='store_true',
|
|
help="build in two stages, using chroot rpm for creating the build chroot",
|
|
default=None)
|
|
parser.add_option('--no-bootstrap-chroot', dest='bootstrapchroot', action='store_false',
|
|
help="build in a single stage, using system rpm for creating the build chroot",
|
|
default=None)
|
|
|
|
parser.add_option('--use-bootstrap-image', dest='usebootstrapimage', action='store_true',
|
|
help="create bootstrap chroot from container image (turns "
|
|
"--bootstrap-chroot on)", default=None)
|
|
parser.add_option('--no-bootstrap-image', dest='usebootstrapimage', action='store_false',
|
|
help="don't create bootstrap chroot from container image", default=None)
|
|
|
|
parser.add_option('--buildroot-image',
|
|
help=(
|
|
"Use an OCI image (or a local file containing an OCI "
|
|
"image as a tarball) as the base for the buildroot. "
|
|
"The image must contain a compatible distribution "
|
|
"(e.g., fedora:41 for fedora-41-x86_64)"))
|
|
|
|
parser.add_option('--additional-package', action='append', default=[],
|
|
type=str, dest="additional_packages",
|
|
help=("Additional package to install into the buildroot before "
|
|
"the build is done. Can be specified multiple times."))
|
|
parser.add_option("--hermetic-build", nargs=2,
|
|
metavar=("LOCKFILE", "REPO_DIRECTORY"),
|
|
help="Perform a hermetic (fully offline) build")
|
|
|
|
(options, args) = parser.parse_known_args()
|
|
|
|
if options.mode == '__default__':
|
|
# handle old-style commands
|
|
if len(args) and args[0] in ('chroot', 'shell', 'rebuild', 'install',
|
|
'installdeps', 'remove', 'init', 'clean'):
|
|
options.mode = args[0]
|
|
args = args[1:]
|
|
else:
|
|
options.mode = 'rebuild'
|
|
|
|
if options.hermetic_build and options.chroot != 'default':
|
|
raise mockbuild.exception.BadCmdline(
|
|
"The --hermetic-build mode uses a special chroot configuration, "
|
|
"you can not select the chroot configuration with the "
|
|
"-r/--root option.")
|
|
|
|
if options.hermetic_build and options.mode != "rebuild":
|
|
raise mockbuild.exception.BadCmdline("--rebuild mode needed with --hermetic-build")
|
|
|
|
options.calculatedeps = None
|
|
if options.mode == "calculatedeps":
|
|
options.mode = "rebuild"
|
|
options.calculatedeps = True
|
|
|
|
# Optparse.parse_args() eats '--' argument, while argparse doesn't. Do it manually.
|
|
if args and args[0] == '--':
|
|
args = args[1:]
|
|
|
|
if options.scrub:
|
|
options.mode = 'clean'
|
|
|
|
# explicitly disallow multiple targets in --target argument
|
|
if options.rpmbuild_arch:
|
|
if options.rpmbuild_arch.find(',') != -1:
|
|
raise mockbuild.exception.BadCmdline("--target option accepts only "
|
|
"one arch. Invalid: %s" % options.rpmbuild_arch)
|
|
|
|
if options.mode == 'buildsrpm' and not (options.spec):
|
|
if not options.scm:
|
|
raise mockbuild.exception.BadCmdline("Must specify both --spec and "
|
|
"--sources with --buildsrpm")
|
|
|
|
if options.recurse:
|
|
# --recurse implies --continue
|
|
options.cont = True
|
|
|
|
if options.localrepo and options.mode != 'chain':
|
|
raise mockbuild.exception.BadCmdline(
|
|
"The --localrepo option works only with --chain")
|
|
|
|
if options.recurse and options.mode != 'chain':
|
|
raise mockbuild.exception.BadCmdline(
|
|
"Only --chain mode supports --recurse build algorithm")
|
|
|
|
if options.cont and options.mode != 'chain':
|
|
raise mockbuild.exception.BadCmdline(
|
|
"Only --chain mode supports --continue build algorithm")
|
|
|
|
if options.spec:
|
|
options.spec = os.path.expanduser(options.spec)
|
|
if options.sources:
|
|
options.sources = os.path.expanduser(options.sources)
|
|
|
|
if options.additional_packages and options.mode != 'rebuild':
|
|
raise mockbuild.exception.BadCmdline(
|
|
"The --additional-package option requires the --rebuild mode")
|
|
|
|
return (options, args)
|
|
|
|
|
|
def handle_signals(buildroot, number, frame):
|
|
|
|
log.info("\nReceived signal %s activating orphansKill", signal_names[number])
|
|
util.orphansKill(buildroot.make_chroot_path())
|
|
sys.exit(128 + number)
|
|
|
|
|
|
@traceLog()
|
|
def setup_logging(config_path, config_opts, options):
|
|
log_ini = os.path.join(config_path, config_opts["log_config_file"])
|
|
|
|
try:
|
|
if not os.path.exists(log_ini):
|
|
if os.path.normpath('/etc/mock') != os.path.normpath(config_path):
|
|
log.warning("Could not find required logging config file: %s. Using default...",
|
|
log_ini)
|
|
log_ini = os.path.join("/etc/mock", config_opts["log_config_file"])
|
|
if not os.path.exists(log_ini):
|
|
raise IOError("Could not find log config file %s" % log_ini)
|
|
else:
|
|
raise IOError("Could not find log config file %s" % log_ini)
|
|
except IOError as exc:
|
|
log.error(exc)
|
|
sys.exit(50)
|
|
|
|
try:
|
|
log_cfg = configparser.ConfigParser()
|
|
logging.config.fileConfig(log_ini)
|
|
log_cfg.read(log_ini)
|
|
except (IOError, OSError, configparser.NoSectionError) as exc:
|
|
log.error("Log config file(%s) not correctly configured: %s", log_ini, exc)
|
|
sys.exit(50)
|
|
|
|
try:
|
|
# set up logging format strings
|
|
config_opts['build_log_fmt_str'] = log_cfg.get("formatter_%s" % config_opts['build_log_fmt_name'],
|
|
"format", raw=1)
|
|
config_opts['root_log_fmt_str'] = log_cfg.get("formatter_%s" % config_opts['root_log_fmt_name'],
|
|
"format", raw=1)
|
|
config_opts['state_log_fmt_str'] = log_cfg.get("formatter_%s" % config_opts['state_log_fmt_name'],
|
|
"format", raw=1)
|
|
except configparser.NoSectionError as exc:
|
|
log.error("Log config file (%s) missing required section: %s", log_ini, exc)
|
|
sys.exit(50)
|
|
|
|
# set logging verbosity
|
|
if options.verbose == 0:
|
|
log.handlers[0].setLevel(logging.WARNING)
|
|
tmplog = logging.getLogger("mockbuild.Root.state")
|
|
if tmplog.handlers:
|
|
tmplog.handlers[0].setLevel(logging.WARNING)
|
|
elif options.verbose == 1:
|
|
log.handlers[0].setLevel(logging.INFO)
|
|
elif options.verbose == 2:
|
|
log.handlers[0].setLevel(logging.DEBUG)
|
|
logging.getLogger("mockbuild.Root.build").propagate = 1
|
|
logging.getLogger("mockbuild").propagate = 1
|
|
|
|
# enable tracing if requested
|
|
logging.getLogger("trace").propagate = 0
|
|
if options.trace:
|
|
logging.getLogger("trace").propagate = 1
|
|
|
|
logging.getLogger("mockbuild").mock_stderr_line_prefix = ""
|
|
if config_opts['stderr_line_prefix'] != "":
|
|
logging.getLogger("mockbuild").mock_stderr_line_prefix = config_opts['stderr_line_prefix']
|
|
|
|
|
|
@traceLog()
|
|
def check_arch_combination(target_arch, config_opts):
|
|
try:
|
|
legal = config_opts['legal_host_arches']
|
|
except KeyError:
|
|
return
|
|
host_arch = config_opts['host_arch']
|
|
if (host_arch not in legal) and not config_opts['forcearch']:
|
|
log.info("Unable to build arch %s natively on arch %s. Setting forcearch to use software emulation.",
|
|
target_arch, host_arch)
|
|
config_opts['forcearch'] = target_arch
|
|
|
|
config.multiply_platform_multiplier(config_opts)
|
|
|
|
if not config_opts['forcearch']:
|
|
return
|
|
|
|
# Check below that we can do cross-architecture builds.
|
|
|
|
option = f"--forcearch={config_opts['forcearch']}"
|
|
binary_pattern = config_opts["qemu_user_static_mapping"].get(config_opts["forcearch"])
|
|
if not binary_pattern:
|
|
# Probably a missing configuration.
|
|
log.warning(
|
|
"Mock will likely fail, %s is enabled "
|
|
"while Mock is unable to detect the corresponding "
|
|
"/usr/bin/qemu-*-static binary",
|
|
option,
|
|
)
|
|
time.sleep(5)
|
|
return
|
|
|
|
binary = f'/usr/bin/qemu-{binary_pattern}-static'
|
|
if os.path.exists(binary):
|
|
return
|
|
|
|
# qemu-user-static is required, but seems to be missing
|
|
if not util.is_host_rh_family():
|
|
# On non-RH systems we are not sure where the qemu-<ARCH>-static
|
|
# binaries reside - therefore we can't even check whether they are
|
|
# installed. Therefore we don't raise an exception; we at least
|
|
# notify the user verbosely, cross our fingers, and continue.
|
|
log.warning("Mock with %s will likely fail with missing %s",
|
|
option, binary)
|
|
time.sleep(5)
|
|
return
|
|
|
|
raise mockbuild.exception.InvalidArchitecture(
|
|
f'The {option} feature requires the {binary} '
|
|
'file to be installed (typically qemu-user-static* package)')
|
|
|
|
|
|
@traceLog()
|
|
def do_debugconfig(config_opts, expand=False):
|
|
jinja_expand = config_opts['__jinja_expand']
|
|
defaults = config.setup_default_config_opts()
|
|
defaults['__jinja_expand'] = expand
|
|
config_opts['__jinja_expand'] = expand
|
|
for key in sorted(config_opts):
|
|
if key == '__jinja_expand':
|
|
value = jinja_expand
|
|
else:
|
|
value = config_opts[key]
|
|
if (key in defaults) and (key in config_opts) and (config_opts[key] != defaults[key]) or \
|
|
(key not in defaults):
|
|
print("config_opts['{}'] = {}".format(key, pformat(value)))
|
|
config_opts['__jinja_expand'] = jinja_expand
|
|
|
|
|
|
@traceLog()
|
|
def do_listchroots(config_path, uidManager):
|
|
uidManager.run_in_subprocess_without_privileges(
|
|
config.list_configs, config_path,
|
|
)
|
|
|
|
|
|
@traceLog()
|
|
def rootcheck():
|
|
"verify mock was started correctly (either by sudo or consolehelper)"
|
|
# if we're root due to sudo or consolehelper, we're ok
|
|
# if not raise an exception and bail
|
|
if os.getuid() == 0 and not (os.environ.get("SUDO_UID") or os.environ.get("USERHELPER_UID")):
|
|
raise RuntimeError("mock will not run from the root account (needs an unprivileged uid so it can drop privs)")
|
|
|
|
|
|
@traceLog()
|
|
def groupcheck(unprivGid, tgtGid):
|
|
"verify that the user running mock is part of the correct group"
|
|
# verify that we're in the correct group (so all our uid/gid manipulations work)
|
|
inmockgrp = False
|
|
members = []
|
|
for gid in os.getgroups() + [unprivGid]:
|
|
name = grp.getgrgid(gid).gr_name
|
|
if gid == tgtGid:
|
|
inmockgrp = True
|
|
break
|
|
members.append(name)
|
|
if not inmockgrp:
|
|
name = grp.getgrgid(tgtGid).gr_name
|
|
raise RuntimeError("Must be member of '%s' group to run mock! (%s)" %
|
|
(name, ", ".join(members)))
|
|
|
|
|
|
@traceLog()
|
|
def unshare_namespace(config_opts):
|
|
base_unshare_flags = util.CLONE_NEWNS
|
|
# IPC ns is unshared later
|
|
extended_unshare_flags = base_unshare_flags | util.CLONE_NEWUTS
|
|
try:
|
|
util.unshare(extended_unshare_flags)
|
|
except mockbuild.exception.UnshareFailed as e:
|
|
log.debug("unshare(%d) failed, falling back to unshare(%d)",
|
|
extended_unshare_flags, base_unshare_flags)
|
|
try:
|
|
util.unshare(base_unshare_flags)
|
|
except mockbuild.exception.UnshareFailed as e2:
|
|
log.error("Namespace unshare failed.")
|
|
if util.mock_host_environment_type() == "docker" \
|
|
and not ('docker_unshare_warning' in config_opts
|
|
and config_opts['docker_unshare_warning']):
|
|
log.error("It seems we are running inside of Docker. Let skip unsharing.")
|
|
log.error("You should *not* run anything but Mock in this container. You have been warned!")
|
|
time.sleep(5)
|
|
else:
|
|
sys.exit(e2.resultcode)
|
|
|
|
|
|
@traceLog()
|
|
def main():
|
|
"Main executable entry point."
|
|
|
|
# initial sanity check for correct invocation method
|
|
rootcheck()
|
|
|
|
# drop unprivileged to parse args, etc.
|
|
# uidManager saves current real uid/gid which are unprivileged (callers)
|
|
# due to suid helper, our current effective uid is 0
|
|
# also supports being run by sudo
|
|
#
|
|
# setuid wrapper has real uid = unpriv, effective uid = 0
|
|
# sudo sets real/effective = 0, and sets env vars
|
|
# setuid wrapper clears environment, so there wont be any conflict between these two
|
|
uidManager = mockbuild.uid.setup_uid_manager()
|
|
|
|
# go unpriv only when root to make --help etc work for non-mock users
|
|
if os.geteuid() == 0:
|
|
uidManager.dropPrivsTemp()
|
|
|
|
(options, args) = command_parse()
|
|
if options.printrootpath or options.list_snapshots:
|
|
options.verbose = 0
|
|
|
|
if options.mode == "scrub-all-chroots":
|
|
return scrub_all_chroots()
|
|
|
|
# config path -- can be overridden on cmdline
|
|
config_path = MOCKCONFDIR
|
|
if options.configdir:
|
|
config_path = options.configdir
|
|
|
|
if options.hermetic_build:
|
|
options.chroot = "hermetic-build"
|
|
|
|
config_opts = uidManager.run_in_subprocess_without_privileges(
|
|
config.load_config, config_path, options.chroot)
|
|
|
|
# cmdline options override config options
|
|
config.set_config_opts_per_cmdline(config_opts, options, args)
|
|
|
|
sys.setrecursionlimit(config_opts["recursion_limit"])
|
|
|
|
util.subscription_redhat_init(config_opts, uidManager)
|
|
|
|
# allow a different mock group to be specified
|
|
uidManager.fix_different_chrootgid(config_opts)
|
|
|
|
# verify that our unprivileged uid is in the mock group
|
|
groupcheck(uidManager.unprivGid, config_opts['chrootgid'])
|
|
|
|
# configure logging
|
|
setup_logging(config_path, config_opts, options)
|
|
|
|
# verify that we're not trying to build an arch that we can't
|
|
check_arch_combination(config_opts['rpmbuild_arch'], config_opts)
|
|
|
|
# security cleanup (don't need/want this in the chroot)
|
|
if 'SSH_AUTH_SOCK' in os.environ:
|
|
del os.environ['SSH_AUTH_SOCK']
|
|
|
|
# elevate privs
|
|
uidManager.become_user_without_push(0, 0)
|
|
|
|
# do whatever we're here to do
|
|
py_version = '{0}.{1}.{2}'.format(*sys.version_info[:3])
|
|
log.info("mock.py version %s starting (python version = %s%s), args: %s",
|
|
VERSION, py_version,
|
|
"" if not _MOCK_NVR else ", NVR = " + _MOCK_NVR,
|
|
" ".join(shlex.quote(x) for x in sys.argv))
|
|
state = State()
|
|
plugins = Plugins(config_opts, state)
|
|
|
|
# When scrubbing all, we also want to scrub a bootstrap chroot
|
|
if options.scrub:
|
|
config_opts['use_bootstrap'] = True
|
|
|
|
# outer buildroot to bootstrap the installation - based on main config with some differences
|
|
bootstrap_buildroot = None
|
|
if config_opts['use_bootstrap']:
|
|
# first take a copy of the config so we can make some modifications
|
|
bootstrap_buildroot_config = config_opts.copy()
|
|
# copy plugins configuration so we get a separate deep copy
|
|
bootstrap_buildroot_config['plugin_conf'] = \
|
|
copy.deepcopy(config_opts['plugin_conf']) # pylint: disable=no-member
|
|
# add '-bootstrap' to the end of the root name
|
|
bootstrap_buildroot_config['root'] = bootstrap_buildroot_config['root'] + '-bootstrap'
|
|
# don't share root cache tarball
|
|
bootstrap_buildroot_config['plugin_conf']['root_cache_opts']['dir'] = \
|
|
"{{cache_topdir}}/" + bootstrap_buildroot_config['root'] + "/root_cache/"
|
|
# we don't want to affect the bootstrap.config['nspawn_args'] array, deep copy
|
|
bootstrap_buildroot_config['nspawn_args'] = config_opts.get('nspawn_args', []).copy()
|
|
|
|
# allow bootstrap buildroot to access the network for getting packages
|
|
bootstrap_buildroot_config['rpmbuild_networking'] = True
|
|
bootstrap_buildroot_config['use_host_resolv'] = True
|
|
util.setup_host_resolv(bootstrap_buildroot_config)
|
|
|
|
# disable updating bootstrap chroot
|
|
bootstrap_buildroot_config['update_before_build'] = False
|
|
|
|
# Enforce host-native repo architecture for bootstrap chroot (unless
|
|
# bootstrap_forcearch=True, which should never be the case). This
|
|
# decision affects condPersonality() for DNF calls!
|
|
host_arch = config_opts["host_arch"]
|
|
if config_opts["use_bootstrap_image"]:
|
|
# with bootstrap image, bootstrap is always native
|
|
bootstrap_buildroot_config['repo_arch'] = config_opts['repo_arch_map'].get(host_arch, host_arch)
|
|
elif host_arch not in config_opts.get("legal_host_arches", []) \
|
|
and not config_opts.get('bootstrap_forcearch'):
|
|
# target chroot uses --forcearch, but bootstrap is native
|
|
bootstrap_buildroot_config['repo_arch'] = config_opts['repo_arch_map'].get(host_arch, host_arch)
|
|
# else:
|
|
# We keep the 'repo_arch' copied from target chroot (config_opts['repo_arch']):
|
|
# - for the 'multilib' cases (we don't want to use x86_64 repos for i586 chroots)
|
|
# - 'bootstrap_forcearch' is set
|
|
|
|
# disable forcearch in bootstrap, per https://github.com/rpm-software-management/mock/issues/1110
|
|
bootstrap_buildroot_config['forcearch'] = None
|
|
|
|
bootstrap_buildroot_state = State(bootstrap=True)
|
|
bootstrap_plugins = Plugins(bootstrap_buildroot_config, bootstrap_buildroot_state)
|
|
bootstrap_buildroot = Buildroot(bootstrap_buildroot_config,
|
|
uidManager, bootstrap_buildroot_state, bootstrap_plugins,
|
|
is_bootstrap=True)
|
|
|
|
# override configs for bootstrap_*
|
|
for option in list(bootstrap_buildroot.config.keys()):
|
|
# options that are not related to bootstrap chroot config_opts
|
|
dont_copy = ["bootstrap_image"]
|
|
prefix = "bootstrap_"
|
|
if option.startswith(prefix) and option not in dont_copy:
|
|
bootstrap_option = option[len(prefix):]
|
|
bootstrap_buildroot.config[bootstrap_option] = bootstrap_buildroot.config[option]
|
|
del bootstrap_buildroot.config[option]
|
|
|
|
if config_opts['redhat_subscription_required']:
|
|
key_dir = '/etc/pki/entitlement'
|
|
chroot_dir = bootstrap_buildroot.make_chroot_path(key_dir)
|
|
mount_point = BindMountPoint(srcpath=key_dir, bindpath=chroot_dir)
|
|
bootstrap_buildroot.mounts.add(mount_point)
|
|
|
|
# this changes config_opts['nspawn_args'], so do it after initializing
|
|
# bootstrap chroot to not inherit the changes there
|
|
util.setup_host_resolv(config_opts)
|
|
|
|
buildroot = Buildroot(config_opts, uidManager, state, plugins, bootstrap_buildroot)
|
|
|
|
for baseurl in options.repos:
|
|
util.add_local_repo(config_opts, baseurl,
|
|
bootstrap=bootstrap_buildroot)
|
|
|
|
if bootstrap_buildroot is not None:
|
|
# add the extra bind mount to the outer chroot
|
|
inner_mount = bootstrap_buildroot.make_chroot_path(buildroot.make_chroot_path())
|
|
|
|
# Hide re-mounted chroot from host by private tmpfs.
|
|
buildroot.mounts.bootstrap_mounts.append(
|
|
FileSystemMountPoint(filetype='tmpfs',
|
|
device='hide_root_in_bootstrap',
|
|
path=inner_mount,
|
|
options="private,mode=0755"))
|
|
buildroot.mounts.bootstrap_mounts.append(
|
|
BindMountPoint(buildroot.make_chroot_path(), inner_mount,
|
|
recursive=True, options="private").treat_as_chroot())
|
|
|
|
signal.signal(signal.SIGTERM, partial(handle_signals, buildroot))
|
|
signal.signal(signal.SIGPIPE, partial(handle_signals, buildroot))
|
|
signal.signal(signal.SIGHUP, partial(handle_signals, buildroot))
|
|
|
|
# postprocess option arguments for bootstrap
|
|
if options.mode in ['installdeps', 'install']:
|
|
args = [buildroot.wrap_host_file(arg) for arg in args]
|
|
|
|
log.info("Signal handler active")
|
|
commands = Commands(config_opts, uidManager, plugins, state, buildroot, bootstrap_buildroot)
|
|
|
|
# TODO: The printrootpath and list_snapshots logic escape the finalization
|
|
# like 'bootstrap.finalize()' or 'state.alldone()'. Move it into
|
|
# run_command().
|
|
state.start("run")
|
|
|
|
if options.printrootpath:
|
|
print(buildroot.make_chroot_path(''))
|
|
sys.exit(0)
|
|
|
|
if options.list_snapshots:
|
|
plugins.call_hooks('list_snapshots', required=True)
|
|
if bootstrap_buildroot is not None:
|
|
bootstrap_buildroot.plugins.call_hooks('list_snapshots', required=True)
|
|
sys.exit(0)
|
|
|
|
# dump configuration to log
|
|
log.debug("mock final configuration:")
|
|
for k, v in list(config_opts.items()):
|
|
log.debug(" %s: %s", k, v)
|
|
|
|
os.umask(0o02)
|
|
os.environ["HOME"] = buildroot.homedir
|
|
|
|
# New namespace starting from here
|
|
unshare_namespace(config_opts)
|
|
|
|
if config_opts['hostname']:
|
|
util.sethostname(config_opts['hostname'])
|
|
|
|
# set personality (ie. setarch)
|
|
util.condPersonality(config_opts['target_arch'])
|
|
|
|
result = 0
|
|
try:
|
|
result = run_command(options, args, config_opts, commands, buildroot)
|
|
# finish state.log if no exception was raised in run_command()
|
|
state.finish("run")
|
|
state.alldone()
|
|
finally:
|
|
buildroot.finalize()
|
|
if bootstrap_buildroot is not None:
|
|
bootstrap_buildroot.finalize()
|
|
return result
|
|
|
|
|
|
@traceLog()
|
|
def run_command(options, args, config_opts, commands, buildroot):
|
|
result = 0
|
|
# TODO separate this
|
|
# Fetch and prepare sources from SCM
|
|
if config_opts['scm']:
|
|
try:
|
|
# pylint: disable=import-outside-toplevel
|
|
from mockbuild import scm
|
|
except ImportError as e:
|
|
raise mockbuild.exception.BadCmdline(
|
|
"Mock SCM module not installed: %s. You should install package mock-scm." % e)
|
|
scmWorker = scm.scmWorker(log, config_opts, config_opts['macros'])
|
|
with buildroot.uid_manager:
|
|
scmWorker.get_sources()
|
|
(options.sources, options.spec) = scmWorker.prepare_sources()
|
|
|
|
if options.mode == 'init':
|
|
if config_opts['clean']:
|
|
commands.clean()
|
|
commands.init()
|
|
|
|
elif options.mode == 'clean':
|
|
if len(options.scrub) == 0:
|
|
commands.clean()
|
|
else:
|
|
commands.scrub(options.scrub)
|
|
|
|
elif options.mode == 'chain':
|
|
if len(args) == 0:
|
|
log.critical("You must specify an SRPM file with --chain")
|
|
return 50
|
|
commands.init(do_log=True)
|
|
result = commands.chain(args, options, buildroot)
|
|
|
|
elif options.mode == 'shell':
|
|
if len(args):
|
|
cmd = args
|
|
else:
|
|
cmd = None
|
|
commands.init(do_log=False)
|
|
return commands.shell(options, cmd)
|
|
|
|
elif options.mode == 'chroot':
|
|
if len(args) == 0:
|
|
log.critical("You must specify a command to run with --chroot")
|
|
return 50
|
|
commands.init(do_log=True)
|
|
return commands.chroot(args, options)
|
|
|
|
elif options.mode == 'installdeps':
|
|
if len(args) == 0:
|
|
log.critical("You must specify an SRPM file with --installdeps")
|
|
return 50
|
|
commands.init()
|
|
rpms = []
|
|
for file in args:
|
|
if os.path.splitext(file)[1] == ".spec":
|
|
commands.installSpecDeps(file)
|
|
else:
|
|
rpms.append(file)
|
|
if rpms:
|
|
util.checkSrpmHeaders(rpms, plainRpmOk=1)
|
|
commands.installSrpmDeps(*rpms)
|
|
|
|
elif options.mode == 'install':
|
|
if len(args) == 0:
|
|
log.critical("You must specify a package list to install.")
|
|
return 50
|
|
|
|
commands.init()
|
|
commands.install_external(args)
|
|
buildroot.install(*args)
|
|
|
|
elif options.mode == 'update':
|
|
commands.init()
|
|
buildroot.pkg_manager.execute('update', *args)
|
|
|
|
elif options.mode == 'remove':
|
|
if len(args) == 0:
|
|
log.critical("You must specify a package list to remove.")
|
|
return 50
|
|
commands.init()
|
|
buildroot.remove(*args)
|
|
|
|
elif options.mode == 'rebuild':
|
|
if options.hermetic_build:
|
|
# No caches with hermetic builds! Bootstrap is extracted from
|
|
# given tarball, buildroot installed from pre-fetched RPMs.
|
|
commands.scrub(["all"])
|
|
|
|
if config_opts['scm'] or (options.spec and options.sources):
|
|
srpm = mockbuild.rebuild.do_buildsrpm(config_opts, commands, buildroot, options, args)
|
|
if srpm:
|
|
args.append(srpm)
|
|
if config_opts['scm']:
|
|
scmWorker.clean()
|
|
options.spec = None
|
|
options.sources = None
|
|
else:
|
|
config_opts['clean'] = False
|
|
|
|
try:
|
|
srpms = []
|
|
for srpm_location in args:
|
|
with buildroot.uid_manager:
|
|
srpm = FileDownloader.get(srpm_location)
|
|
if not srpm:
|
|
raise mockbuild.exception.BadCmdline(
|
|
"Invalid {} source RPM".format(srpm_location))
|
|
srpms.append(srpm)
|
|
mockbuild.rebuild.do_rebuild(config_opts, commands, buildroot, options, srpms)
|
|
finally:
|
|
FileDownloader.cleanup()
|
|
|
|
elif options.mode == 'buildsrpm':
|
|
mockbuild.rebuild.do_buildsrpm(config_opts, commands, buildroot, options, args)
|
|
|
|
elif options.mode == 'debugconfig':
|
|
do_debugconfig(config_opts)
|
|
|
|
elif options.mode == 'debugconfigexpand':
|
|
do_debugconfig(config_opts, True)
|
|
|
|
elif options.mode == 'listchroots':
|
|
do_listchroots(config_opts["config_path"], buildroot.uid_manager)
|
|
|
|
elif options.mode == 'orphanskill':
|
|
util.orphansKill(buildroot.make_chroot_path())
|
|
|
|
elif options.mode == 'copyin':
|
|
commands.init()
|
|
if len(args) < 2:
|
|
log.critical("Must have source and destinations for copyin")
|
|
return 50
|
|
dest = buildroot.make_chroot_path(args[-1])
|
|
if len(args) > 2 and not os.path.isdir(dest):
|
|
log.critical("multiple source files and %s is not a directory!", dest)
|
|
return 50
|
|
args = args[:-1]
|
|
for src in args:
|
|
if not os.path.lexists(src):
|
|
log.critical("No such file or directory: %s", src)
|
|
return 50
|
|
log.info("copying %s to %s", src, dest)
|
|
if os.path.isdir(src):
|
|
dest2 = dest
|
|
if os.path.exists(dest2):
|
|
path_suffix = os.path.split(src)[1]
|
|
dest2 = os.path.join(dest2, path_suffix)
|
|
if os.path.exists(dest2):
|
|
log.critical("Destination %s already exists!", dest2)
|
|
return 50
|
|
shutil.copytree(src, dest2)
|
|
else:
|
|
shutil.copy(src, dest)
|
|
buildroot.chown_home_dir()
|
|
|
|
elif options.mode == 'copyout':
|
|
commands.init()
|
|
with buildroot.uid_manager:
|
|
if len(args) < 2:
|
|
log.critical("Must have source and destinations for copyout")
|
|
return 50
|
|
dest = args[-1]
|
|
sources = []
|
|
for arg in args[:-1]:
|
|
with util.env_var_override("HOME", buildroot.homedir):
|
|
arg = os.path.expanduser(arg)
|
|
matches = glob.glob(buildroot.make_chroot_path(arg))
|
|
if not matches:
|
|
log.critical("%s not found", arg)
|
|
return 50
|
|
sources += matches
|
|
if len(sources) > 1 and not os.path.isdir(dest):
|
|
log.critical("multiple source files and %s is not a directory!", dest)
|
|
return 50
|
|
for src in sources:
|
|
log.info("copying %s to %s", src, dest)
|
|
if os.path.isdir(src):
|
|
shutil.copytree(src, dest, symlinks=True)
|
|
else:
|
|
if os.path.islink(src):
|
|
linkto = os.readlink(src)
|
|
os.symlink(linkto, dest)
|
|
else:
|
|
shutil.copy(src, dest)
|
|
|
|
elif options.mode in ('pm-cmd', 'yum-cmd', 'dnf-cmd'):
|
|
log.info('Running %s %s', options.mode, ' '.join(args))
|
|
commands.init()
|
|
buildroot.pkg_manager.execute(*args)
|
|
elif options.mode == 'snapshot':
|
|
if len(args) < 1:
|
|
log.critical("Requires a snapshot name")
|
|
return 50
|
|
buildroot.plugins.call_hooks('make_snapshot', args[0], required=True)
|
|
if buildroot.bootstrap_buildroot is not None:
|
|
buildroot.bootstrap_buildroot.plugins.call_hooks('make_snapshot', args[0], required=True)
|
|
elif options.mode == 'rollback-to':
|
|
if len(args) < 1:
|
|
log.critical("Requires a snapshot name")
|
|
return 50
|
|
buildroot.plugins.call_hooks('rollback_to', args[0], required=True)
|
|
if buildroot.bootstrap_buildroot is not None:
|
|
buildroot.bootstrap_buildroot.plugins.call_hooks('rollback_to', args[0], required=True)
|
|
elif options.mode == 'remove_snapshot':
|
|
if len(args) < 1:
|
|
log.critical("Requires a snapshot name")
|
|
return 50
|
|
buildroot.plugins.call_hooks('remove_snapshot', args[0], required=True)
|
|
if buildroot.bootstrap_buildroot is not None:
|
|
buildroot.bootstrap_buildroot.plugins.call_hooks('remove_snapshot', args[0], required=True)
|
|
elif options.mode == 'umount':
|
|
buildroot.plugins.call_hooks('umount_root')
|
|
if buildroot.bootstrap_buildroot is not None:
|
|
buildroot.bootstrap_buildroot.plugins.call_hooks('umount_root')
|
|
elif options.mode == 'mount':
|
|
buildroot.plugins.call_hooks('mount_root')
|
|
if buildroot.bootstrap_buildroot is not None:
|
|
buildroot.bootstrap_buildroot.plugins.call_hooks('mount_root')
|
|
|
|
buildroot.nuke_rpm_db()
|
|
return result
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# TODO: this was documented as "fix for python 2.4 logging module bug:"
|
|
# TODO: ...but it is apparently still needed; without it there are various
|
|
# TODO: exceptions from trace_decorator like:
|
|
# TODO: TypeError: not enough arguments for format string
|
|
logging.raiseExceptions = 0
|
|
|
|
exitStatus = 0
|
|
|
|
|
|
try:
|
|
exitStatus = main()
|
|
|
|
except (SystemExit,):
|
|
raise # pylint: disable=try-except-raise
|
|
|
|
except (OSError,) as e:
|
|
if e.errno == errno.EPERM:
|
|
print()
|
|
log.error("%s", e)
|
|
print()
|
|
log.error("The most common cause for this error is trying to run "
|
|
"/usr/libexec/mock/mock as an unprivileged user.")
|
|
log.error("You should not run /usr/libexec/mock/mock directly.")
|
|
print()
|
|
exitStatus = 2
|
|
else:
|
|
raise
|
|
|
|
except (KeyboardInterrupt,):
|
|
exitStatus = 7
|
|
log.error("Exiting on user interrupt, <CTRL>-C")
|
|
|
|
except (mockbuild.exception.ResultDirNotAccessible,) as exc:
|
|
exitStatus = exc.resultcode
|
|
log.error(str(exc))
|
|
|
|
except (mockbuild.exception.BadCmdline, mockbuild.exception.BuildRootLocked) as exc:
|
|
exitStatus = exc.resultcode
|
|
log.error(str(exc))
|
|
|
|
except (mockbuild.exception.Error) as exc:
|
|
exitStatus = exc.resultcode
|
|
log.error(str(exc))
|
|
|
|
except (Exception,) as exc: # pylint: disable=broad-except
|
|
exitStatus = 1
|
|
log.exception(exc)
|
|
|
|
logging.shutdown()
|
|
sys.exit(exitStatus)
|