#!/usr/bin/env python # spec-convert-gcj # ================ # # Create a JPackage spec file that contains GCJ support from a JPackage # SRPM and its corresponding RPMs. spec-convert-gcj uses the binary RPMs # to determine each sub-package's list of jar files. # # Example: # spec-convert-gcj ~/rpmbuild/SRPMS/bsh-*1.3.0-6jpp* ~/rpmbuild/RPMS/noarch/bsh-*1.3.0-6jpp* import sys import os import warnings class Error(Exception): pass # parsing state state = "PREAMBLE" partList = [ "PREAMBLE", "%package", "%prep", "%build", "%install", "%check", "%clean", "%preun", "%postun", "%pre", "%post", "%files", "%changelog", "%description", "%triggerpostun", "%triggerun", "%triggerin", "%trigger", "%verifyscript", "END" ] packages = {} macroizedpackagenames = {} currentpackage = None doneuptofiles = False mainpackageempty = False NAME = "spec-convert-gcj" VERSION = "1.1" class Package: name = "" jarfilenames = [] postdone = False postundone = False def __init__(self, name, jarfilenames): self.name = name self.jarfilenames = jarfilenames def contains_jars(self): return len(self.jarfilenames) != 0 def get_name(self): return self.name def get_jar_file_names(self): return self.jarfilenames def set_post_done(self): self.postdone = True def get_post_done(self): return self.postdone def set_postun_done(self): self.postundone = True def get_postun_done(self): return self.postdone def __str__(self): return self.name def get_package_name(rpmfilename): rpmoutput = os.popen("rpm --queryformat %{name} -qp " + rpmfilename) packagename = rpmoutput.read() status = rpmoutput.close() if status is not None or packagename == "": raise Error, "spec file name lookup failed" else: print "spec file name lookup passed" return packagename def extract_files(rpmfilename, filenamepattern): status = os.system("rpm2cpio " + rpmfilename + " | cpio -id " + filenamepattern) if status > 0: raise Error, "%s exited with code %d" % ("cpio", status) elif status < 0: raise Error, "%s killed by signal %d" % ("cpio", -status) else: print "rpm2cpio passed" def get_jar_file_names_callback(jarfilenames, dirname, fnames): for fname in fnames: if fname.endswith(".jar") \ and os.path.isfile(os.path.join(dirname, fname)) \ and not os.path.islink(os.path.join(dirname, fname)): jarfilenames.append(fname) os.remove(os.path.join(dirname, fname)) def note_package(rpmfilename): global packages unmacroizedpackagename = get_package_name(rpmfilename) if unmacroizedpackagename != mainpackagename + "-debuginfo": if unmacroizedpackagename == mainpackagename: packagename = mainpackagename else: packagename = macroizedpackagenames[unmacroizedpackagename] jarfilenames = [] extract_files(rpmfilename, "*.jar") os.path.walk(".", get_jar_file_names_callback, jarfilenames) packages[packagename] = Package(packagename, jarfilenames) def filter_preamble(line): if line.lower().startswith("buildarch:"): if line.lower().split()[1] == "noarch": add_buildarch(line) else: outputspecfile.write(line) else: outputspecfile.write(line) def add_buildarch(line): outputspecfile.write("%if ! %{gcj_support}\n") outputspecfile.write(line) outputspecfile.write("%endif\n") def add_requires(): outputspecfile.write("%if %{gcj_support}\n") outputspecfile.write("BuildRequires:\t\tjava-gcj-compat-devel\n") outputspecfile.write("Requires(post):\t\tjava-gcj-compat\n") outputspecfile.write("Requires(postun):\tjava-gcj-compat\n") outputspecfile.write("%endif\n") outputspecfile.write("\n") def add_aot_compile_rpm(): outputspecfile.write("%if %{gcj_support}\n") outputspecfile.write("%{_bindir}/aot-compile-rpm\n") outputspecfile.write("%endif\n") outputspecfile.write("\n") def add_so_and_db_files(): outputspecfile.write("%if %{gcj_support}\n") outputspecfile.write("%dir %attr(-,root,root) %{_libdir}/gcj/%{name}\n") for jarfilename in currentpackage.get_jar_file_names(): outputspecfile.write("%attr(-,root,root) %{_libdir}/gcj/%{name}/" + jarfilename + ".*\n") outputspecfile.write("%endif\n") outputspecfile.write("\n") def add_rebuild_gcj_db(): outputspecfile.write("%if %{gcj_support}\n") add_rebuild_gcj_db_no_condition() outputspecfile.write("%endif\n") outputspecfile.write("\n") def add_rebuild_gcj_db_no_condition(): outputspecfile.write("if [ -x %{_bindir}/rebuild-gcj-db ]\n") outputspecfile.write("then\n") outputspecfile.write(" %{_bindir}/rebuild-gcj-db\n") outputspecfile.write("fi\n") def add_name(name): if name.startswith(mainpackagename): if name != mainpackagename: outputspecfile.write(" " + name.replace(mainpackagename + "-", "", 1)) else: # full name outputspecfile.write(" -n " + name) outputspecfile.write("\n") def add_post(name): outputspecfile.write("%if %{gcj_support}\n") outputspecfile.write("%post") add_name(name) add_rebuild_gcj_db_no_condition() outputspecfile.write("%endif\n") outputspecfile.write("\n") def add_postun(name): outputspecfile.write("%if %{gcj_support}\n") outputspecfile.write("%postun") add_name(name) add_rebuild_gcj_db_no_condition() outputspecfile.write("%endif\n") outputspecfile.write("\n") def add_before_part(state): global doneuptofiles if state == "%files": if doneuptofiles == False: for packagename in packages: package = packages[packagename] if package.contains_jars(): if not package.get_post_done(): add_post(package.get_name()) if not package.get_postun_done(): add_postun(package.get_name()) doneuptofiles = True def add_to_part(state): if state == "PREAMBLE": if currentpackage is not None \ and currentpackage.contains_jars(): add_requires() if state == "%package": if currentpackage is not None \ and currentpackage.contains_jars(): add_requires() elif state == "%install": add_aot_compile_rpm() elif state == "%files": if currentpackage is not None \ and currentpackage.contains_jars(): add_so_and_db_files() elif state == "%post": if currentpackage is not None \ and currentpackage.contains_jars(): add_rebuild_gcj_db() currentpackage.set_post_done() elif state == "%postun": if currentpackage is not None \ and currentpackage.contains_jars(): add_rebuild_gcj_db() currentpackage.set_postun_done() def set_currentpackage(line): global currentpackage fields = line.split() if len(fields) == 1: if not mainpackageempty: try: currentpackage = packages[mainpackagename] except KeyError, e: pass else: currentpackage = None else: if fields[1] == "-n": # full name specified try: currentpackage = packages[fields[2]] except KeyError, e: pass else: try: currentpackage = packages[mainpackagename + "-" + fields[1]] except KeyError, e: pass def set_macroized_package_names(): global macroizedpackagenames packagelines = os.popen("cat " + specfilename + " | grep '^%package'") packagenames = os.popen("rpm -q --specfile --queryformat='%{name}\n' " + specfilename + " | grep -v " + mainpackagename + "-debuginfo") packageline = packagelines.readline() # skip main package name packagename = packagenames.readline() packagename = packagenames.readline() if packagename != "": while packageline != "": fields = packageline.split() if len(fields) == 2: macroizedpackagenames[packagename.strip()] = mainpackagename + "-" + fields[1] else: if len(fields) == 3: macroizedpackagenames[packagename.strip()] = fields[2] packageline = packagelines.readline() packagename = packagenames.readline() packagelines.close() packagenames.close() if packageline != "" or packagename !="": raise Error, "number of Name: and %package lines does not match number of names returned by spec file query" else: print "macroized package names creation passed" print "map of unmacroized names to macroized names:" print macroizedpackagenames def next_state(line): global state if line == "": add_to_part(state) state = "END" return for part in partList: if line.startswith(part): add_to_part(state) state = part add_before_part(state) set_currentpackage(line) def print_version(): print NAME, VERSION sys.exit(0) def print_usage(): print "Usage: " + NAME + " SRPM RPMS" print "Create a JPackage spec file that contains GCJ support from a JPackage" print "SRPM and its corresponding RPMs. " + NAME + " uses the binary RPMs" print "to determine each sub-package's list of jar files." print "" print "Example:" print " " + NAME + " ~/rpmbuild/SRPMS/bsh-*1.3.0-6jpp* ~/rpmbuild/RPMS/noarch/bsh-*1.3.0-6jpp*" sys.exit(0) if __name__ == "__main__": try: options = sys.argv[1:] if len(options) == 0: print NAME + ": no input files" print_usage() origdir = os.getcwd() # find SRPM within arguments srpmfilename = "" rpmfilenames = [] for option in options: if option.endswith(".src.rpm"): if srpmfilename != "": raise Error, "more than one SRPM specified" else: srpmfilename = os.path.join(origdir, option) elif option.endswith(".rpm"): rpmfilenames.append(os.path.join(origdir, option)) elif option == "--version": print_version() elif option == "--help": print_usage() warnings.filterwarnings("ignore", message="tmpnam is a potential security risk to your program") tmpdir = os.path.basename(os.tmpnam()) os.mkdir(tmpdir) os.chdir(tmpdir) # get spec file name: assumes that the spec file is called # %{name}.spec print "processing: " + srpmfilename mainpackagename = get_package_name(srpmfilename) specfilename = mainpackagename + ".spec" # extract spec file print "extracting spec file: " + specfilename extract_files(srpmfilename, specfilename) # check for existing gcj support checkconverted = os.popen("grep gcj_support " + specfilename) checksupport = checkconverted.readline() if checksupport != "": raise Error, "these rpms already support gcj" status = checkconverted.close() if status is None: raise Error, "failed to close grep pipe" else: print "no gcj support detected" # calculate unmacroized name-to-macroized name mapping set_macroized_package_names() for rpmfilename in rpmfilenames: note_package(rpmfilename) # open spec file for reading specfile = open(specfilename, 'r') # open output spec file for writing outputspecfile = open(os.path.join(origdir, mainpackagename + "-gcj.spec"), 'w') # check if there is a main package or only sub-packages if mainpackagename in packages: currentpackage = packages[mainpackagename] else: mainpackageempty = True print "main package:", currentpackage line = specfile.readline() next_state(line) # skip license block while line.startswith("#"): outputspecfile.write(line) line = specfile.readline() outputspecfile.write("\n%define gcj_support %{?_with_gcj_support:1}%{!?_with_gcj_support:%{?_without_gcj_support:0}%{!?_without_gcj_support:%{?_gcj_support:%{_gcj_support}}%{!?_gcj_support:0}}}\n") while line != "": if state == "PREAMBLE": filter_preamble(line) else: outputspecfile.write(line) line = specfile.readline() next_state(line) outputspecfile.close() os.chdir(origdir) except Error, e: print >> sys.stderr, "%s: error: %s" % ( os.path.basename(sys.argv[0]), e) sys.exit(1)