366 lines
12 KiB
Python
Executable File
366 lines
12 KiB
Python
Executable File
#!/usr/bin/python
|
|
#
|
|
# makeupdates - Generate an updates.img containing changes since the last
|
|
# tag, but only changes to the main anaconda runtime.
|
|
# initrd/stage1 updates have to be created separately.
|
|
#
|
|
# Copyright (C) 2009 Red Hat, Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as published
|
|
# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
# Author: David Cantrell <dcantrell@redhat.com>
|
|
|
|
import getopt
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
def getArchiveTag(configure, spec):
|
|
tag = ""
|
|
|
|
f = open(configure)
|
|
lines = f.readlines()
|
|
f.close()
|
|
|
|
for line in lines:
|
|
if line.startswith('AC_INIT('):
|
|
fields = line.split('[')
|
|
tag += fields[1].split(']')[0] + '-' + fields[2].split(']')[0]
|
|
break
|
|
else:
|
|
continue
|
|
|
|
f = open(spec)
|
|
lines = f.readlines()
|
|
f.close()
|
|
|
|
for line in lines:
|
|
if line.startswith('Release:'):
|
|
tag += '-' + line.split()[1].split('%')[0]
|
|
else:
|
|
continue
|
|
|
|
return tag
|
|
|
|
def getArchiveTagOffset(configure, spec, offset):
|
|
tag = getArchiveTag(configure, spec)
|
|
|
|
if not tag.count("-") >= 2:
|
|
return tag
|
|
ldash = tag.rfind("-")
|
|
bldash = tag[:ldash].rfind("-")
|
|
ver = tag[bldash+1:ldash]
|
|
|
|
if not ver.count(".") >= 1:
|
|
return tag
|
|
ver = ver[:ver.rfind(".")]
|
|
|
|
if not len(ver) > 0:
|
|
return tag
|
|
globstr = "refs/tags/" + tag[:bldash+1] + ver + ".*"
|
|
proc = subprocess.Popen(['git', 'for-each-ref', '--sort=taggerdate',
|
|
'--format=%(tag)', globstr],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE).communicate()
|
|
lines = proc[0].strip("\n").split('\n')
|
|
lines.reverse()
|
|
|
|
try:
|
|
return lines[offset]
|
|
except IndexError:
|
|
return tag
|
|
|
|
def doGitDiff(tag, args=[]):
|
|
proc = subprocess.Popen(['git', 'diff', '--name-status', tag] + args,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE).communicate()
|
|
|
|
lines = proc[0].split('\n')
|
|
return lines
|
|
|
|
def copyUpdatedFiles(tag, updates, cwd):
|
|
def pruneFile(src, names):
|
|
lst = []
|
|
|
|
for name in names:
|
|
if name.startswith('Makefile') or name.endswith('.pyc'):
|
|
lst.append(name)
|
|
|
|
return lst
|
|
|
|
def install_to_dir(fname, relpath):
|
|
sys.stdout.write("Including %s\n" % fname)
|
|
outdir = os.path.join(updates, relpath)
|
|
if not os.path.isdir(outdir):
|
|
os.makedirs(outdir)
|
|
shutil.copy2(file, outdir)
|
|
|
|
subdirs = []
|
|
|
|
# Updates get overlaid onto the runtime filesystem. Anaconda expects them
|
|
# to be in /run/install/updates, so put them in
|
|
# $updatedir/run/install/updates.
|
|
tmpupdates = updates.rstrip('/')
|
|
if not tmpupdates.endswith("/run/install/updates"):
|
|
tmpupdates = os.path.join(tmpupdates, "run/install/updates")
|
|
|
|
lines = doGitDiff(tag)
|
|
for line in lines:
|
|
fields = line.split()
|
|
|
|
if len(fields) < 2:
|
|
continue
|
|
|
|
status = fields[0]
|
|
file = fields[1]
|
|
|
|
if status == "D":
|
|
continue
|
|
|
|
if file.endswith('.spec.in') or (file.find('Makefile') != -1) or \
|
|
file.endswith('.c') or file.endswith('.h') or \
|
|
file.endswith('.sh') or file == 'configure.ac':
|
|
continue
|
|
|
|
if file.endswith('.glade'):
|
|
# Some UI files should go under ui/<dir> where dir is the
|
|
# directory above the file.glade
|
|
dir_parts = os.path.dirname(file).split(os.path.sep)
|
|
g_idx = dir_parts.index("gui")
|
|
uidir = os.path.sep.join(dir_parts[g_idx+1:])
|
|
path_comps = [tmpupdates, "ui"]
|
|
if uidir:
|
|
path_comps.append(uidir)
|
|
install_to_dir(file, os.path.join(*path_comps))
|
|
elif file.startswith('pyanaconda/'):
|
|
# pyanaconda stuff goes into /tmp/updates/[path]
|
|
dirname = os.path.join(tmpupdates, os.path.dirname(file))
|
|
install_to_dir(file, dirname)
|
|
elif file == 'anaconda':
|
|
# anaconda itself we just overwrite
|
|
install_to_dir(file, "usr/sbin")
|
|
elif file.endswith('.service') or file.endswith(".target"):
|
|
# same for systemd services
|
|
install_to_dir(file, "lib/systemd/system")
|
|
elif file.endswith('/anaconda-generator'):
|
|
# yeah, this should probably be more clever..
|
|
install_to_dir(file, "lib/systemd/system-generators")
|
|
elif file == "data/tmux.conf":
|
|
install_to_dir(file, "usr/share/anaconda")
|
|
elif file == "data/interactive-defaults.ks":
|
|
install_to_dir(file, "usr/share/anaconda")
|
|
elif file == "data/liveinst/liveinst":
|
|
install_to_dir(file, "usr/sbin")
|
|
elif file == "data/70-anaconda.rules":
|
|
install_to_dir(file, "lib/udev/rules.d")
|
|
elif file.startswith("data/pixmaps"):
|
|
install_to_dir(file, "usr/share/anaconda/pixmaps")
|
|
elif file.startswith("data/ui/"):
|
|
install_to_dir(file, "usr/share/anaconda/ui")
|
|
elif file.startswith("data/post-scripts/"):
|
|
install_to_dir(file, "usr/share/anaconda/post-scripts")
|
|
elif file.find('/') != -1:
|
|
fields = file.split('/')
|
|
subdir = fields[0]
|
|
if subdir in ['po', 'scripts','command-stubs', 'tests',
|
|
'docs', 'fonts', 'utils',
|
|
'liveinst', 'dracut', 'data']:
|
|
continue
|
|
else:
|
|
sys.stdout.write("Including %s\n" % (file,))
|
|
install_to_dir(file, tmpupdates)
|
|
else:
|
|
sys.stdout.write("Including %s\n" % (file,))
|
|
install_to_dir(file, tmpupdates)
|
|
|
|
def _compilableChanged(tag, compilable):
|
|
lines = doGitDiff(tag, [compilable])
|
|
|
|
for line in lines:
|
|
fields = line.split()
|
|
|
|
if len(fields) < 2:
|
|
continue
|
|
|
|
status = fields[0]
|
|
file = fields[1]
|
|
|
|
if status == "D":
|
|
continue
|
|
|
|
if file.startswith('Makefile') or file.endswith('.h') or \
|
|
file.endswith('.c'):
|
|
return True
|
|
|
|
return False
|
|
|
|
def isysChanged(tag):
|
|
return _compilableChanged(tag, 'pyanaconda/isys')
|
|
|
|
def widgetsChanged(tag):
|
|
return _compilableChanged(tag, 'widgets')
|
|
|
|
def copyUpdatedIsys(updates, cwd):
|
|
os.chdir(cwd)
|
|
|
|
if not os.path.isfile('Makefile'):
|
|
if not os.path.isfile('configure'):
|
|
os.system('./autogen.sh')
|
|
os.system('./configure')
|
|
|
|
os.system('make')
|
|
|
|
# Updates get overlaid onto the runtime filesystem. Anaconda expects them
|
|
# to be in /run/install/updates, so put them in
|
|
# $updatedir/run/install/updates.
|
|
tmpupdates = updates.rstrip('/')
|
|
if not tmpupdates.endswith("/run/install/updates/pyanaconda"):
|
|
tmpupdates = os.path.join(tmpupdates, "run/install/updates/pyanaconda")
|
|
|
|
isysmodule = os.path.realpath(cwd + '/pyanaconda/isys/.libs/_isys.so')
|
|
if os.path.isfile(isysmodule):
|
|
shutil.copy2(isysmodule, tmpupdates)
|
|
|
|
def copyUpdatedWidgets(updates, cwd):
|
|
os.chdir(cwd)
|
|
|
|
if os.path.isdir("/lib64"):
|
|
libdir = "/lib64/"
|
|
else:
|
|
libdir = "/lib/"
|
|
|
|
if not os.path.isdir(updates + libdir):
|
|
os.makedirs(updates + libdir)
|
|
|
|
if not os.path.isdir(updates + libdir + "girepository-1.0"):
|
|
os.makedirs(updates + libdir + "girepository-1.0")
|
|
|
|
if not os.path.isfile('Makefile'):
|
|
if not os.path.isfile('configure'):
|
|
os.system('./autogen.sh')
|
|
os.system('./configure --enable-gtk-doc --enable-introspection')
|
|
|
|
os.system('make')
|
|
|
|
files = ["libAnacondaWidgets.so", "libAnacondaWidgets.so.0", "libAnacondaWidgets.so.0.0.0"]
|
|
for f in files:
|
|
path = os.path.normpath(cwd + "/widgets/src/.libs/" + f)
|
|
if os.path.islink(path) and not os.path.exists(updates + libdir + os.path.basename(path)):
|
|
os.symlink(os.readlink(path), updates + libdir + os.path.basename(path))
|
|
elif os.path.isfile(path):
|
|
shutil.copy2(path, updates + libdir)
|
|
|
|
typelib = os.path.realpath(cwd + "/widgets/src/AnacondaWidgets-1.0.typelib")
|
|
if os.path.isfile(typelib):
|
|
shutil.copy2(typelib, updates + libdir + "girepository-1.0")
|
|
|
|
def addRpms(updates, add_rpms):
|
|
for rpm in add_rpms:
|
|
cmd = "cd %s && rpm2cpio %s | cpio -dium" % (updates, rpm)
|
|
sys.stdout.write(cmd+"\n")
|
|
os.system(cmd)
|
|
|
|
def createUpdatesImage(cwd, updates):
|
|
os.chdir(updates)
|
|
os.system("find . | cpio -c -o | gzip -9cv > %s/updates.img" % (cwd,))
|
|
sys.stdout.write("updates.img ready\n")
|
|
|
|
def usage(cmd):
|
|
sys.stdout.write("Usage: %s [OPTION]...\n" % (cmd,))
|
|
sys.stdout.write("Options:\n")
|
|
sys.stdout.write(" -k, --keep Do not delete updates subdirectory.\n")
|
|
sys.stdout.write(" -c, --compile Compile code if there are isys changes.\n")
|
|
sys.stdout.write(" -h, --help Display this help and exit.\n")
|
|
sys.stdout.write(" -t, --tag Make image from TAG to HEAD.\n")
|
|
sys.stdout.write(" -o, --offset Make image from (latest_tag - OFFSET) to HEAD.\n")
|
|
sys.stdout.write(" -a, --add Add contents of rpm to the update\n")
|
|
|
|
def main(argv):
|
|
prog = os.path.basename(sys.argv[0])
|
|
cwd = os.getcwd()
|
|
configure = os.path.realpath(cwd + '/configure.ac')
|
|
spec = os.path.realpath(cwd + '/anaconda.spec.in')
|
|
updates = cwd + '/updates'
|
|
keep, compile, help, unknown = False, False, False, False
|
|
tag = None
|
|
opts = []
|
|
offset = 0
|
|
add_rpms = []
|
|
|
|
try:
|
|
opts, args = getopt.getopt(sys.argv[1:], 'a:t:o:kc?',
|
|
['add=', 'tag=', 'offset=',
|
|
'keep', 'compile', 'help'])
|
|
except getopt.GetoptError:
|
|
help = True
|
|
|
|
for o, a in opts:
|
|
if o in ('-k', '--keep'):
|
|
keep = True
|
|
elif o in ('-c', '--compile'):
|
|
compile = True
|
|
elif o in ('-?', '--help'):
|
|
help = True
|
|
elif o in ('-t', '--tag'):
|
|
tag = a
|
|
elif o in ('-o', '--offset'):
|
|
offset = int(a)
|
|
elif o in ('-a', '--add'):
|
|
add_rpms.append(os.path.abspath(a))
|
|
else:
|
|
unknown = True
|
|
|
|
if help:
|
|
usage(prog)
|
|
sys.exit(0)
|
|
elif unknown:
|
|
sys.stderr.write("%s: extra operand `%s'" % (prog, sys.argv[1],))
|
|
sys.stderr.write("Try `%s --help' for more information." % (prog,))
|
|
sys.exit(1)
|
|
|
|
if not os.path.isfile(configure) and not os.path.isfile(spec):
|
|
sys.stderr.write("You must be at the top level of the anaconda source tree.\n")
|
|
sys.exit(1)
|
|
|
|
if not tag:
|
|
if offset < 1:
|
|
tag = getArchiveTag(configure, spec)
|
|
else:
|
|
tag = getArchiveTagOffset(configure, spec, offset)
|
|
sys.stdout.write("Using tag: %s\n" % tag)
|
|
|
|
if not os.path.isdir(updates):
|
|
os.makedirs(updates)
|
|
|
|
copyUpdatedFiles(tag, updates, cwd)
|
|
|
|
if compile:
|
|
if isysChanged(tag):
|
|
copyUpdatedIsys(updates, cwd)
|
|
|
|
if widgetsChanged(tag):
|
|
copyUpdatedWidgets(updates, cwd)
|
|
|
|
if add_rpms:
|
|
addRpms(updates, add_rpms)
|
|
|
|
createUpdatesImage(cwd, updates)
|
|
|
|
if not keep:
|
|
shutil.rmtree(updates)
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv)
|