chiark / gitweb /
Read new or old recipe formats, write new format
authorCiaran Gultnieks <ciaran@ciarang.com>
Sun, 27 Oct 2013 14:06:46 +0000 (14:06 +0000)
committerCiaran Gultnieks <ciaran@ciarang.com>
Sun, 27 Oct 2013 14:06:55 +0000 (14:06 +0000)
Slightly tested. See https://f-droid.org/wiki/page/Build_Recipe_Format

docs/fdroid.texi
fdroidserver/common.py
fdroidserver/rewritemeta.py

index d917662bb9856a6220f54d25d61428705a416a4a..f57985690b2111be3ddb1df9261333071a28b365 100644 (file)
@@ -1068,7 +1068,7 @@ gradle build system and store version info in a separate file; if the
 developers make a new branch for each release and don't make tags; or if you've 
 changed the package name or version code logic.
 @item
-@code{Static} - No checking is doneeither development has ceased or new versions
+@code{Static} - No checking is done - either development has ceased or new versions
 are not desired. This method is also used when there is no other checking method 
 available and the upstream developer keeps us posted on new versions.
 @item
index 027ff1a5afe555de28a318805c0687e03c6e3223..5e2d58bdadfe0b56881e1de0f8f2646820824861 100644 (file)
@@ -424,6 +424,8 @@ def metafieldtype(name):
         return 'flag'
     if name == 'Build Version':
         return 'build'
+    if name == 'Build':
+        return 'buildv2'
     if name == 'Use Built':
         return 'obsolete'
     return 'string'
@@ -454,7 +456,7 @@ def metafieldtype(name):
 #  'descriptionlines' - original lines of description as formatted in the
 #                       metadata file.
 #
-def parse_metadata(metafile, **kw):
+def parse_metadata(metafile, verbose=False):
 
     def parse_buildline(lines):
         value = "".join(lines)
@@ -477,6 +479,8 @@ def parse_metadata(metafile, **kw):
         return thisbuild
 
     def add_comments(key):
+        if len(curcomments) == 0:
+            return
         for comment in curcomments:
             thisinfo['comments'].append((key, comment))
         del curcomments[:]
@@ -525,9 +529,35 @@ def parse_metadata(metafile, **kw):
     mode = 0
     buildlines = []
     curcomments = []
+    curbuild = None
 
     for line in metafile:
         line = line.rstrip('\r\n')
+        if mode == 3:
+            if not line.startswith(' '):
+                if 'commit' not in curbuild:
+                    raise MetaDataException("No commit specified for {0} in {1}".format(
+                        curbuild['version'], metafile.name))
+                thisinfo['builds'].append(curbuild)
+                add_comments('build:' + curbuild['version'])
+                mode = 0
+            else:
+                if line.endswith('\\'):
+                    buildlines.append(line[:-1].lstrip())
+                else:
+                    buildlines.append(line.lstrip())
+                    bl = ''.join(buildlines)
+                    bv = bl.split('=', 1)
+                    if len(bv) != 2:
+                        raise MetaDataException("Invalid build flag at {0} in {1}".
+                                format(buildlines[0], metafile.name))
+                    name, val = bv
+                    if name in curbuild:
+                        raise MetaDataException("Duplicate definition on {0} in version {1} of {2}".
+                                format(name, curbuild['version'], metafile.name))
+                    curbuild[name] = val.lstrip()
+                    buildlines = []
+
         if mode == 0:
             if len(line) == 0:
                 continue
@@ -547,7 +577,7 @@ def parse_metadata(metafile, **kw):
                 field = 'Current Version Code'
 
             fieldtype = metafieldtype(field)
-            if fieldtype != 'build':
+            if fieldtype not in ['build', 'buildv2']:
                 add_comments(field)
             if fieldtype == 'multiline':
                 mode = 1
@@ -570,6 +600,20 @@ def parse_metadata(metafile, **kw):
                 else:
                     thisinfo['builds'].append(parse_buildline([value]))
                     add_comments('build:' + thisinfo['builds'][-1]['version'])
+            elif fieldtype == 'buildv2':
+                curbuild = {}
+                vv = value.split(',')
+                if len(vv) != 2:
+                    raise MetaDataException('Build should have comma-separated version and vercode, not "{0}", in {1}'.
+                        format(value, metafile.name))
+                curbuild['version'] = vv[0]
+                curbuild['vercode'] = vv[1]
+                try:
+                    testvercode = int(curbuild['vercode'])
+                except:
+                    raise MetaDataException("Invalid version code for build in " + metafile.name)
+                buildlines = []
+                mode = 3
             elif fieldtype == 'obsolete':
                 pass        # Just throw it away!
             else:
@@ -595,6 +639,8 @@ def parse_metadata(metafile, **kw):
         raise MetaDataException(field + " not terminated in " + metafile.name)
     elif mode == 2:
         raise MetaDataException("Unterminated continuation in " + metafile.name)
+    elif mode == 3:
+        raise MetaDataException("Unterminated build in " + metafile.name)
 
     if len(thisinfo['Description']) == 0:
         thisinfo['Description'].append('No description available')
@@ -637,12 +683,16 @@ def getsrcname(app, build):
 #
 # 'dest'    - The path to the output file
 # 'app'     - The app data
-def write_metadata(dest, app):
+def write_metadata(dest, app, verbose=False):
 
     def writecomments(key):
+        written = 0
         for pf, comment in app['comments']:
             if pf == key:
                 mf.write(comment + '\n')
+                written += 1
+        if verbose and written > 0:
+            print "...writing comments for " + (key if key else 'EOF')
 
     def writefield(field, value=None):
         writecomments(field)
@@ -671,7 +721,8 @@ def write_metadata(dest, app):
     mf.write('\n')
     if app['Name']:
         writefield('Name')
-    writefield('Auto Name')
+    if len(app['Auto Name']) > 0:
+        writefield('Auto Name')
     writefield('Summary')
     writefield('Description', '')
     for line in app['Description']:
@@ -685,24 +736,40 @@ def write_metadata(dest, app):
         writefield('Repo Type')
         writefield('Repo')
         mf.write('\n')
-    keystoignore = ['version', 'vercode', 'subvercode', 'commit']
     for build in app['builds']:
         writecomments('build:' + build['version'])
-        mf.write('Build Version:')
-        if 'origlines' in build:
-            # Keeping the original formatting if we loaded it from a file...
-            mf.write('\\\n'.join(build['origlines']) + '\n')
-        else:
-            mf.write("%s,%s,%s" % (
-                build['version'],
-                getvercode(build),
-                build['commit']))
-            for key,value in build.iteritems():
-                if key not in keystoignore:
-                    mf.write(',' + key + '=' + value)
-            mf.write('\n')
-    if len(app['builds']) > 0:
+        mf.write('Build:')
+        mf.write("%s,%s\n" % (
+            build['version'],
+            getvercode(build)))
+
+        # 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',
+                    'oldsdkloc', 'target', 'compilesdk', 'update',
+                    'encoding', 'forceversion', 'forcevercode', 'rm',
+                    'fixtrans', 'fixapos', 'extlibs', 'srclibs',
+                    'patch', 'prebuild', 'initfun', 'scanignore', 'build',
+                    'buildjni', 'gradle', 'maven', 'preassemble',
+                    'bindir', 'antcommand', 'novcheck']
+
+        def write_builditem(key, value):
+            if key not in ['version', 'vercode', 'origlines']:
+                if verbose:
+                    print "...writing {0} : {1}".format(key, value)
+                outline = '    ' + key + '='
+                bits = value.split('&& ')
+                outline += '&& \\\n        '.join([s.lstrip() for s in bits])
+                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 app['Archive Policy']:
         writefield('Archive Policy')
     writefield('Auto Update Mode')
@@ -722,14 +789,15 @@ def write_metadata(dest, app):
 
 # Read all metadata. Returns a list of 'app' objects (which are dictionaries as
 # returned by the parse_metadata function.
-def read_metadata(verbose=False, xref=True):
+def read_metadata(verbose=False, xref=True, package=None):
     apps = []
     for metafile in sorted(glob.glob(os.path.join('metadata', '*.txt'))):
-        try:
-            appinfo = parse_metadata(metafile, verbose=verbose)
-        except Exception, e:
-            raise MetaDataException("Problem reading metadata file %s: - %s" % (metafile, str(e)))
-        apps.append(appinfo)
+        if package is None or metafile == os.path.join('metadata', package + '.txt'):
+            try:
+                appinfo = parse_metadata(metafile, verbose=verbose)
+            except Exception, e:
+                raise MetaDataException("Problem reading metadata file %s: - %s" % (metafile, str(e)))
+            apps.append(appinfo)
 
     if xref:
         # Parse all descriptions at load time, just to ensure cross-referencing
index 38e318edddaf4417a26ffd0b578f4871ca5d17d9..1f488af31cb2b10d5ca8ecd65865cb57e7b11ae6 100644 (file)
@@ -32,22 +32,19 @@ def main():
     parser.add_option("-v", "--verbose", action="store_true", default=False,
                       help="Spew out even more information than normal")
     parser.add_option("-p", "--package", default=None,
-                      help="Build only the specified package")
+                      help="Process only the specified package")
     (options, args) = parser.parse_args()
 
     # Get all apps...
-    apps = common.read_metadata(options.verbose)
+    apps = common.read_metadata(options.verbose, package=options.package)
 
-    # Filter apps according to command-line options
-    if options.package:
-        apps = [app for app in apps if app['id'] == options.package]
-        if len(apps) == 0:
-            print "No such package"
-            sys.exit(1)
+    if len(apps) == 0 and options.package:
+        print "No such package"
+        sys.exit(1)
 
     for app in apps:
         print "Writing " + app['id']
-        common.write_metadata(os.path.join('metadata', app['id']) + '.txt', app)
+        common.write_metadata(os.path.join('metadata', app['id']) + '.txt', app, verbose=options.verbose)
 
     print "Finished."