chiark / gitweb /
find cmds from SDK build-tools in a more flexible way, on the fly
authorHans-Christoph Steiner <hans@eds.org>
Tue, 9 Dec 2014 13:12:41 +0000 (14:12 +0100)
committerHans-Christoph Steiner <hans@eds.org>
Sun, 14 Dec 2014 12:25:20 +0000 (13:25 +0100)
This is a more flexible approach than testing for the complete SDK and
build-tools up front.  This will only test for the commands that are
actually being run, so that if you only have `aapt` installed, you can do
`fdroid update` without errors, but other commands will still give
appropriate errors.

This also makes the build_tools item in config.py optional, it is only
needed if you want to force a specific version of the build-tools.

fdroidserver/build.py
fdroidserver/common.py
fdroidserver/init.py
fdroidserver/update.py
tests/common.TestCase
tests/run-tests

index 9c8acfa2ab846ad9e0354d87c00343aff6d58520..498cd4139464ca29160006e2628bba552bcb7949 100644 (file)
@@ -35,7 +35,7 @@ import logging
 
 import common
 import metadata
-from common import FDroidException, BuildException, VCSException, FDroidPopen, SilentPopen
+from common import FDroidException, BuildException, VCSException, FDroidPopen, SdkToolsPopen
 
 try:
     import paramiko
@@ -754,7 +754,7 @@ def build_local(app, thisbuild, vcs, build_dir, output_dir, srclib_dir, extlib_d
     if not os.path.exists(src):
         raise BuildException("Unsigned apk is not at expected location of " + src)
 
-    p = SilentPopen([config['aapt'], 'dump', 'badging', src])
+    p = SdkToolsPopen(['aapt', 'dump', 'badging', src])
 
     vercode = None
     version = None
index b16d0b6b859908c11950238fee006d97091a441a..8c87dd3f51c8dddf6bbd32e0e297caa1ea6a7292 100644 (file)
@@ -130,9 +130,6 @@ def read_config(opts, config_file='config.py'):
 
     fill_config_defaults(config)
 
-    if not test_build_tools_exists(config):
-        sys.exit(3)
-
     bin_paths = {
         'aapt': [
             os.path.join(config['sdk_path'], 'build-tools', config['build_tools'], 'aapt'),
@@ -194,7 +191,42 @@ def read_config(opts, config_file='config.py'):
     return config
 
 
+def find_sdk_tools_cmd(cmd):
+    '''find a working path to a tool from the Android SDK'''
+
+    tooldirs = []
+    if 'sdk_path' in config and os.path.exists(config['sdk_path']):
+        # try to find a working path to this command, in all the recent possible paths
+        if 'build_tools' in config:
+            build_tools = os.path.join(config['sdk_path'], 'build-tools')
+            # if 'build_tools' was manually set and exists, check only that one
+            configed_build_tools = os.path.join(build_tools, config['build_tools'])
+            if os.path.exists(configed_build_tools):
+                tooldirs.append(configed_build_tools)
+            else:
+                # no configed version, so hunt known paths for it
+                for f in sorted(os.listdir(build_tools), reverse=True):
+                    if os.path.isdir(os.path.join(build_tools, f)):
+                        tooldirs.append(os.path.join(build_tools, f))
+                tooldirs.append(build_tools)
+        sdk_tools = os.path.join(config['sdk_path'], 'tools')
+        if os.path.exists(sdk_tools):
+            tooldirs.append(sdk_tools)
+        sdk_platform_tools = os.path.join(config['sdk_path'], 'platform-tools')
+        if os.path.exists(sdk_platform_tools):
+            tooldirs.append(sdk_platform_tools)
+    tooldirs.append('/usr/bin')
+    for d in tooldirs:
+        if os.path.isfile(os.path.join(d, cmd)):
+            return os.path.join(d, cmd)
+    # did not find the command, exit with error message
+    ensure_build_tools_exists(config)
+
+
 def test_sdk_exists(thisconfig):
+    if 'sdk_path' not in thisconfig:
+        logging.error("'sdk_path' not set in config.py!")
+        return False
     if thisconfig['sdk_path'] == default_config['sdk_path']:
         logging.error('No Android SDK found!')
         logging.error('You can use ANDROID_HOME to set the path to your SDK, i.e.:')
@@ -214,16 +246,15 @@ def test_sdk_exists(thisconfig):
     return True
 
 
-def test_build_tools_exists(thisconfig):
+def ensure_build_tools_exists(thisconfig):
     if not test_sdk_exists(thisconfig):
-        return False
+        sys.exit(3)
     build_tools = os.path.join(thisconfig['sdk_path'], 'build-tools')
     versioned_build_tools = os.path.join(build_tools, thisconfig['build_tools'])
     if not os.path.isdir(versioned_build_tools):
         logging.critical('Android Build Tools path "'
                          + versioned_build_tools + '" does not exist!')
-        return False
-    return True
+        sys.exit(3)
 
 
 def write_password_file(pwtype, password=None):
@@ -1597,8 +1628,7 @@ def isApkDebuggable(apkfile, config):
 
     :param apkfile: full path to the apk to check"""
 
-    p = SilentPopen([config['aapt'],
-                     'dump', 'xmltree', apkfile, 'AndroidManifest.xml'])
+    p = SdkToolsPopen(['aapt', 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'])
     if p.returncode != 0:
         logging.critical("Failed to get apk manifest information")
         sys.exit(1)
@@ -1637,6 +1667,14 @@ class PopenResult:
     output = ''
 
 
+def SdkToolsPopen(commands, cwd=None, shell=False):
+    cmd = commands[0]
+    if cmd not in config:
+        config[cmd] = find_sdk_tools_cmd(commands[0])
+    return FDroidPopen([config[cmd]] + commands[1:],
+                       cwd=cwd, shell=shell, output=False)
+
+
 def SilentPopen(commands, cwd=None, shell=False):
     return FDroidPopen(commands, cwd=cwd, shell=shell, output=False)
 
index 6f96a24b29843f3187a7a518382b107ed2f76416..98c9081754d8478bc56f1800e6dd4d751d0f4666 100644 (file)
@@ -168,28 +168,31 @@ def main():
         logging.info('Try running `fdroid init` in an empty directory.')
         sys.exit()
 
-    # try to find a working aapt, in all the recent possible paths
-    build_tools = os.path.join(test_config['sdk_path'], 'build-tools')
-    aaptdirs = []
-    aaptdirs.append(os.path.join(build_tools, test_config['build_tools']))
-    aaptdirs.append(build_tools)
-    for f in os.listdir(build_tools):
-        if os.path.isdir(os.path.join(build_tools, f)):
-            aaptdirs.append(os.path.join(build_tools, f))
-    for d in sorted(aaptdirs, reverse=True):
-        if os.path.isfile(os.path.join(d, 'aapt')):
-            aapt = os.path.join(d, 'aapt')
-            break
-    if os.path.isfile(aapt):
-        dirname = os.path.basename(os.path.dirname(aapt))
-        if dirname == 'build-tools':
-            # this is the old layout, before versioned build-tools
-            test_config['build_tools'] = ''
-        else:
-            test_config['build_tools'] = dirname
-        write_to_config(test_config, 'build_tools')
-    if not common.test_build_tools_exists(test_config):
-        sys.exit(3)
+    if os.path.exists('/usr/bin/aapt'):
+        # make sure at least aapt is found, since this can't do anything without it
+        config['aapt'] = common.find_sdk_tools_cmd('aapt')
+    else:
+        # try to find a working aapt, in all the recent possible paths
+        build_tools = os.path.join(test_config['sdk_path'], 'build-tools')
+        aaptdirs = []
+        aaptdirs.append(os.path.join(build_tools, test_config['build_tools']))
+        aaptdirs.append(build_tools)
+        for f in os.listdir(build_tools):
+            if os.path.isdir(os.path.join(build_tools, f)):
+                aaptdirs.append(os.path.join(build_tools, f))
+        for d in sorted(aaptdirs, reverse=True):
+            if os.path.isfile(os.path.join(d, 'aapt')):
+                aapt = os.path.join(d, 'aapt')
+                break
+        if os.path.isfile(aapt):
+            dirname = os.path.basename(os.path.dirname(aapt))
+            if dirname == 'build-tools':
+                # this is the old layout, before versioned build-tools
+                test_config['build_tools'] = ''
+            else:
+                test_config['build_tools'] = dirname
+            write_to_config(test_config, 'build_tools')
+        common.ensure_build_tools_exists(test_config)
 
     # now that we have a local config.py, read configuration...
     config = common.read_config(options)
index 0339f4606fb1c4119d4cc53db3e48bf155be4e9c..eb04ddbb8d9e93a0b9ee49a959da6d0ffa9206d2 100644 (file)
@@ -39,7 +39,7 @@ import logging
 
 import common
 import metadata
-from common import FDroidPopen, SilentPopen
+from common import FDroidPopen, SdkToolsPopen
 from metadata import MetaDataException
 
 
@@ -436,7 +436,7 @@ def scan_apks(apps, apkcache, repodir, knownapks):
             thisinfo['features'] = set()
             thisinfo['icons_src'] = {}
             thisinfo['icons'] = {}
-            p = SilentPopen([config['aapt'], 'dump', 'badging', apkfile])
+            p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile])
             if p.returncode != 0:
                 if options.delete_unknown:
                     if os.path.exists(apkfile):
index d4bbd61bded2d4402f699ab28a1cb4a80c01c0f9..d206637856b8efa8453e4acc95cc208deacc2a4d 100755 (executable)
@@ -21,9 +21,50 @@ import fdroidserver.common
 class CommonTest(unittest.TestCase):
     '''fdroidserver/common.py'''
 
+    def _set_build_tools(self):
+        build_tools = os.path.join(fdroidserver.common.config['sdk_path'], 'build-tools')
+        if os.path.exists(build_tools):
+            fdroidserver.common.config['build_tools'] = ''
+            for f in sorted(os.listdir(build_tools), reverse=True):
+                versioned = os.path.join(build_tools, f)
+                if os.path.isdir(versioned) \
+                        and os.path.isfile(os.path.join(versioned, 'aapt')):
+                    fdroidserver.common.config['build_tools'] = versioned
+                    break
+            return True
+        else:
+            print 'no build-tools found: ' + build_tools
+            return False
+
+    def _find_all(self):
+        for cmd in ('aapt', 'adb', 'android', 'zipalign'):
+            path = fdroidserver.common.find_sdk_tools_cmd(cmd)
+            if path is not None:
+                self.assertTrue(os.path.exists(path))
+                self.assertTrue(os.path.isfile(path))
+
+    def test_find_sdk_tools_cmd(self):
+        fdroidserver.common.config = dict()
+        # TODO add this once everything works without sdk_path set in config
+        #self._find_all()
+        sdk_path = os.getenv('ANDROID_HOME')
+        if os.path.exists(sdk_path):
+            fdroidserver.common.config['sdk_path'] = sdk_path
+            if os.path.exists('/usr/bin/aapt'):
+                # this test only works when /usr/bin/aapt is installed
+                self._find_all()
+            build_tools = os.path.join(sdk_path, 'build-tools')
+            if self._set_build_tools():
+                self._find_all()
+            else:
+                print 'no build-tools found: ' + build_tools
+
     def testIsApkDebuggable(self):
         config = dict()
-        config['aapt'] = '/usr/bin/aapt'
+        config['sdk_path'] = os.getenv('ANDROID_HOME')
+        fdroidserver.common.config = config
+        self._set_build_tools();
+        config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt')
         # these are set debuggable
         testfiles = []
         testfiles.append(os.path.join(os.path.dirname(__file__), 'urzip.apk'))
index 486bb3ea605fad6fbff4c3280ac38ef8d871e313..3ea444376432668745152a5db858fc3572cb4740 100755 (executable)
@@ -209,16 +209,20 @@ $fdroid init --keystore $KEYSTORE --android-home $FAKE_ANDROID_HOME
 #------------------------------------------------------------------------------#
 echo_header "check that 'fdroid init' fails when build-tools cannot be found"
 
-REPOROOT=`create_test_dir`
-FAKE_ANDROID_HOME=`create_test_dir`
-create_fake_android_home $FAKE_ANDROID_HOME
-rm -f $FAKE_ANDROID_HOME/build-tools/*/aapt
-KEYSTORE=$REPOROOT/keystore.jks
-cd $REPOROOT
-set +e
-$fdroid init --keystore $KEYSTORE --android-home $FAKE_ANDROID_HOME
-[ $? -eq 0 ] && exit 1
-set -e
+if [ -e /usr/bin/aapt ]; then
+    echo "/usr/bin/aapt exists, not running test"
+else
+    REPOROOT=`create_test_dir`
+    FAKE_ANDROID_HOME=`create_test_dir`
+    create_fake_android_home $FAKE_ANDROID_HOME
+    rm -f $FAKE_ANDROID_HOME/build-tools/*/aapt
+    KEYSTORE=$REPOROOT/keystore.jks
+    cd $REPOROOT
+    set +e
+    $fdroid init --keystore $KEYSTORE --android-home $FAKE_ANDROID_HOME
+    [ $? -eq 0 ] && exit 1
+    set -e
+fi
 
 
 #------------------------------------------------------------------------------#