about summary refs log tree commit diff
path: root/pkgs/build-support/templaterpm
diff options
context:
space:
mode:
authorThomas Strobel <ts468@cam.ac.uk>2015-01-02 13:16:44 +0100
committerCray Elliott <MP2E@archlinux.us>2015-01-27 01:18:52 -0800
commitf0228c6eb2f1a2c4ba89705fec7c696dd0b33a2f (patch)
treeb275360464d8512200dd19066dc7beae0e42a243 /pkgs/build-support/templaterpm
parenta9b774204b6570593635cfb9f46b20cc2f9246f1 (diff)
downloadnixlib-f0228c6eb2f1a2c4ba89705fec7c696dd0b33a2f.tar
nixlib-f0228c6eb2f1a2c4ba89705fec7c696dd0b33a2f.tar.gz
nixlib-f0228c6eb2f1a2c4ba89705fec7c696dd0b33a2f.tar.bz2
nixlib-f0228c6eb2f1a2c4ba89705fec7c696dd0b33a2f.tar.lz
nixlib-f0228c6eb2f1a2c4ba89705fec7c696dd0b33a2f.tar.xz
nixlib-f0228c6eb2f1a2c4ba89705fec7c696dd0b33a2f.tar.zst
nixlib-f0228c6eb2f1a2c4ba89705fec7c696dd0b33a2f.zip
Add: nix-template-rpm: Script to generate templates of nix expressions from RPM .spec files
Diffstat (limited to 'pkgs/build-support/templaterpm')
-rw-r--r--pkgs/build-support/templaterpm/default.nix24
-rwxr-xr-xpkgs/build-support/templaterpm/nix-template-rpm.py274
2 files changed, 298 insertions, 0 deletions
diff --git a/pkgs/build-support/templaterpm/default.nix b/pkgs/build-support/templaterpm/default.nix
new file mode 100644
index 000000000000..aca4e340e267
--- /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 000000000000..42f8ee8a75fe
--- /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)))