2 # Copyright (C) 2008, Thomas Leonard
3 # See the COPYING file for details, or visit http://0install.net.
6 from optparse
import OptionParser
7 import tempfile
, shutil
, os
8 from xml
.dom
import minidom
11 manifest_algorithm
= 'sha1new'
13 from zeroinstall
.injector
.namespaces
import XMLNS_IFACE
15 deb_category_to_freedesktop
= {
16 'devel' : 'Development',
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()
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
])
54 child
= subprocess
.Popen(cmd
, stdout
= subprocess
.PIPE
)
55 output
, unused
= child
.communicate()
57 print >>sys
.stderr
, output
58 print >>sys
.stderr
, "%s: code = %d" % (' '.join(cmd
), child
.returncode
)
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]
67 for x
in description_and_summary
[1:]:
76 description
+= x
[1:].replace('. ', '. ') + '\n'
77 description
= description
.strip()
79 pkg_name
= '(unknown)'
82 for line
in details
.split('\n'):
84 assert line
.startswith(' ')
87 key
, value
= line
.split(':', 1)
90 category
= deb_category_to_freedesktop
.get(value
)
94 print >>sys
.stderr
, "Warning: no mapping for Debian category '%s'" % value
95 elif key
== 'Package':
97 elif key
== 'Version':
100 template
= '''<interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
102 doc
= minidom
.parseString(template
)
103 root
= doc
.documentElement
106 tmp
= tempfile
.mkdtemp(prefix
= 'deb2zero-')
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(';'):
117 if cat
in valid_categories
:
120 elif f
.startswith('./usr/bin'):
123 manifest
= read_child(['0store', 'manifest', tmp
, manifest_algorithm
])
124 digest
= manifest
.rsplit('\n', 2)[1]
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
)
134 new
.appendChild(doc
.createTextNode(text
))
135 parent
.appendChild(doc
.createTextNode(after
))
138 add_node(root
, 'name', pkg_name
)
139 add_node(root
, 'summary', summary
)
140 add_node(root
, 'description', description
)
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'))
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
)