# -*- coding: utf-8 -*-
#
# common.py - part of the FDroid server tools
# Copyright (C) 2013, Ciaran Gultnieks, ciaran@ciarang.com
# Copyright (C) 2013 Daniel MartÃ
' self.state = self.stPARA elif self.state == self.stPARA: self.text_html += ' ' self.text_plain += ' ' self.addtext(line) def end(self): self.endcur() # Parse multiple lines of description as written in a metadata file, returning # a single string in plain text format. def description_plain(lines, linkres): ps = DescriptionFormatter(linkres) for line in lines: ps.parseline(line) ps.end() return ps.text_plain # Parse multiple lines of description as written in a metadata file, returning # a single string in wiki format. Used for the Maintainer Notes field as well, # because it's the same format. def description_wiki(lines): ps = DescriptionFormatter(None) for line in lines: ps.parseline(line) ps.end() return ps.text_wiki # Parse multiple lines of description as written in a metadata file, returning # a single string in HTML format. def description_html(lines,linkres): ps = DescriptionFormatter(linkres) for line in lines: ps.parseline(line) ps.end() return ps.text_html def parse_srclib(metafile, **kw): thisinfo = {} if metafile and not isinstance(metafile, file): metafile = open(metafile, "r") # Defaults for fields that come from metadata thisinfo['Repo Type'] = '' thisinfo['Repo'] = '' thisinfo['Subdir'] = None thisinfo['Prepare'] = None thisinfo['Srclibs'] = None thisinfo['Update Project'] = None if metafile is None: return thisinfo for line in metafile: line = line.rstrip('\r\n') if not line or line.startswith("#"): continue try: field, value = line.split(':',1) except ValueError: raise MetaDataException("Invalid metadata in " + metafile.name + " at: " + line) if field == "Subdir": thisinfo[field] = value.split(',') else: thisinfo[field] = value return thisinfo # Read all metadata. Returns a list of 'app' objects (which are dictionaries as # returned by the parse_metadata function. def read_metadata(xref=True, package=None): apps = [] for basedir in ('metadata', 'tmp'): if not os.path.exists(basedir): os.makedirs(basedir) for metafile in sorted(glob.glob(os.path.join('metadata', '*.txt'))): if package is None or metafile == os.path.join('metadata', package + '.txt'): try: appinfo = parse_metadata(metafile) except Exception, e: raise MetaDataException("Problem reading metadata file %s: - %s" % (metafile, str(e))) check_metadata(appinfo) apps.append(appinfo) if xref: # Parse all descriptions at load time, just to ensure cross-referencing # errors are caught early rather than when they hit the build server. def linkres(link): for app in apps: if app['id'] == link: return ("fdroid.app:" + link, "Dummy name - don't know yet") raise MetaDataException("Cannot resolve app id " + link) for app in apps: try: description_html(app['Description'], linkres) except Exception, e: raise MetaDataException("Problem with description of " + app['id'] + " - " + str(e)) return apps # Get the type expected for a given metadata field. def metafieldtype(name): if name in ['Description', 'Maintainer Notes']: return 'multiline' if name == 'Build Version': return 'build' if name == 'Build': return 'buildv2' if name == 'Use Built': return 'obsolete' return 'string' # Parse metadata for a single application. # # 'metafile' - the filename to read. The package id for the application comes # from this filename. Pass None to get a blank entry. # # Returns a dictionary containing all the details of the application. There are # two major kinds of information in the dictionary. Keys beginning with capital # letters correspond directory to identically named keys in the metadata file. # Keys beginning with lower case letters are generated in one way or another, # and are not found verbatim in the metadata. # # Known keys not originating from the metadata are: # # 'id' - the application's package ID # 'builds' - a list of dictionaries containing build information # for each defined build # 'comments' - a list of comments from the metadata file. Each is # a tuple of the form (field, comment) where field is # the name of the field it preceded in the metadata # file. Where field is None, the comment goes at the # end of the file. Alternatively, 'build:version' is # for a comment before a particular build version. # 'descriptionlines' - original lines of description as formatted in the # metadata file. # def parse_metadata(metafile): def parse_buildline(lines): value = "".join(lines) parts = [p.replace("\\,", ",") for p in re.split(r"(? 0: #print "...writing comments for " + (key if key else 'EOF') def writefield(field, value=None): writecomments(field) if value is None: value = app[field] mf.write("%s:%s\n" % (field, value)) mf = open(dest, 'w') if app['Disabled']: writefield('Disabled') if app['AntiFeatures']: writefield('AntiFeatures') if app['Provides']: writefield('Provides') writefield('Categories') writefield('License') writefield('Web Site') writefield('Source Code') writefield('Issue Tracker') if app['Donate']: writefield('Donate') if app['FlattrID']: writefield('FlattrID') if app['Bitcoin']: writefield('Bitcoin') if app['Litecoin']: writefield('Litecoin') if app['Dogecoin']: writefield('Dogecoin') mf.write('\n') if app['Name']: writefield('Name') if app['Auto Name']: writefield('Auto Name') writefield('Summary') writefield('Description', '') for line in app['Description']: mf.write("%s\n" % line) mf.write('.\n') mf.write('\n') if app['Requires Root']: writefield('Requires Root', 'Yes') mf.write('\n') if app['Repo Type']: writefield('Repo Type') writefield('Repo') mf.write('\n') for build in app['builds']: writecomments('build:' + build['version']) mf.write("Build:%s,%s\n" % ( build['version'], build['vercode'])) # This defines the preferred order for the build items - as in the # manual, they're roughly in order of application. keyorder = ['disable', 'commit', 'subdir', 'submodules', 'init', 'gradle', 'maven', 'oldsdkloc', 'target', 'compilesdk', 'update', 'encoding', 'forceversion', 'forcevercode', 'rm', 'fixtrans', 'fixapos', 'extlibs', 'srclibs', 'patch', 'prebuild', 'scanignore', 'scandelete', 'build', 'buildjni', 'preassemble', 'bindir', 'antcommand', 'novcheck'] def write_builditem(key, value): if key in ['version', 'vercode', 'origlines', 'type']: return if key in valuetypes['bool'].attrs: if not value: return value = 'yes' #if options.verbose: #print "...writing {0} : {1}".format(key, value) outline = ' %s=' % key outline += '&& \\\n '.join([s.lstrip() for s in value.split('&& ')]) outline += '\n' mf.write(outline) for key in keyorder: if key in build: write_builditem(key, build[key]) for key, value in build.iteritems(): if not key in keyorder: write_builditem(key, value) mf.write('\n') if 'Maintainer Notes' in app: writefield('Maintainer Notes', '') for line in app['Maintainer Notes']: mf.write("%s\n" % line) mf.write('.\n') mf.write('\n') if app['Archive Policy']: writefield('Archive Policy') writefield('Auto Update Mode') writefield('Update Check Mode') if app['Vercode Operation']: writefield('Vercode Operation') if 'Update Check Data' in app: writefield('Update Check Data') if app['Current Version']: writefield('Current Version') writefield('Current Version Code') mf.write('\n') if app['No Source Since']: writefield('No Source Since') mf.write('\n') writecomments(None) mf.close()