chiark / gitweb /
allow arbitrary build products, not only APKs
authorHans-Christoph Steiner <hans@eds.org>
Mon, 31 Oct 2016 18:53:55 +0000 (19:53 +0100)
committerHans-Christoph Steiner <hans@eds.org>
Mon, 7 Nov 2016 13:53:01 +0000 (14:53 +0100)
This makes it so that the final build product can be specified in output=
and it'll work no matter if its an APK or not.  This was developed around
the case of building the OTA update.zip for the Privileged Extension. It
should work for any build process in theory but it has not yet been tested.

https://gitlab.com/fdroid/privileged-extension/issues/9

fdroidserver/build.py
fdroidserver/common.py
fdroidserver/update.py
tests/common.TestCase

index 021c5da722167b0918aacadeddd511e41b9027a4..cbbed3cd987278abc8b9ca4a077383f43f44ad43 100644 (file)
@@ -454,6 +454,54 @@ def capitalize_intact(string):
     return string[0].upper() + string[1:]
 
 
+def get_metadata_from_apk(app, build, apkfile):
+    """get the required metadata from the built APK"""
+
+    p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
+
+    vercode = None
+    version = None
+    foundid = None
+    nativecode = None
+    for line in p.output.splitlines():
+        if line.startswith("package:"):
+            pat = re.compile(".*name='([a-zA-Z0-9._]*)'.*")
+            m = pat.match(line)
+            if m:
+                foundid = m.group(1)
+            pat = re.compile(".*versionCode='([0-9]*)'.*")
+            m = pat.match(line)
+            if m:
+                vercode = m.group(1)
+            pat = re.compile(".*versionName='([^']*)'.*")
+            m = pat.match(line)
+            if m:
+                version = m.group(1)
+        elif line.startswith("native-code:"):
+            nativecode = line[12:]
+
+    # Ignore empty strings or any kind of space/newline chars that we don't
+    # care about
+    if nativecode is not None:
+        nativecode = nativecode.strip()
+        nativecode = None if not nativecode else nativecode
+
+    if build.buildjni and build.buildjni != ['no']:
+        if nativecode is None:
+            raise BuildException("Native code should have been built but none was packaged")
+    if build.novcheck:
+        vercode = build.vercode
+        version = build.version
+    if not version or not vercode:
+        raise BuildException("Could not find version information in build in output")
+    if not foundid:
+        raise BuildException("Could not find package ID in output")
+    if foundid != app.id:
+        raise BuildException("Wrong package ID - build " + foundid + " but expected " + app.id)
+
+    return vercode, version
+
+
 def build_local(app, build, vcs, build_dir, output_dir, srclib_dir, extlib_dir, tmp_dir, force, onserver, refresh):
     """Do a build locally."""
 
@@ -809,7 +857,7 @@ def build_local(app, build, vcs, build_dir, output_dir, srclib_dir, extlib_dir,
         src = os.path.normpath(apks[0])
 
     # Make sure it's not debuggable...
-    if common.isApkDebuggable(src, config):
+    if common.isApkAndDebuggable(src, config):
         raise BuildException("APK is debuggable")
 
     # By way of a sanity check, make sure the version and version
@@ -818,56 +866,17 @@ def build_local(app, build, vcs, build_dir, output_dir, srclib_dir, extlib_dir,
     if not os.path.exists(src):
         raise BuildException("Unsigned apk is not at expected location of " + src)
 
-    p = SdkToolsPopen(['aapt', 'dump', 'badging', src], output=False)
-
-    vercode = None
-    version = None
-    foundid = None
-    nativecode = None
-    for line in p.output.splitlines():
-        if line.startswith("package:"):
-            pat = re.compile(".*name='([a-zA-Z0-9._]*)'.*")
-            m = pat.match(line)
-            if m:
-                foundid = m.group(1)
-            pat = re.compile(".*versionCode='([0-9]*)'.*")
-            m = pat.match(line)
-            if m:
-                vercode = m.group(1)
-            pat = re.compile(".*versionName='([^']*)'.*")
-            m = pat.match(line)
-            if m:
-                version = m.group(1)
-        elif line.startswith("native-code:"):
-            nativecode = line[12:]
-
-    # Ignore empty strings or any kind of space/newline chars that we don't
-    # care about
-    if nativecode is not None:
-        nativecode = nativecode.strip()
-        nativecode = None if not nativecode else nativecode
-
-    if build.buildjni and build.buildjni != ['no']:
-        if nativecode is None:
-            raise BuildException("Native code should have been built but none was packaged")
-    if build.novcheck:
+    if common.get_file_extension(src) == 'apk':
+        vercode, version = get_metadata_from_apk(app, build, src)
+        if (version != build.version or vercode != build.vercode):
+            raise BuildException(("Unexpected version/version code in output;"
+                                  " APK: '%s' / '%s', "
+                                  " Expected: '%s' / '%s'")
+                                 % (version, str(vercode), build.version,
+                                    str(build.vercode)))
+    else:
         vercode = build.vercode
         version = build.version
-    if not version or not vercode:
-        raise BuildException("Could not find version information in build in output")
-    if not foundid:
-        raise BuildException("Could not find package ID in output")
-    if foundid != app.id:
-        raise BuildException("Wrong package ID - build " + foundid + " but expected " + app.id)
-
-    if (version != build.version or
-            vercode != build.vercode):
-        raise BuildException(("Unexpected version/version code in output;"
-                              " APK: '%s' / '%s', "
-                              " Expected: '%s' / '%s'")
-                             % (version, str(vercode), build.version,
-                                str(build.vercode))
-                             )
 
     # Add information for 'fdroid verify' to be able to reproduce the build
     # environment.
index 2ef58460a8ebb9d7b93d20c5af3718351e8b749c..b653d5a87d09e67d686068cd7fafb2f0557cee7f 100644 (file)
@@ -1618,11 +1618,14 @@ def get_file_extension(filename):
     return os.path.splitext(filename)[1].lower()[1:]
 
 
-def isApkDebuggable(apkfile, config):
-    """Returns True if the given apk file is debuggable
+def isApkAndDebuggable(apkfile, config):
+    """Returns True if the given file is an APK and is debuggable
 
     :param apkfile: full path to the apk to check"""
 
+    if get_file_extension(apkfile) != 'apk':
+        return False
+
     p = SdkToolsPopen(['aapt', 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'],
                       output=False)
     if p.returncode != 0:
index 30390f825d1652663e5bb614901cfb0143205c6d..cebd5a929c3498b81fba287278a508dbd0dd0bdf 100644 (file)
@@ -739,7 +739,7 @@ def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False):
                 apk['minSdkVersion'] = 1
 
             # Check for debuggable apks...
-            if common.isApkDebuggable(apkfile, config):
+            if common.isApkAndDebuggable(apkfile, config):
                 logging.warning('{0} is set to android:debuggable="true"'.format(apkfile))
 
             # Get the signature (or md5 of, to be precise)...
index d7dca14ea50c0c5489fed28a722d3707d4958c25..d01568a2cab34934ac8084499e1a339d9962b928 100755 (executable)
@@ -74,7 +74,7 @@ class CommonTest(unittest.TestCase):
         testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip-badsig.apk'))
         testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip-badcert.apk'))
         for apkfile in testfiles:
-            debuggable = fdroidserver.common.isApkDebuggable(apkfile, config)
+            debuggable = fdroidserver.common.isApkAndDebuggable(apkfile, config)
             self.assertTrue(debuggable,
                             "debuggable APK state was not properly parsed!")
         # these are set NOT debuggable
@@ -82,7 +82,7 @@ class CommonTest(unittest.TestCase):
         testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip-release.apk'))
         testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip-release-unsigned.apk'))
         for apkfile in testfiles:
-            debuggable = fdroidserver.common.isApkDebuggable(apkfile, config)
+            debuggable = fdroidserver.common.isApkAndDebuggable(apkfile, config)
             self.assertFalse(debuggable,
                              "debuggable APK state was not properly parsed!")