Whilst attempting to package crystalspace I got sick of scanning the configure.ac by hand, and wrote a script to try to automate this. Attached is the result; you give it the path to an unpacked/prepped source tree, and it looks inside at the configure.ac/configure.in/configure scripts (if any), scouring them for header file references, then using rpm and yum to figure out what provides them, and finally spits out a sorted list of BuildRequires: lines. It uses some special-casing/heuristics/hacks to try to generate something sane. This was useful to me, and I suspect to other, so perhaps (suitably renamed) it could live in the rpmdevtools package? If anyone's familiar with the yum api, there's some major optimization that could be done (I call out to yum each time, rather than setting it up in code and amortizing the startup costs) Hope this helps Dave
#!/usr/bin/env python # # autospec.py - figure out the BuildRequires from a prepped source tree # # Copyright (c) David Malcolm <dmalcolm@xxxxxxxxxx> # # 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 General Public License for more details. import re from os.path import join, exists import rpm import os import sys class Specdata: """ This class accumulates results from sniffing BuildRequires """ def __init__(self): self.brDict = {} self.missingHeaders = {} self.showReasons = False def __str__(self): result = "" for br in sorted(self.brDict): result += "BuildRequires: %s\n" % br if self.showReasons: reasons = self.brDict[br] for reason in reasons: result += "# (for %s)\n" % reason for h in self.missingHeaders: print "# unknown header: #include <%s>" % h return result def add_build_requires(self, package, reason): if package in self.brDict: if reason not in self.brDict[package]: self.brDict[package].append(reason) else: self.brDict[package] = [reason] def add_missing_header(self, header): self.missingHeaders[header] = True def get_package_for_file(absPath): ts = rpm.TransactionSet() mi = ts.dbMatch('basenames', absPath) for h in mi: return h['name'] def get_yum_whatprovides_file(absPath): # FIXME: use yum API to amortize startup expense of all these yum queries print '# yum whatprovides %s' % absPath for line in os.popen('yum whatprovides %s' % absPath): m = re.match('(.*)\.(.*) : (.*)', line) if m: return m.group(1) # Don't bother checking for headers; these are for Windows support etc headerBlacklist = """ windows.h ddraw.h dsound.h dinput.h winsock.h """.strip().split('\n') #print headerBlacklist class BRSniffer: """ Given a prepped source tree, try to guess appropriate BuildRequires lines for the specfile. A function really, but it's useful as a class for chopping up into subroutines """ def __init__(self): self.spec = Specdata() self.relHeaders = {} # header files processed so far def scan_any_configure_files(self, treePath): # Scan for configure.ac/configure.in first, then the full configure script (to try to catch everything) for acFilename in ['configure.ac', 'configure.in', 'configure']: path = join(treePath, acFilename) if exists(path): print "# scanning %s" % acFilename for line in open(path): # print line, m = re.match(r'.*#include \<(.*)\>.*', line) if m: relHeader = m.group(1) # e.g. "X11/Xaw/Form.h" self.process_rel_header(relHeader) def process_rel_header(self, relHeader): """ Handle a #include <foo.h> line, trying to add whatever provides foo.h """ inclusion = '#include <%s>' % relHeader if relHeader in self.relHeaders: # we've already seen this header: return self.relHeaders[relHeader]=True # in case we see it again # Ignore certain headers that look like MS Windows support etc if relHeader in headerBlacklist: return # Perl and Python headers live in other paths that won't get found # without specialcasing (or actually parsing the m4): if relHeader == 'perl.h': self.spec.add_build_requires('perl-devel', reason = inclusion) return if relHeader == 'Python.h': self.spec.add_build_requires('python-devel', reason = inclusion) return if not re.match('.*\.h', relHeader): # C++-style inclusion e.g. '#include <string>': # for now, assume this: self.spec.add_build_requires('libstdc++-devel', reason = inclusion) return # FIXME: should search in various paths? absHeader = join('/usr/include', relHeader) if exists(absHeader): # which installed pacakge provides this? # print "Uses: %s " % absHeader package = get_package_for_file(absHeader) if package: self.spec.add_build_requires(package, reason = inclusion) else: # not found: use yum; which package provides this? # print "Not found: %s " % absHeader package = get_yum_whatprovides_file(absHeader) if package: self.spec.add_build_requires(package, reason = inclusion) else: self.spec.add_missing_header(relHeader) def guess_buildrequires(treepath): sniffer = BRSniffer() sniffer.scan_any_configure_files(treepath) return sniffer.spec def usage(): print "%s PATH" % sys.argv[0] print "given the path to a prepped source tree, try to guess a suitable" print "set of BuildRequires: lines for the specfile" # FIXME: would it be better to simply work with a specfile, and figure things out based on buildroot etc? if __name__ == '__main__': try: treepath = sys.argv[1] except: usage() sys.exit(1) s = guess_buildrequires(treepath) print s
-- fedora-devel-list mailing list fedora-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/fedora-devel-list