chiark / gitweb /
handle gradle-plugin 3.0 output apk location
[fdroidserver.git] / fdroidserver / build.py
index 8913136060b5042d3ded37d735656e23bd155940..c43b18fdeac25f9d179be5c280c18aa190a8ba79 100644 (file)
@@ -136,8 +136,8 @@ def build_server(app, build, vcs, build_dir, output_dir, log_dir, force):
         ftp.mkdir('metadata')
         ftp.mkdir('srclibs')
         ftp.chdir('metadata')
-        ftp.put(os.path.join('metadata', app.id + '.txt'),
-                app.id + '.txt')
+        ftp.put(app.metadatapath, os.path.basename(app.metadatapath))
+
         # And patches if there are any...
         if os.path.exists(os.path.join('metadata', app.id)):
             send_dir(os.path.join('metadata', app.id))
@@ -275,14 +275,13 @@ def force_gradle_build_tools(build_dir, build_tools):
                                path)
 
 
-def capitalize_intact(string):
-    """Like str.capitalize(), but leave the rest of the string intact without
-    switching it to lowercase."""
+def transform_first_char(string, method):
+    """Uses method() on the first character of string."""
     if len(string) == 0:
         return string
     if len(string) == 1:
-        return string.upper()
-    return string[0].upper() + string[1:]
+        return method(string)
+    return method(string[0]) + string[1:]
 
 
 def has_native_code(apkobj):
@@ -393,10 +392,23 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
 
     # create ..._toolsversion.log when running in builder vm
     if onserver:
+        # before doing anything, run the sudo commands to setup the VM
+        if build.sudo:
+            logging.info("Running 'sudo' commands in %s" % os.getcwd())
+
+            p = FDroidPopen(['sudo', 'bash', '-x', '-c', build.sudo])
+            if p.returncode != 0:
+                raise BuildException("Error running sudo command for %s:%s" %
+                                     (app.id, build.versionName), p.output)
+
         log_path = os.path.join(log_dir,
                                 common.get_toolsversion_logname(app, build))
         with open(log_path, 'w') as f:
             f.write(get_android_tools_version_log(build.ndk_path()))
+    else:
+        if build.sudo:
+            logging.warning('%s:%s runs this on the buildserver with sudo:\n\t%s'
+                            % (app.id, build.versionName, build.sudo))
 
     # Prepare the source code...
     root_dir, srclibpaths = common.prepare_source(vcs, app, build,
@@ -431,7 +443,7 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
         if flavours == ['yes']:
             flavours = []
 
-        flavours_cmd = ''.join([capitalize_intact(flav) for flav in flavours])
+        flavours_cmd = ''.join([transform_first_char(flav, str.upper) for flav in flavours])
 
         gradletasks += ['assemble' + flavours_cmd + 'Release']
 
@@ -451,6 +463,9 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
     elif bmethod == 'kivy':
         pass
 
+    elif bmethod == 'buildozer':
+        pass
+
     elif bmethod == 'ant':
         logging.info("Cleaning Ant project...")
         p = FDroidPopen(['ant', 'clean'], cwd=root_dir)
@@ -494,7 +509,7 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
     else:
         # Scan before building...
         logging.info("Scanning source for common problems...")
-        count = scanner.scan_source(build_dir, root_dir, build)
+        count = scanner.scan_source(build_dir, build)
         if count > 0:
             if force:
                 logging.warn('Scanner found %d problems' % count)
@@ -645,6 +660,73 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
         cmd.append('release')
         p = FDroidPopen(cmd, cwd=distdir)
 
+    elif bmethod == 'buildozer':
+        logging.info("Building Kivy project using buildozer...")
+
+        # parse buildozer.spez
+        spec = os.path.join(root_dir, 'buildozer.spec')
+        if not os.path.exists(spec):
+            raise BuildException("Expected to find buildozer-compatible spec at {0}"
+                                 .format(spec))
+        defaults = {'orientation': 'landscape', 'icon': '',
+                    'permissions': '', 'android.api': "19"}
+        bconfig = ConfigParser(defaults, allow_no_value=True)
+        bconfig.read(spec)
+
+        # update spec with sdk and ndk locations to prevent buildozer from
+        # downloading.
+        loc_ndk = common.env['ANDROID_NDK']
+        loc_sdk = common.env['ANDROID_SDK']
+        if loc_ndk == '$ANDROID_NDK':
+            loc_ndk = loc_sdk + '/ndk-bundle'
+
+        bc_ndk = None
+        bc_sdk = None
+        try:
+            bc_ndk = bconfig.get('app', 'android.sdk_path')
+        except Exception:
+            pass
+        try:
+            bc_sdk = bconfig.get('app', 'android.ndk_path')
+        except Exception:
+            pass
+
+        if bc_sdk is None:
+            bconfig.set('app', 'android.sdk_path', loc_sdk)
+        if bc_ndk is None:
+            bconfig.set('app', 'android.ndk_path', loc_ndk)
+
+        fspec = open(spec, 'w')
+        bconfig.write(fspec)
+        fspec.close()
+
+        logging.info("sdk_path = %s" % loc_sdk)
+        logging.info("ndk_path = %s" % loc_ndk)
+
+        p = None
+        # execute buildozer
+        cmd = ['buildozer', 'android', 'release']
+        try:
+            p = FDroidPopen(cmd, cwd=root_dir)
+        except Exception:
+            pass
+
+        # buidozer not installed ? clone repo and run
+        if (p is None or p.returncode != 0):
+            cmd = ['git', 'clone', 'https://github.com/kivy/buildozer.git']
+            p = subprocess.Popen(cmd, cwd=root_dir, shell=False)
+            p.wait()
+            if p.returncode != 0:
+                raise BuildException("Distribute build failed")
+
+            cmd = ['python', 'buildozer/buildozer/scripts/client.py', 'android', 'release']
+            p = FDroidPopen(cmd, cwd=root_dir)
+
+        # expected to fail.
+        # Signing will fail if not set by environnment vars (cf. p4a docs).
+        # But the unsigned apk will be ok.
+        p.returncode = 0
+
     elif bmethod == 'gradle':
         logging.info("Building Gradle project...")
 
@@ -697,11 +779,11 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
                            '{0}-{1}-release.apk'.format(
                                bconfig.get('app', 'title'),
                                bconfig.get('app', 'version')))
-    elif omethod == 'gradle':
+
+    elif omethod == 'buildozer':
         src = None
         for apks_dir in [
-                os.path.join(root_dir, 'build', 'outputs', 'apk'),
-                os.path.join(root_dir, 'build', 'apk'),
+                os.path.join(root_dir, '.buildozer', 'android', 'platform', 'build', 'dists', bconfig.get('app', 'title'), 'bin'),
                 ]:
             for apkglob in ['*-release-unsigned.apk', '*-unsigned.apk', '*.apk']:
                 apks = glob.glob(os.path.join(apks_dir, apkglob))
@@ -718,6 +800,31 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
         if src is None:
             raise BuildException('Failed to find any output apks')
 
+    elif omethod == 'gradle':
+        src = None
+        apk_dirs = [
+            os.path.join(root_dir, 'build', 'outputs', 'apk', 'release'),
+            os.path.join(root_dir, 'build', 'outputs', 'apk'),
+            os.path.join(root_dir, 'build', 'apk'),
+            ]
+        if flavours_cmd:
+            apk_dirs.append(os.path.join(root_dir, 'build', 'outputs', 'apk', transform_first_char(flavours_cmd, str.lower), 'release'))
+        for apks_dir in apk_dirs:
+            for apkglob in ['*-release-unsigned.apk', '*-unsigned.apk', '*.apk']:
+                apks = glob.glob(os.path.join(apks_dir, apkglob))
+
+                if len(apks) > 1:
+                    raise BuildException('More than one resulting apks found in %s' % apks_dir,
+                                         '\n'.join(apks))
+                if len(apks) == 1:
+                    src = apks[0]
+                    break
+            if src is not None:
+                break
+
+        if src is None:
+            raise BuildException('Failed to find any output apks')
+
     elif omethod == 'ant':
         stdout_apk = '\n'.join([
             line for line in p.output.splitlines() if '.apk' in line])
@@ -735,7 +842,7 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
         src = os.path.normpath(apks[0])
 
     # Make sure it's not debuggable...
-    if common.isApkAndDebuggable(src, config):
+    if common.isApkAndDebuggable(src):
         raise BuildException("APK is debuggable")
 
     # By way of a sanity check, make sure the version and version
@@ -1053,7 +1160,7 @@ def main():
                         url = url.replace('%v', build.versionName)
                         url = url.replace('%c', str(build.versionCode))
                         logging.info("...retrieving " + url)
-                        of = common.get_release_filename(app, build) + '.binary'
+                        of = re.sub(r'.apk$', '.binary.apk', common.get_release_filename(app, build))
                         of = os.path.join(output_dir, of)
                         try:
                             net.download_file(url, local_filename=of)