forked from mirrors/nixpkgs
Add: nix-template-rpm: Script to generate templates of nix expressions from RPM .spec files
This commit is contained in:
parent
a9b774204b
commit
f0228c6eb2
24
pkgs/build-support/templaterpm/default.nix
Normal file
24
pkgs/build-support/templaterpm/default.nix
Normal file
|
@ -0,0 +1,24 @@
|
|||
{stdenv, makeWrapper, python, toposort, rpm}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
name = "nix-template-rpm-${version}";
|
||||
version = "0.1";
|
||||
|
||||
buildInputs = [ makeWrapper python toposort rpm ];
|
||||
|
||||
phases = [ "installPhase" "fixupPhase" ];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp ${./nix-template-rpm.py} $out/bin/nix-template-rpm
|
||||
wrapProgram $out/bin/nix-template-rpm \
|
||||
--set PYTHONPATH "${rpm}/lib/${python.libPrefix}/site-packages":"${toposort}/lib/${python.libPrefix}/site-packages"
|
||||
'';
|
||||
|
||||
meta = with stdenv.lib; {
|
||||
description = "Create templates of nix expressions from RPM .spec files";
|
||||
maintainers = with maintainers; [ tstrobel ];
|
||||
platforms = with stdenv.lib.platforms; unix;
|
||||
hydraPlatforms = [];
|
||||
};
|
||||
}
|
274
pkgs/build-support/templaterpm/nix-template-rpm.py
Executable file
274
pkgs/build-support/templaterpm/nix-template-rpm.py
Executable file
|
@ -0,0 +1,274 @@
|
|||
#!/bin/env python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import argparse
|
||||
import shutil
|
||||
import rpm
|
||||
import urlparse
|
||||
import traceback
|
||||
import toposort
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class NixTemplateRPM(object):
|
||||
def __init__(self, specFilename, inputDir=None, maintainer="MAINTAINER"):
|
||||
rpm.addMacro("buildroot","$out")
|
||||
rpm.addMacro("_libdir","lib")
|
||||
rpm.addMacro("_libexecdir","libexec")
|
||||
rpm.addMacro("_sbindir","sbin")
|
||||
rpm.addMacro("_sysconfdir","etc")
|
||||
rpm.addMacro("_topdir","SPACER_DIR_FOR_REMOVAL")
|
||||
rpm.addMacro("_sourcedir","SOURCE_DIR_SPACER")
|
||||
|
||||
ts = rpm.TransactionSet()
|
||||
|
||||
self.specFilename = specFilename
|
||||
self.spec = ts.parseSpec(specFilename)
|
||||
|
||||
self.inputDir = inputDir
|
||||
self.maintainer = maintainer
|
||||
|
||||
self.packageGroups = [ "ocaml", "python" ]
|
||||
|
||||
|
||||
|
||||
def rewriteCommands(self, string):
|
||||
string = string.replace('SPACER_DIR_FOR_REMOVAL/','')
|
||||
string = string.replace('SPACER_DIR_FOR_REMOVAL','')
|
||||
string = '\n'.join(map(lambda line: ' '.join(map(lambda x: x.replace('SOURCE_DIR_SPACER/','${./')+'}' if x.startswith('SOURCE_DIR_SPACER/') else x, line.split(' '))), string.split('\n')))
|
||||
string = string.replace('\n','\n ')
|
||||
string = string.rstrip()
|
||||
return string
|
||||
|
||||
|
||||
def rewriteName(self, string):
|
||||
parts = string.split('-')
|
||||
parts = filter(lambda x: not x == "devel", parts)
|
||||
parts = filter(lambda x: not x == "doc", parts)
|
||||
if len(parts) > 1 and parts[0] in self.packageGroups:
|
||||
return parts[0] + '-' + ''.join(parts[1:2] + map(lambda x: x.capitalize(), parts[2:]))
|
||||
else:
|
||||
return ''.join(parts[:1] + map(lambda x: x.capitalize(), parts[1:]))
|
||||
|
||||
|
||||
def rewriteInputs(self,target,inputs):
|
||||
camelcase = lambda l: l[:1] + map(lambda x: x.capitalize(), l[1:])
|
||||
filterDevel = lambda l: filter(lambda x: not x == "devel", l)
|
||||
filterDoc = lambda l: filter(lambda x: not x == "doc", l)
|
||||
rewrite = lambda l: ''.join(camelcase(filterDoc(filterDevel(l))))
|
||||
|
||||
def filterPackageGroup(target):
|
||||
if target == None:
|
||||
return [ rewrite(x.split('-')) for x in inputs if (not x.split('-')[0] in self.packageGroups) or (len(x.split('-')) == 1) ]
|
||||
elif target in self.packageGroups:
|
||||
return [ target + '_' + rewrite(x.split('-')[1:]) for x in inputs if (x.split('-')[0] == target) and (len(x.split('-')) > 1)]
|
||||
else:
|
||||
raise Exception("Unknown target")
|
||||
return []
|
||||
|
||||
if target == None:
|
||||
packages = filterPackageGroup(None)
|
||||
packages.sort()
|
||||
elif target in self.packageGroups:
|
||||
packages = filterPackageGroup(target)
|
||||
packages.sort()
|
||||
elif target == "ALL":
|
||||
packages = []
|
||||
for t in [None] + self.packageGroups:
|
||||
tmp = filterPackageGroup(t)
|
||||
tmp.sort()
|
||||
packages += tmp
|
||||
else:
|
||||
raise Exception("Unknown target")
|
||||
packages = []
|
||||
return packages
|
||||
|
||||
|
||||
def getBuildInputs(self,target=None):
|
||||
return self.rewriteInputs(target,self.spec.sourceHeader['requires'])
|
||||
|
||||
def getSelf(self):
|
||||
name = self.spec.sourceHeader['name']
|
||||
if len(name.split('-')) > 1 and name.split('-')[0] in self.packageGroups:
|
||||
return self.rewriteInputs(name.split('-')[0], [self.spec.sourceHeader['name']])[0]
|
||||
else:
|
||||
return self.rewriteInputs(None, [self.spec.sourceHeader['name']])[0]
|
||||
|
||||
|
||||
|
||||
def copyPatches(self, input_dir, output_dir):
|
||||
patches = [source for (source, _, flag) in self.spec.sources if flag==2]
|
||||
for filename in patches:
|
||||
shutil.copyfile(os.path.join(input_dir, filename), os.path.join(output_dir, filename))
|
||||
|
||||
|
||||
def copySources(self, input_dir, output_dir):
|
||||
filenames = [source for (source, _, flag) in self.spec.sources if flag==1 if not urlparse.urlparse(source).scheme in ["http", "https"] ]
|
||||
for filename in filenames:
|
||||
shutil.copyfile(os.path.join(input_dir, filename), os.path.join(output_dir, filename))
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
out = 'stdenv.mkDerivation {\n'
|
||||
out += ' name = "' + self.rewriteName(self.spec.sourceHeader['name']) + '-' + self.spec.sourceHeader['version'] + '";\n'
|
||||
out += ' version = "' + self.spec.sourceHeader['version'] + '";\n'
|
||||
return out
|
||||
|
||||
|
||||
@property
|
||||
def src(self):
|
||||
sources = [source for (source, _, flag) in self.spec.sources if flag==1 if urlparse.urlparse(source).scheme in ["http", "https"] ]
|
||||
out = ''
|
||||
for url in sources:
|
||||
p = subprocess.Popen(['nix-prefetch-url', url], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
output, err = p.communicate()
|
||||
sha256 = output[:-1] #remove new line
|
||||
out += ' src = fetchurl {\n'
|
||||
out += ' url = "' + url + '";\n'
|
||||
out += ' sha256 = "' + sha256 + '";\n'
|
||||
out += ' };\n'
|
||||
return out
|
||||
|
||||
|
||||
@property
|
||||
def patch(self):
|
||||
patches = [source for (source, _, flag) in self.spec.sources if flag==2]
|
||||
out = ' patches = [ ' + ' '.join(map(lambda x: './'+x, patches)) + ' ];\n'
|
||||
return out
|
||||
|
||||
|
||||
@property
|
||||
def buildInputs(self):
|
||||
out = ' buildInputs = [ '
|
||||
out += ' '.join(self.getBuildInputs("ALL"))
|
||||
out += ' ];\n'
|
||||
return out
|
||||
|
||||
|
||||
@property
|
||||
def configure(self):
|
||||
out = ' configurePhase = \'\'\n ' + self.rewriteCommands(self.spec.prep) + '\n \'\';\n';
|
||||
return out
|
||||
|
||||
|
||||
@property
|
||||
def build(self):
|
||||
out = ' buildPhase = \'\'\n ' + self.rewriteCommands(self.spec.build) + '\n \'\';\n';
|
||||
return out
|
||||
|
||||
|
||||
@property
|
||||
def install(self):
|
||||
out = ' installPhase = \'\'\n ' + self.rewriteCommands(self.spec.install) + '\n \'\';\n';
|
||||
return out
|
||||
|
||||
@property
|
||||
def ocamlExtra(self):
|
||||
if "ocaml" in self.getBuildInputs("ALL"):
|
||||
return ' createFindlibDestdir = true;\n'
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
out = ' meta = {\n'
|
||||
out += ' homepage = ' + self.spec.sourceHeader['url'] + ';\n'
|
||||
out += ' description = "' + self.spec.sourceHeader['summary'] + '";\n'
|
||||
out += ' license = stdenv.lib.licenses.' + self.spec.sourceHeader['license'] + ';\n'
|
||||
out += ' platforms = [ "i686-linux" "x86_64-linux" ];\n'
|
||||
out += ' maintainers = with stdenv.lib.maintainers; [ ' + self.maintainer + ' ];\n'
|
||||
out += ' };\n'
|
||||
out += '}\n'
|
||||
return out
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
head = '{stdenv, fetchurl, ' + ', '.join(self.getBuildInputs("ALL")) + '}:\n\n'
|
||||
body = [ self.name, self.src, self.patch, self.buildInputs, self.configure, self.build, self.ocamlExtra, self.install, self.meta ]
|
||||
return head + '\n'.join(body)
|
||||
|
||||
|
||||
def __cmp__(self,other):
|
||||
if self.getSelf() in other.getBuildInputs("ALL"):
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
|
||||
|
||||
def callPackage(self, output_dir):
|
||||
callPackage = ' ' + self.getSelf() + ' = callPackage ' + os.path.relpath(output_dir) + ' {'
|
||||
newline = False;
|
||||
for target in self.packageGroups:
|
||||
tmp = self.getBuildInputs(target)
|
||||
if len(tmp) > 0:
|
||||
newline = True;
|
||||
callPackage += '\n ' + 'inherit (' + target + 'Packages) ' + ' '.join(tmp) + ';'
|
||||
if newline:
|
||||
callPackage += '\n };'
|
||||
else:
|
||||
callPackage += ' };'
|
||||
return callPackage
|
||||
|
||||
|
||||
|
||||
def generateTemplate(self, outputDir):
|
||||
output_dir = os.path.normpath( os.path.join(outputDir, self.rewriteName(self.spec.sourceHeader['name'])) )
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
if self.inputDir != None:
|
||||
self.copySources(self.inputDir, output_dir)
|
||||
self.copyPatches(self.inputDir, output_dir)
|
||||
|
||||
nixfile = open(os.path.join(output_dir,'default.nix'), 'w')
|
||||
nixfile.write(str(self))
|
||||
nixfile.close()
|
||||
|
||||
shutil.copyfile(self.specFilename, os.path.join(output_dir, os.path.basename(self.specFilename)))
|
||||
|
||||
self.pkgCall = self.callPackage(output_dir)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
#Parse command line options
|
||||
parser = argparse.ArgumentParser(description="Generate .nix templates from RPM spec files")
|
||||
parser.add_argument("specs", metavar="SPEC", nargs="+", help="spec file")
|
||||
parser.add_argument("-o", "--output", metavar="OUT_DIR", required=True, help="output directory")
|
||||
parser.add_argument("-i", "--input", metavar="IN_DIR", default=None, help="input directory")
|
||||
parser.add_argument("-m", "--maintainer", metavar="MAINTAINER", required=True, help="package maintainer")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
nameMap = {}
|
||||
|
||||
for specPath in args.specs:
|
||||
try:
|
||||
sys.stderr.write("INFO: generate nix file from: %s\n" % specPath)
|
||||
spec = NixTemplateRPM(specPath, args.input, args.maintainer)
|
||||
spec.generateTemplate(args.output)
|
||||
nameMap[spec.getSelf()] = spec
|
||||
|
||||
except Exception, e:
|
||||
sys.stderr.write("ERROR: %s failed with:\n%s\n%s\n" % (specPath,e.message,traceback.format_exc()))
|
||||
|
||||
graph = {}
|
||||
for k, v in nameMap.items():
|
||||
graph[k] = set(v.getBuildInputs("ALL"))
|
||||
|
||||
sortedSpecs = toposort.toposort_flatten(graph)
|
||||
sortedSpecs = filter( lambda x: x in nameMap.keys(), sortedSpecs)
|
||||
|
||||
print '\n\n'.join(map(lambda x: x.pkgCall, map(lambda x: nameMap[x], sortedSpecs)))
|
|
@ -13000,6 +13000,8 @@ let
|
|||
|
||||
nix-prefetch-scripts = callPackage ../tools/package-management/nix-prefetch-scripts { };
|
||||
|
||||
nix-template-rpm = callPackage ../build-support/templaterpm { inherit (pythonPackages) python toposort; };
|
||||
|
||||
nix-repl = callPackage ../tools/package-management/nix-repl { };
|
||||
|
||||
nix-serve = callPackage ../tools/package-management/nix-serve { };
|
||||
|
|
|
@ -12967,6 +12967,22 @@ let
|
|||
};
|
||||
};
|
||||
|
||||
toposort = buildPythonPackage rec {
|
||||
name = "toposort-${version}";
|
||||
version = "1.1";
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://pypi.python.org/packages/source/t/toposort/toposort-1.1.tar.gz";
|
||||
sha256 = "1izmirbwmd9xrk7rq83p486cvnsslfa5ljvl7rijj1r64zkcnf3a";
|
||||
};
|
||||
meta = {
|
||||
description = "A topological sort algorithm";
|
||||
homepage = https://pypi.python.org/pypi/toposort/1.1;
|
||||
maintainers = [ stdenv.lib.maintainers.tstrobel ];
|
||||
platforms = stdenv.lib.platforms.linux;
|
||||
#license = stdenv.lib.licenses.apache;
|
||||
};
|
||||
};
|
||||
|
||||
snapperGUI = buildPythonPackage rec {
|
||||
name = "Snapper-GUI";
|
||||
|
||||
|
|
Loading…
Reference in a new issue