chiark / gitweb /
Merge branch 'master' into logging
[fdroidserver.git] / fdroidserver / common.py
index b348af1f1836077885f7600935a36df97f4976ab..9469f77645934b2dc0b8e1dc498f7ff24c275859 100644 (file)
@@ -26,6 +26,7 @@ import operator
 import Queue
 import threading
 import magic
+import logging
 
 import metadata
 
@@ -43,12 +44,10 @@ def read_config(opts, config_file='config.py'):
     if config is not None:
         return config
     if not os.path.isfile(config_file):
-        print "Missing config file - is this a repo directory?"
+        logging.critical("Missing config file - is this a repo directory?")
         sys.exit(2)
 
     options = opts
-    if not hasattr(options, 'verbose'):
-        options.verbose = False
 
     defconfig = {
         'sdk_path': "$ANDROID_HOME",
@@ -69,14 +68,13 @@ def read_config(opts, config_file='config.py'):
     }
     config = {}
 
-    if options.verbose:
-        print "Reading %s..." % config_file
+    logging.info("Reading %s" % config_file)
     execfile(config_file, config)
 
     if any(k in config for k in ["keystore", "keystorepass", "keypass"]):
         st = os.stat(config_file)
         if st.st_mode & stat.S_IRWXG or st.st_mode & stat.S_IRWXO:
-            print "WARNING: unsafe permissions on {0} (should be 0600)!".format(config_file)
+            logging.warn("unsafe permissions on {0} (should be 0600)!".format(config_file))
 
     for k, v in defconfig.items():
         if k not in config:
@@ -129,7 +127,7 @@ def read_app_args(args, allapps, allow_vercodes=False):
         allids = [app["id"] for app in allapps]
         for p in vercodes:
             if p not in allids:
-                print "No such package: %s" % p
+                logging.critical("No such package: %s" % p)
         raise Exception("Found invalid app ids in arguments")
 
     error = False
@@ -143,7 +141,7 @@ def read_app_args(args, allapps, allow_vercodes=False):
             allvcs = [b['vercode'] for b in app['builds']]
             for v in vercodes[app['id']]:
                 if v not in allvcs:
-                    print "No such vercode %s for app %s" % (v, app['id'])
+                    logging.critical("No such vercode %s for app %s" % (v, app['id']))
 
     if error:
         raise Exception("Found invalid vercodes for some apps")
@@ -250,10 +248,10 @@ class vcs:
                     writeback = False
                 else:
                     deleterepo = True
-                    print "*** Repository details changed - deleting ***"
+                    logging.info("Repository details changed - deleting")
             else:
                 deleterepo = True
-                print "*** Repository details missing - deleting ***"
+                logging.info("Repository details missing - deleting")
         if deleterepo:
             shutil.rmtree(self.local)
 
@@ -296,29 +294,28 @@ class vcs_git(vcs):
     # fdroidserver) and then we'll proceed to destroy it! This is called as
     # a safety check.
     def checkrepo(self):
-        p = subprocess.Popen(['git', 'rev-parse', '--show-toplevel'],
-                stdout=subprocess.PIPE, cwd=self.local)
-        result = p.communicate()[0].rstrip()
+        p = FDroidPopen(['git', 'rev-parse', '--show-toplevel'], cwd=self.local)
+        result = p.stdout.rstrip()
         if not result.endswith(self.local):
             raise VCSException('Repository mismatch')
 
     def gotorevisionx(self, rev):
         if not os.path.exists(self.local):
-            # Brand new checkout...
+            # Brand new checkout
             if subprocess.call(['git', 'clone', self.remote, self.local]) != 0:
                 raise VCSException("Git clone failed")
             self.checkrepo()
         else:
             self.checkrepo()
-            # Discard any working tree changes...
+            # Discard any working tree changes
             if subprocess.call(['git', 'reset', '--hard'], cwd=self.local) != 0:
                 raise VCSException("Git reset failed")
             # Remove untracked files now, in case they're tracked in the target
-            # revision (it happens!)...
+            # revision (it happens!)
             if subprocess.call(['git', 'clean', '-dffx'], cwd=self.local) != 0:
                 raise VCSException("Git clean failed")
             if not self.refreshed:
-                # Get latest commits and tags from remote...
+                # Get latest commits and tags from remote
                 if subprocess.call(['git', 'fetch', 'origin'],
                         cwd=self.local) != 0:
                     raise VCSException("Git fetch failed")
@@ -326,11 +323,11 @@ class vcs_git(vcs):
                         cwd=self.local) != 0:
                     raise VCSException("Git fetch failed")
                 self.refreshed = True
-        # Check out the appropriate revision...
+        # Check out the appropriate revision
         rev = str(rev if rev else 'origin/master')
         if subprocess.call(['git', 'checkout', '-f', rev], cwd=self.local) != 0:
             raise VCSException("Git checkout failed")
-        # Get rid of any uncontrolled files left behind...
+        # Get rid of any uncontrolled files left behind
         if subprocess.call(['git', 'clean', '-dffx'], cwd=self.local) != 0:
             raise VCSException("Git clean failed")
 
@@ -351,9 +348,8 @@ class vcs_git(vcs):
 
     def gettags(self):
         self.checkrepo()
-        p = subprocess.Popen(['git', 'tag'],
-                stdout=subprocess.PIPE, cwd=self.local)
-        return p.communicate()[0].splitlines()
+        p = FDroidPopen(['git', 'tag'], cwd=self.local)
+        return p.stdout.splitlines()
 
 
 class vcs_gitsvn(vcs):
@@ -373,15 +369,14 @@ class vcs_gitsvn(vcs):
     # fdroidserver) and then we'll proceed to destory it! This is called as
     # a safety check.
     def checkrepo(self):
-        p = subprocess.Popen(['git', 'rev-parse', '--show-toplevel'],
-                stdout=subprocess.PIPE, cwd=self.local)
-        result = p.communicate()[0].rstrip()
+        p = FDroidPopen(['git', 'rev-parse', '--show-toplevel'], cwd=self.local)
+        result = p.stdout.rstrip()
         if not result.endswith(self.local):
             raise VCSException('Repository mismatch')
 
     def gotorevisionx(self, rev):
         if not os.path.exists(self.local):
-            # Brand new checkout...
+            # Brand new checkout
             gitsvn_cmd = '%sgit svn clone %s' % self.userargs()
             if ';' in self.remote:
                 remote_split = self.remote.split(';')
@@ -402,15 +397,15 @@ class vcs_gitsvn(vcs):
             self.checkrepo()
         else:
             self.checkrepo()
-            # Discard any working tree changes...
+            # Discard any working tree changes
             if subprocess.call(['git', 'reset', '--hard'], cwd=self.local) != 0:
                 raise VCSException("Git reset failed")
             # Remove untracked files now, in case they're tracked in the target
-            # revision (it happens!)...
+            # revision (it happens!)
             if subprocess.call(['git', 'clean', '-dffx'], cwd=self.local) != 0:
                 raise VCSException("Git clean failed")
             if not self.refreshed:
-                # Get new commits and tags from repo...
+                # Get new commits and tags from repo
                 if subprocess.call(['%sgit svn rebase %s' % self.userargs()],
                         cwd=self.local, shell=True) != 0:
                     raise VCSException("Git svn rebase failed")
@@ -420,12 +415,8 @@ class vcs_gitsvn(vcs):
         if rev:
             nospaces_rev = rev.replace(' ', '%20')
             # Try finding a svn tag
-            p = subprocess.Popen(['git', 'checkout', 'tags/' + nospaces_rev],
-                    cwd=self.local, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-            out, err = p.communicate()
-            if p.returncode == 0:
-                print out
-            else:
+            p = FDroidPopen(['git', 'checkout', 'tags/' + nospaces_rev], cwd=self.local)
+            if p.returncode != 0:
                 # No tag found, normal svn rev translation
                 # Translate svn rev into git format
                 rev_split = rev.split('/')
@@ -439,30 +430,22 @@ class vcs_gitsvn(vcs):
                     treeish = 'master'
                     svn_rev = rev
 
-                p = subprocess.Popen(['git', 'svn', 'find-rev', 'r' + svn_rev, treeish],
-                    cwd=self.local, stdout=subprocess.PIPE)
-                git_rev = p.communicate()[0].rstrip()
+                p = FDroidPopen(['git', 'svn', 'find-rev', 'r' + svn_rev, treeish],
+                    cwd=self.local)
+                git_rev = p.stdout.rstrip()
 
                 if p.returncode != 0 or not git_rev:
                     # Try a plain git checkout as a last resort
-                    p = subprocess.Popen(['git', 'checkout', rev], cwd=self.local,
-                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-                    out, err = p.communicate()
-                    if p.returncode == 0:
-                        print out
-                    else:
+                    p = FDroidPopen(['git', 'checkout', rev], cwd=self.local)
+                    if p.returncode != 0:
                         raise VCSException("No git treeish found and direct git checkout failed")
                 else:
                     # Check out the git rev equivalent to the svn rev
-                    p = subprocess.Popen(['git', 'checkout', git_rev], cwd=self.local,
-                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-                    out, err = p.communicate()
-                    if p.returncode == 0:
-                        print out
-                    else:
+                    p = FDroidPopen(['git', 'checkout', git_rev], cwd=self.local)
+                    if p.returncode != 0:
                         raise VCSException("Git svn checkout failed")
 
-        # Get rid of any uncontrolled files left behind...
+        # Get rid of any uncontrolled files left behind
         if subprocess.call(['git', 'clean', '-dffx'], cwd=self.local) != 0:
             raise VCSException("Git clean failed")
 
@@ -472,9 +455,10 @@ class vcs_gitsvn(vcs):
 
     def getref(self):
         self.checkrepo()
-        p = subprocess.Popen(['git', 'svn', 'find-rev', 'HEAD'],
-                stdout=subprocess.PIPE, cwd=self.local)
-        return p.communicate()[0].strip()
+        p = FDroidPopen(['git', 'svn', 'find-rev', 'HEAD'], cwd=self.local)
+        if p.returncode != 0:
+            return None
+        return p.stdout.strip()
 
 class vcs_svn(vcs):
 
@@ -511,11 +495,11 @@ class vcs_svn(vcs):
             raise VCSException("Svn update failed")
 
     def getref(self):
-        p = subprocess.Popen(['svn', 'info'],
-                stdout=subprocess.PIPE, cwd=self.local)
+        p = FDroidPopen(['svn', 'info'], cwd=self.local)
         for line in p.communicate()[0].splitlines():
             if line and line.startswith('Last Changed Rev: '):
                 return line[18:]
+        return None
 
 class vcs_hg(vcs):
 
@@ -542,11 +526,9 @@ class vcs_hg(vcs):
         if subprocess.call(['hg', 'update', '-C', rev],
                 cwd=self.local) != 0:
             raise VCSException("Hg checkout failed")
-        p = subprocess.Popen(['hg', 'purge', '--all'], stdout=subprocess.PIPE,
-                             cwd=self.local)
-        result = p.communicate()[0]
+        p = FDroidPopen(['hg', 'purge', '--all'], cwd=self.local)
         # Also delete untracked files, we have to enable purge extension for that:
-        if "'purge' is provided by the following extension" in result:
+        if "'purge' is provided by the following extension" in p.stdout:
             with open(self.local+"/.hg/hgrc", "a") as myfile:
                 myfile.write("\n[extensions]\nhgext.purge=\n")
             if subprocess.call(['hg', 'purge', '--all'], cwd=self.local) != 0:
@@ -555,9 +537,8 @@ class vcs_hg(vcs):
             raise VCSException("HG purge failed")
 
     def gettags(self):
-        p = subprocess.Popen(['hg', 'tags', '-q'],
-                stdout=subprocess.PIPE, cwd=self.local)
-        return p.communicate()[0].splitlines()[1:]
+        p = FDroidPopen(['hg', 'tags', '-q'], cwd=self.local)
+        return p.stdout.splitlines()[1:]
 
 
 class vcs_bzr(vcs):
@@ -585,10 +566,9 @@ class vcs_bzr(vcs):
             raise VCSException("Bzr revert failed")
 
     def gettags(self):
-        p = subprocess.Popen(['bzr', 'tags'],
-                stdout=subprocess.PIPE, cwd=self.local)
+        p = FDroidPopen(['bzr', 'tags'], cwd=self.local)
         return [tag.split('   ')[0].strip() for tag in
-                p.communicate()[0].splitlines()]
+                p.stdout.splitlines()]
 
 def retrieve_string(xml_dir, string):
     if string.startswith('@string/'):
@@ -664,8 +644,7 @@ def ant_subprojects(root_dir):
             relpath = os.path.join(root_dir, path)
             if not os.path.isdir(relpath):
                 continue
-            if options.verbose:
-                print "Found subproject %s..." % path
+            logging.info("Found subproject at %s" % path)
             subprojects.append(path)
     return subprojects
 
@@ -836,7 +815,7 @@ def getsrclib(spec, srclib_dir, srclibpaths=[], subdir=None, target=None,
                         % name, p.stdout)
 
         if srclib["Update Project"] == "Yes" and not (autoupdate and number):
-            print "Updating srclib %s at path %s" % (name, libdir)
+            logging.info("Updating srclib %s at path %s" % (name, libdir))
             cmd = [os.path.join(config['sdk_path'], 'tools', 'android'),
                 'update', 'project', '-p', libdir]
             if target:
@@ -873,32 +852,30 @@ def getsrclib(spec, srclib_dir, srclibpaths=[], subdir=None, target=None,
 #   'srclibpaths' is information on the srclibs being used
 def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=False):
 
-    # Optionally, the actual app source can be in a subdirectory...
+    # Optionally, the actual app source can be in a subdirectory
     if 'subdir' in build:
         root_dir = os.path.join(build_dir, build['subdir'])
     else:
         root_dir = build_dir
 
-    # Get a working copy of the right revision...
-    print "Getting source for revision " + build['commit']
+    # Get a working copy of the right revision
+    logging.info("Getting source for revision " + build['commit'])
     vcs.gotorevision(build['commit'])
 
     # Check that a subdir (if we're using one) exists. This has to happen
-    # after the checkout, since it might not exist elsewhere...
+    # after the checkout, since it might not exist elsewhere
     if not os.path.exists(root_dir):
         raise BuildException('Missing subdir ' + root_dir)
 
-    # Initialise submodules if requred...
+    # Initialise submodules if requred
     if build['submodules']:
-        if options.verbose:
-            print "Initialising submodules..."
+        logging.info("Initialising submodules")
         vcs.initsubmodules()
 
-    # Run an init command if one is required...
+    # Run an init command if one is required
     if 'init' in build:
         cmd = replace_config_vars(build['init'])
-        if options.verbose:
-            print "Running 'init' commands in %s" % root_dir
+        logging.info("Running 'init' commands in %s" % root_dir)
 
         p = FDroidPopen(['bash', '-x', '-c', cmd], cwd=root_dir)
         if p.returncode != 0:
@@ -909,18 +886,18 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
     if 'patch' in build:
         for patch in build['patch'].split(';'):
             patch = patch.strip()
-            print "Applying " + patch
+            logging.info("Applying " + patch)
             patch_path = os.path.join('metadata', app['id'], patch)
             if subprocess.call(['patch', '-p1',
                             '-i', os.path.abspath(patch_path)], cwd=build_dir) != 0:
                 raise BuildException("Failed to apply patch %s" % patch_path)
 
-    # Get required source libraries...
+    # Get required source libraries
     srclibpaths = []
     updatemode = build.get('update', 'auto')
     if 'srclibs' in build:
         target=build['target'] if 'target' in build else None
-        print "Collecting source libraries..."
+        logging.info("Collecting source libraries")
         for lib in build['srclibs'].split(';'):
             srclibpaths.append(getsrclib(lib, srclib_dir, srclibpaths,
                 target=target, preponly=onserver, autoupdate=(updatemode=='auto')))
@@ -945,13 +922,13 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
             update_dirs = ['.'] + ant_subprojects(root_dir)
         else:
             update_dirs = [d.strip() for d in updatemode.split(';')]
-        # Force build.xml update if necessary...
+        # Force build.xml update if necessary
         if updatemode == 'force' or 'target' in build:
             if updatemode == 'force':
                 update_dirs = ['.']
             buildxml = os.path.join(root_dir, 'build.xml')
             if os.path.exists(buildxml):
-                print 'Force-removing old build.xml'
+                logging.info('Force-removing old build.xml')
                 os.remove(buildxml)
 
         for d in update_dirs:
@@ -959,11 +936,10 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
             # Clean update dirs via ant
             p = FDroidPopen(['ant', 'clean'], cwd=subdir)
             dparms = parms + ['-p', d]
-            if options.verbose:
-                if d == '.':
-                    print "Updating main project..."
-                else:
-                    print "Updating subproject %s..." % d
+            if d == '.':
+                logging.info("Updating main project")
+            else:
+                logging.info("Updating subproject %s" % d)
             p = FDroidPopen(dparms, cwd=root_dir)
             # Check to see whether an error was returned without a proper exit
             # code (this is the case for the 'no target set or target invalid'
@@ -972,21 +948,20 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
                 raise BuildException("Failed to update project at %s" % d,
                         p.stdout)
 
-    # Update the local.properties file...
+    # Update the local.properties file
     localprops = [ os.path.join(build_dir, 'local.properties') ]
     if 'subdir' in build:
         localprops += [ os.path.join(root_dir, 'local.properties') ]
     for path in localprops:
         if not os.path.isfile(path):
             continue
-        if options.verbose:
-            print "Updating properties file at %s" % path
+        logging.info("Updating properties file at %s" % path)
         f = open(path, 'r')
         props = f.read()
         f.close()
         props += '\n'
         # Fix old-fashioned 'sdk-location' by copying
-        # from sdk.dir, if necessary...
+        # from sdk.dir, if necessary
         if build['oldsdkloc']:
             sdkloc = re.match(r".*^sdk.dir=(\S+)$.*", props,
                 re.S|re.M).group(1)
@@ -995,10 +970,10 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
             props += "sdk.dir=%s\n" % config['sdk_path']
             props += "sdk-location=%s\n" % ['sdk_path']
         if 'ndk_path' in config:
-            # Add ndk location...
+            # Add ndk location
             props += "ndk.dir=%s\n" % config['ndk_path']
             props += "ndk-location=%s\n" % config['ndk_path']
-        # Add java.encoding if necessary...
+        # Add java.encoding if necessary
         if 'encoding' in build:
             props += "java.encoding=%s\n" % build['encoding']
         f = open(path, 'w')
@@ -1024,7 +999,7 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
                     'build.gradle'], cwd=gradle_dir)
 
     # Remove forced debuggable flags
-    print "Removing debuggable flags..."
+    logging.info("Removing debuggable flags")
     for path in manifest_paths(root_dir, flavour):
         if not os.path.isfile(path):
             continue
@@ -1032,9 +1007,9 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
             's/android:debuggable="[^"]*"//g', path]) != 0:
             raise BuildException("Failed to remove debuggable flags")
 
-    # Insert version code and number into the manifest if necessary...
+    # Insert version code and number into the manifest if necessary
     if build['forceversion']:
-        print "Changing the version name..."
+        logging.info("Changing the version name")
         for path in manifest_paths(root_dir, flavour):
             if not os.path.isfile(path):
                 continue
@@ -1049,7 +1024,7 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
                     path]) != 0:
                     raise BuildException("Failed to amend build.gradle")
     if build['forcevercode']:
-        print "Changing the version code..."
+        logging.info("Changing the version code")
         for path in manifest_paths(root_dir, flavour):
             if not os.path.isfile(path):
                 continue
@@ -1064,13 +1039,12 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
                     path]) != 0:
                     raise BuildException("Failed to amend build.gradle")
 
-    # Delete unwanted files...
+    # Delete unwanted files
     if 'rm' in build:
         for part in build['rm'].split(';'):
             dest = os.path.join(build_dir, part.strip())
             rdest = os.path.abspath(dest)
-            if options.verbose:
-                print "Removing {0}".format(rdest)
+            logging.info("Removing {0}".format(rdest))
             if not rdest.startswith(os.path.abspath(build_dir)):
                 raise BuildException("rm for {1} is outside build root {0}".format(
                     os.path.abspath(build_dir),os.path.abspath(dest)))
@@ -1082,10 +1056,9 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
                 else:
                     subprocess.call('rm -rf ' + rdest, shell=True)
             else:
-                if options.verbose:
-                    print "...but it didn't exist"
+                logging.info("...but it didn't exist")
 
-    # Fix apostrophes translation files if necessary...
+    # Fix apostrophes translation files if necessary
     if build['fixapos']:
         for root, dirs, files in os.walk(os.path.join(root_dir, 'res')):
             for filename in files:
@@ -1096,7 +1069,7 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
                         os.path.join(root, filename)]) != 0:
                         raise BuildException("Failed to amend " + filename)
 
-    # Fix translation files if necessary...
+    # Fix translation files if necessary
     if build['fixtrans']:
         for root, dirs, files in os.walk(os.path.join(root_dir, 'res')):
             for filename in files:
@@ -1122,7 +1095,7 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
                             else:
                                 index += 1
                         # We only want to insert the positional arguments
-                        # when there is more than one argument...
+                        # when there is more than one argument
                         if oldline != line:
                             if num > 2:
                                 changed = True
@@ -1137,33 +1110,31 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=
 
     remove_signing_keys(build_dir)
 
-    # Add required external libraries...
+    # Add required external libraries
     if 'extlibs' in build:
-        print "Collecting prebuilt libraries..."
+        logging.info("Collecting prebuilt libraries")
         libsdir = os.path.join(root_dir, 'libs')
         if not os.path.exists(libsdir):
             os.mkdir(libsdir)
         for lib in build['extlibs'].split(';'):
             lib = lib.strip()
-            if options.verbose:
-                print "...installing extlib {0}".format(lib)
+            logging.info("...installing extlib {0}".format(lib))
             libf = os.path.basename(lib)
             libsrc = os.path.join(extlib_dir, lib)
             if not os.path.exists(libsrc):
                 raise BuildException("Missing extlib file {0}".format(libsrc))
             shutil.copyfile(libsrc, os.path.join(libsdir, libf))
 
-    # Run a pre-build command if one is required...
+    # Run a pre-build command if one is required
     if 'prebuild' in build:
         cmd = replace_config_vars(build['prebuild'])
 
-        # Substitute source library paths into prebuild commands...
+        # Substitute source library paths into prebuild commands
         for name, number, libpath in srclibpaths:
             libpath = os.path.relpath(libpath, root_dir)
             cmd = cmd.replace('$$' + name + '$$', libpath)
 
-        if options.verbose:
-            print "Running 'prebuild' commands in %s" % root_dir
+        logging.info("Running 'prebuild' commands in %s" % root_dir)
 
         p = FDroidPopen(['bash', '-x', '-c', cmd], cwd=root_dir)
         if p.returncode != 0:
@@ -1229,7 +1200,7 @@ def scan_source(build_dir, root_dir, thisbuild):
         return False
 
     def removeproblem(what, fd, fp):
-        print 'Removing %s at %s' % (what, fd)
+        logging.info('Removing %s at %s' % (what, fd))
         os.remove(fp)
 
     def handleproblem(what, fd, fp):
@@ -1239,20 +1210,20 @@ def scan_source(build_dir, root_dir, thisbuild):
             problems.append('Found %s at %s' % (what, fd))
 
     def warnproblem(what, fd, fp):
-        print 'Warning: Found %s at %s' % (what, fd)
+        logging.info('Warning: Found %s at %s' % (what, fd))
 
-    # Iterate through all files in the source code...
+    # Iterate through all files in the source code
     for r,d,f in os.walk(build_dir):
         for curfile in f:
 
             if '/.hg' in r or '/.git' in r or '/.svn' in r:
                 continue
 
-            # Path (relative) to the file...
+            # Path (relative) to the file
             fp = os.path.join(r, curfile)
             fd = fp[len(build_dir):]
 
-            # Check if this file has been explicitly excluded from scanning...
+            # Check if this file has been explicitly excluded from scanning
             if toignore(fd):
                 continue
 
@@ -1280,7 +1251,7 @@ def scan_source(build_dir, root_dir, thisbuild):
     ms.close()
 
     # Presence of a jni directory without buildjni=yes might
-    # indicate a problem... (if it's not a problem, explicitly use
+    # indicate a problem (if it's not a problem, explicitly use
     # buildjni=no to bypass this check)
     if (os.path.exists(os.path.join(root_dir, 'jni')) and
             thisbuild.get('buildjni') is None):
@@ -1358,15 +1329,14 @@ def isApkDebuggable(apkfile, config):
 
     :param apkfile: full path to the apk to check"""
 
-    p = subprocess.Popen([os.path.join(config['sdk_path'],
+    p = FDroidPopen([os.path.join(config['sdk_path'],
         'build-tools', config['build_tools'], 'aapt'),
         'dump', 'xmltree', apkfile, 'AndroidManifest.xml'],
-        stdout=subprocess.PIPE)
-    output = p.communicate()[0]
+        output=False)
     if p.returncode != 0:
-        print "ERROR: Failed to get apk manifest information"
+        logging.critical("Failed to get apk manifest information")
         sys.exit(1)
-    for line in output.splitlines():
+    for line in p.stdout.splitlines():
         if 'android:debuggable' in line and not line.endswith('0x0'):
             return True
     return False
@@ -1398,26 +1368,25 @@ class AsynchronousFileReader(threading.Thread):
 class PopenResult:
     returncode = None
     stdout = ''
-    stderr = ''
-    stdout_apk = ''
 
-def FDroidPopen(commands, cwd=None):
+def FDroidPopen(commands, cwd=None, output=True):
     """
     Run a command and capture the output.
 
     :param commands: command and argument list like in subprocess.Popen
     :param cwd: optionally specifies a working directory
+    :param output: whether to print output as it is fetched
     :returns: A PopenResult.
     """
 
-    if options.verbose:
-        if cwd:
-            print "Directory: %s" % cwd
-        print " > %s" % ' '.join(commands)
+    if cwd:
+        logging.info("Directory: %s" % cwd)
+    logging.info("> %s" % ' '.join(commands))
 
     result = PopenResult()
     p = subprocess.Popen(commands, cwd=cwd,
-            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+            stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+            stdin=subprocess.PIPE)
 
     stdout_queue = Queue.Queue()
     stdout_reader = AsynchronousFileReader(p.stdout, stdout_queue)
@@ -1427,7 +1396,7 @@ def FDroidPopen(commands, cwd=None):
     while not stdout_reader.eof():
         while not stdout_queue.empty():
             line = stdout_queue.get()
-            if options.verbose:
+            if output and options.verbose:
                 # Output directly to console
                 sys.stdout.write(line)
                 sys.stdout.flush()
@@ -1474,8 +1443,8 @@ def remove_signing_keys(build_dir):
                     else:
                         o.write(line)
 
-            if changed and options.verbose:
-                print "Cleaned build.gradle of keysigning configs at %s" % path
+            if changed:
+                logging.info("Cleaned build.gradle of keysigning configs at %s" % path)
 
         for propfile in ('build.properties', 'default.properties', 'ant.properties'):
             if propfile in files:
@@ -1492,8 +1461,8 @@ def remove_signing_keys(build_dir):
                         else:
                             o.write(line)
 
-                if changed and options.verbose:
-                    print "Cleaned %s of keysigning configs at %s" % (propfile,path)
+                if changed:
+                    logging.info("Cleaned %s of keysigning configs at %s" % (propfile,path))
 
 def replace_config_vars(cmd):
     cmd = cmd.replace('$$SDK$$', config['sdk_path'])