diff --git a/pkgs/build-support/templaterpm/default.nix b/pkgs/build-support/templaterpm/default.nix new file mode 100644 index 00000000000..aca4e340e26 --- /dev/null +++ b/pkgs/build-support/templaterpm/default.nix @@ -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 = []; + }; +} diff --git a/pkgs/build-support/templaterpm/nix-template-rpm.py b/pkgs/build-support/templaterpm/nix-template-rpm.py new file mode 100755 index 00000000000..42f8ee8a75f --- /dev/null +++ b/pkgs/build-support/templaterpm/nix-template-rpm.py @@ -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))) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 26cb9eda76a..3a97ef49f50 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -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 { }; diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix index 0132ce4ba25..bbfcd3b6b15 100644 --- a/pkgs/top-level/python-packages.nix +++ b/pkgs/top-level/python-packages.nix @@ -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";