Create working feeds.
[deb2zero.git] / deb2zero
blobb9286db87b43bce2e229ad161c86ffd7260decf2
1 #!/usr/bin/env python
2 # Copyright (C) 2008, Thomas Leonard
3 # See the COPYING file for details, or visit http://0install.net.
5 import sys, time
6 from optparse import OptionParser
7 import tempfile, shutil, os
8 from xml.dom import minidom
9 import subprocess
11 manifest_algorithm = 'sha1new'
13 from zeroinstall.injector.namespaces import XMLNS_IFACE
15 deb_category_to_freedesktop = {
16 'devel' : 'Development',
17 'web' : 'Network',
20 valid_categories = [
21 'AudioVideo',
22 'Audio',
23 'Video',
24 'Development',
25 'Education',
26 'Game',
27 'Graphics',
28 'Network',
29 'Office',
30 'Settings',
31 'System',
32 'Utility',
35 parser = OptionParser('usage: %prog [options] http://.../package.deb\n'
36 'Create a Zero Install feed for a Debian package.')
37 (options, args) = parser.parse_args()
39 if len(args) != 1:
40 parser.print_help()
41 sys.exit(1)
43 pkg_url = args[0]
44 assert pkg_url.startswith('http:') or \
45 pkg_url.startswith('https:') or \
46 pkg_url.startswith('ftp:')
47 deb_file = os.path.abspath(pkg_url.rsplit('/', 1)[1])
49 if not os.path.exists(deb_file):
50 print "File '%s' not found, so downloading from %s..." % (deb_file, pkg_url)
51 subprocess.check_call(['wget', pkg_url])
53 def read_child(cmd):
54 child = subprocess.Popen(cmd, stdout = subprocess.PIPE)
55 output, unused = child.communicate()
56 if child.returncode:
57 print >>sys.stderr, output
58 print >>sys.stderr, "%s: code = %d" % (' '.join(cmd), child.returncode)
59 sys.exit(1)
60 return output
62 details = read_child(['dpkg-deb', '--info', deb_file])
64 description_and_summary = details.split('\n Description: ')[1].split('\n')
65 summary = description_and_summary[0]
66 description = ''
67 for x in description_and_summary[1:]:
68 if not x: continue
69 assert x[0] == ' '
70 x = x[1:]
71 if x[0] != ' ':
72 break
73 if x == ' .':
74 description += '\n'
75 else:
76 description += x[1:].replace('. ', '. ') + '\n'
77 description = description.strip()
79 pkg_name = '(unknown)'
80 pkg_version = None
81 category = None
82 for line in details.split('\n'):
83 if not line: continue
84 assert line.startswith(' ')
85 line = line[1:]
86 if ':' in line:
87 key, value = line.split(':', 1)
88 value = value.strip()
89 if key == 'Section':
90 category = deb_category_to_freedesktop.get(value)
91 if category:
92 break
93 else:
94 print >>sys.stderr, "Warning: no mapping for Debian category '%s'" % value
95 elif key == 'Package':
96 pkg_name = value
97 elif key == 'Version':
98 pkg_version = value
100 template = '''<interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
101 </interface>'''
102 doc = minidom.parseString(template)
103 root = doc.documentElement
105 pkg_main = None
106 tmp = tempfile.mkdtemp(prefix = 'deb2zero-')
107 try:
108 files = read_child(['dpkg-deb', '-X', deb_file, tmp])
110 for f in files.split('\n'):
111 if f.endswith('.desktop'):
112 full = os.path.join(tmp, f)
113 for line in file(full):
114 if line.startswith('Categories'):
115 for cat in line.split('=', 1)[1].split(';'):
116 cat = cat.strip()
117 if cat in valid_categories:
118 category = cat
119 break
120 elif f.startswith('./usr/bin'):
121 pkg_main = f[2:]
123 manifest = read_child(['0store', 'manifest', tmp, manifest_algorithm])
124 digest = manifest.rsplit('\n', 2)[1]
125 finally:
126 shutil.rmtree(tmp)
128 def add_node(parent, element, text = None, before = ' ', after = '\n'):
129 doc = parent.ownerDocument
130 parent.appendChild(doc.createTextNode(before))
131 new = doc.createElementNS(XMLNS_IFACE, element)
132 parent.appendChild(new)
133 if text:
134 new.appendChild(doc.createTextNode(text))
135 parent.appendChild(doc.createTextNode(after))
136 return new
138 add_node(root, 'name', pkg_name)
139 add_node(root, 'summary', summary)
140 add_node(root, 'description', description)
141 if category:
142 add_node(root, 'category', category)
143 group = add_node(root, 'group', '')
144 impl = add_node(group, 'implementation', before = '\n ', after = '\n ')
145 impl.setAttribute('id', digest)
146 impl.setAttribute('version', pkg_version)
147 impl.setAttribute('released', time.strftime('%Y-%m-%d'))
148 if pkg_main:
149 impl.setAttribute('main', pkg_main)
151 archive = add_node(impl, 'archive', before = '\n ', after = '\n ')
152 archive.setAttribute('href', pkg_url)
153 archive.setAttribute('size', str(os.path.getsize(deb_file)))
155 print "<?xml version='1.0'?>"
156 root.writexml(sys.stdout)
157 print