1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-08-18 12:20:10 +02:00

Merge branch 'gradle' (finally!)

Conflicts:
	fdroidserver/checkupdates.py
This commit is contained in:
Daniel Martí 2013-08-14 12:03:19 +02:00
commit 2fdd274580
6 changed files with 153 additions and 91 deletions

View File

@ -5,9 +5,8 @@
sdk_path = "/path/to/android-sdk-linux_86" sdk_path = "/path/to/android-sdk-linux_86"
ndk_path = "/path/to/android-ndk-r8e" ndk_path = "/path/to/android-ndk-r8e"
# May be necessary for fdroid update; you may still need to make a symlink to # Build tools version to be used
# aapt in platform-tools build_tools = "18.0.1"
aapt_path = "/path/to/android-sdk-linux_x86/build-tools/17.0.0/aapt"
#You probably don't need to change this... #You probably don't need to change this...
javacc_path = "/usr/share/java" javacc_path = "/usr/share/java"
@ -15,6 +14,12 @@ javacc_path = "/usr/share/java"
#Command for running maven 3 (could be mvn, mvn3, or a full path) #Command for running maven 3 (could be mvn, mvn3, or a full path)
mvn3 = "mvn3" mvn3 = "mvn3"
# Gradle command
gradle = "gradle"
# Android gradle plugin version
gradle_plugin = "0.5.+"
repo_url = "https://f-droid.org/repo" repo_url = "https://f-droid.org/repo"
repo_name = "F-Droid" repo_name = "F-Droid"
repo_icon = "fdroid-icon.png" repo_icon = "fdroid-icon.png"

View File

@ -772,6 +772,14 @@ already exist, but not a good idea if it's heavily customised. If you get an
error about invalid target, first try @code{init=rm -rf bin/}; otherwise this error about invalid target, first try @code{init=rm -rf bin/}; otherwise this
parameter should do the trick. parameter should do the trick.
Please note that gradle builds should be using compilesdk=.
@item compilesdk=<level>
Practically accomplishes the same that target= does when used in ant and maven
projects. compilesdk= is used rather than target= so as to not cause any more
confusion. It only takes effect on gradle builds in the build.gradle file,
thus using it in any other case is not wise.
@item update=xxx @item update=xxx
By default, 'android update project' is used to generate or update the By default, 'android update project' is used to generate or update the
build.xml file. Specifying update=no bypasses that. build.xml file. Specifying update=no bypasses that.

View File

@ -415,6 +415,29 @@ def build_local(app, thisbuild, vcs, build_dir, output_dir, srclib_dir, extlib_d
if 'mvnflags' in thisbuild: if 'mvnflags' in thisbuild:
mvncmd += thisbuild['mvnflags'] mvncmd += thisbuild['mvnflags']
p = subprocess.Popen(mvncmd, cwd=root_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p = subprocess.Popen(mvncmd, cwd=root_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
elif 'gradle' in thisbuild:
flavour = thisbuild['gradle']
if 'compilesdk' in thisbuild:
level = thisbuild["compilesdk"].split('-')[1]
subprocess.call(['sed', '-i',
's@compileSdkVersion[ ]*[0-9]*@compileSdkVersion '+level+'@g',
'build.gradle'], cwd=root_dir)
subprocess.call(['sed', '-i',
's@buildToolsVersion[ ]*["\'][0-9\.]*["\']@buildToolsVersion "'+build_tools+'"@g',
'build.gradle'], cwd=root_dir)
subprocess.call(['sed', '-i',
's@com.android.tools.build:gradle:[0-9\.\+]*@com.android.tools.build:gradle:'+gradle_plugin+'@g',
'build.gradle'], cwd=root_dir)
if install:
commands = [gradle, 'assemble'+flavour+'Debug', 'install'+flavour+'Debug']
else:
commands = [gradle, 'assemble'+flavour+'Release']
p = subprocess.Popen(commands, cwd=root_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
else: else:
if install: if install:
antcommands = ['debug','install'] antcommands = ['debug','install']
@ -464,6 +487,8 @@ def build_local(app, thisbuild, vcs, build_dir, output_dir, srclib_dir, extlib_d
raise BuildException('Failed to find output') raise BuildException('Failed to find output')
src = m.group(1) src = m.group(1)
src = os.path.join(bindir, src) + '.apk' src = os.path.join(bindir, src) + '.apk'
elif 'gradle' in thisbuild:
src = os.path.join(build_dir, 'build', 'apk', '-'.join([app['id'], flavour, 'release', 'unsigned'])+'.apk')
else: else:
src = re.match(r".*^.*Creating (.+) for release.*$.*", output, src = re.match(r".*^.*Creating (.+) for release.*$.*", output,
re.S|re.M).group(1) re.S|re.M).group(1)
@ -478,14 +503,8 @@ def build_local(app, thisbuild, vcs, build_dir, output_dir, srclib_dir, extlib_d
print "Checking " + src print "Checking " + src
if not os.path.exists(src): if not os.path.exists(src):
raise BuildException("Unsigned apk is not at expected location of " + src) raise BuildException("Unsigned apk is not at expected location of " + src)
if ('aapt_path' not in globals()):
# (re-)read configuration
execfile('config.py', globals())
if not os.path.exists(aapt_path):
print "Missing aapt - check aapt_path in your config"
sys.exit(1)
p = subprocess.Popen([aapt_path, p = subprocess.Popen([os.path.join(sdk_path, 'build-tools', build_tools, 'aapt'),
'dump', 'badging', src], 'dump', 'badging', src],
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
output = p.communicate()[0] output = p.communicate()[0]

View File

@ -60,9 +60,12 @@ def check_tags(app, sdk_path):
vcs.gotorevision(None) vcs.gotorevision(None)
flavour = None
if len(app['builds']) > 0: if len(app['builds']) > 0:
if 'subdir' in app['builds'][-1]: if 'subdir' in app['builds'][-1]:
build_dir = os.path.join(build_dir, app['builds'][-1]['subdir']) build_dir = os.path.join(build_dir, app['builds'][-1]['subdir'])
if 'gradle' in app['builds'][-1]:
flavour = app['builds'][-1]['gradle']
hver = None hver = None
hcode = "0" hcode = "0"
@ -71,15 +74,13 @@ def check_tags(app, sdk_path):
vcs.gotorevision(tag) vcs.gotorevision(tag)
# Only process tags where the manifest exists... # Only process tags where the manifest exists...
path = common.manifest_path(build_dir) paths = common.manifest_paths(build_dir, flavour)
print "Trying manifest at %s" % path version, vercode, package = common.parse_androidmanifests(paths)
if os.path.exists(path): print "Manifest exists. Found version %s" % version
version, vercode, package = common.parse_androidmanifest(build_dir) if package and package == app['id'] and version and vercode:
print "Manifest exists. Found version %s" % version if int(vercode) > int(hcode):
if package and package == app['id'] and version and vercode: hcode = str(int(vercode))
if int(vercode) > int(hcode): hver = version
hcode = str(int(vercode))
hver = version
if hver: if hver:
return (hver, hcode) return (hver, hcode)
@ -138,20 +139,20 @@ def check_repomanifest(app, sdk_path, branch=None):
elif vcs.repotype() == 'bzr': elif vcs.repotype() == 'bzr':
vcs.gotorevision(None) vcs.gotorevision(None)
flavour = None
if len(app['builds']) > 0: if len(app['builds']) > 0:
if 'subdir' in app['builds'][-1]: if 'subdir' in app['builds'][-1]:
build_dir = os.path.join(build_dir, app['builds'][-1]['subdir']) build_dir = os.path.join(build_dir, app['builds'][-1]['subdir'])
if 'gradle' in app['builds'][-1]:
flavour = app['builds'][-1]['gradle']
if not os.path.isdir(build_dir): if not os.path.isdir(build_dir):
return (None, "Subdir '" + app['builds'][-1]['subdir'] + "'is not a valid directory") return (None, "Subdir '" + app['builds'][-1]['subdir'] + "'is not a valid directory")
if os.path.exists(os.path.join(build_dir, 'AndroidManifest.xml')): paths = common.manifest_paths(build_dir, flavour)
version, vercode, package = common.parse_androidmanifest(build_dir)
elif os.path.exists(os.path.join(build_dir, 'src', 'main', 'AndroidManifest.xml')): version, vercode, package = common.parse_androidmanifests(paths)
# Alternate location for simple gradle locations...
version, vercode, package = common.parse_androidmanifest(os.path.join(build_dir, 'src', 'main'))
else:
return (None, "AndroidManifest.xml not found")
if not package: if not package:
return (None, "Couldn't find package ID") return (None, "Couldn't find package ID")
if package != app['id']: if package != app['id']:
@ -219,6 +220,7 @@ def check_market(app):
def main(): def main():
#Read configuration... #Read configuration...
globals()['gradle'] = "gradle"
execfile('config.py', globals()) execfile('config.py', globals())
# Parse command line... # Parse command line...
@ -302,15 +304,18 @@ def main():
vcs = common.getvcs(app["Repo Type"], app["Repo"], app_dir, sdk_path) vcs = common.getvcs(app["Repo Type"], app["Repo"], app_dir, sdk_path)
vcs.gotorevision(None) vcs.gotorevision(None)
flavour = None
if len(app['builds']) > 0: if len(app['builds']) > 0:
if 'subdir' in app['builds'][-1]: if 'subdir' in app['builds'][-1]:
app_dir = os.path.join(app_dir, app['builds'][-1]['subdir']) app_dir = os.path.join(app_dir, app['builds'][-1]['subdir'])
if 'gradle' in app['builds'][-1]:
flavour = app['builds'][-1]['gradle']
new_name = common.fetch_real_name(app_dir) new_name = common.fetch_real_name(app_dir, flavour)
if new_name != app['Auto Name']: if new_name != app['Auto Name']:
app['Auto Name'] = new_name app['Auto Name'] = new_name
if not writeit: writeit = True
writeit = True
except Exception: except Exception:
msg = "Auto Name failed for %s due to exception: %s" % (app['id'], traceback.format_exc()) msg = "Auto Name failed for %s due to exception: %s" % (app['id'], traceback.format_exc())

View File

@ -22,6 +22,7 @@ import subprocess
import time import time
import operator import operator
import cgi import cgi
import fileinput
def getvcs(vcstype, remote, local, sdk_path): def getvcs(vcstype, remote, local, sdk_path):
if vcstype == 'git': if vcstype == 'git':
@ -861,77 +862,109 @@ def description_html(lines,linkres):
ps.end() ps.end()
return ps.text_html return ps.text_html
def retrieve_string(app_dir, string_id): def retrieve_string(xml_dir, string):
string_search = re.compile(r'.*"'+string_id+'".*>([^<]+?)<.*').search if not string.startswith('@string/'):
for xmlfile in glob.glob(os.path.join( return string.replace("\\'","'")
app_dir, 'res', 'values', '*.xml')): string_search = re.compile(r'.*"'+string[8:]+'".*>([^<]+?)<.*').search
for xmlfile in glob.glob(os.path.join(xml_dir, '*.xml')):
for line in file(xmlfile): for line in file(xmlfile):
matches = string_search(line) matches = string_search(line)
if matches: if matches:
s = matches.group(1) return retrieve_string(xml_dir, matches.group(1))
if s.startswith('@string/'):
return retrieve_string(app_dir, s[8:]);
return s.replace("\\'","'")
return '' return ''
# Find the AM.xml - try the new gradle method first. # Return list of existing files that will be used to find the highest vercode
def manifest_path(app_dir): def manifest_paths(app_dir, flavour):
gradlepath = os.path.join(app_dir, 'source', 'main', 'AndroidManifest.xml')
if os.path.exists(gradlepath):
return gradlepath
rootpath = os.path.join(app_dir, 'AndroidManifest.xml')
return rootpath
possible_manifests = [ os.path.join(app_dir, 'AndroidManifest.xml'),
os.path.join(app_dir, 'src', 'main', 'AndroidManifest.xml'),
os.path.join(app_dir, 'build.gradle') ]
if flavour is not None:
possible_manifests.append(
os.path.join(app_dir, 'src', flavour, 'AndroidManifest.xml'))
return [path for path in possible_manifests if os.path.isfile(path)]
# Retrieve the package name # Retrieve the package name
def fetch_real_name(app_dir): def fetch_real_name(app_dir, flavour):
app_search = re.compile(r'.*<application.*').search app_search = re.compile(r'.*<application.*').search
name_search = re.compile(r'.*android:label="([^"]+)".*').search name_search = re.compile(r'.*android:label="([^"]+)".*').search
app_found = False app_found = False
name = None name = None
for line in file(manifest_path(app_dir)): for f in manifest_paths(app_dir, flavour):
if not app_found: if not f.endswith(".xml"):
if app_search(line): continue
app_found = True xml_dir = os.path.join(f[:-19], 'res', 'values')
if app_found: for line in file(f):
if name is not None: if not app_found:
break if app_search(line):
matches = name_search(line) app_found = True
if matches: if app_found:
name = matches.group(1) matches = name_search(line)
if matches:
return retrieve_string(xml_dir, matches.group(1))
if name.startswith('@string/'): return ''
return retrieve_string(app_dir, name[8:])
return name
# Extract some information from the AndroidManifest.xml at the given path. # Extract some information from the AndroidManifest.xml at the given path.
# Returns (version, vercode, package), any or all of which might be None. # Returns (version, vercode, package), any or all of which might be None.
# All values returned are strings. # All values returned are strings.
def parse_androidmanifest(app_dir): def parse_androidmanifests(paths):
vcsearch = re.compile(r'.*android:versionCode="([0-9]+?)".*').search vcsearch = re.compile(r'.*android:versionCode="([0-9]+?)".*').search
vnsearch = re.compile(r'.*android:versionName="([^"]+?)".*').search vnsearch = re.compile(r'.*android:versionName="([^"]+?)".*').search
psearch = re.compile(r'.*package="([^"]+)".*').search psearch = re.compile(r'.*package="([^"]+)".*').search
vnsearch_xml = re.compile(r'.*"(app_|)version">([^<]+?)<.*').search
version = None vcsearch_g = re.compile(r'.*versionCode[ ]+?([0-9]+?).*').search
vercode = None vnsearch_g = re.compile(r'.*versionName[ ]+?"([^"]+?)".*').search
package = None psearch_g = re.compile(r'.*packageName[ ]+?"([^"]+)".*').search
for line in file(manifest_path(app_dir)):
if not package: max_version = None
matches = psearch(line) max_vercode = None
if matches: max_package = None
package = matches.group(1)
if not version: for path in paths:
matches = vnsearch(line)
if matches: gradle = path.endswith("gradle")
version = matches.group(1) version = None
if not vercode: vercode = None
matches = vcsearch(line) # Remember package name, may be defined separately from version+vercode
if matches: package = max_package
vercode = matches.group(1)
if version.startswith('@string/'): for line in file(path):
version = retrieve_string(app_dir, version[8:]) if not package:
return (version, vercode, package) if gradle:
matches = psearch_g(line)
else:
matches = psearch(line)
if matches:
package = matches.group(1)
if not version:
if gradle:
matches = vnsearch_g(line)
else:
matches = vnsearch(line)
if matches:
version = matches.group(1)
if not vercode:
if gradle:
matches = vcsearch_g(line)
else:
matches = vcsearch(line)
if matches:
vercode = matches.group(1)
# Better some package name than nothing
if max_package is None:
max_package = package
if max_vercode is None or (vercode is not None and vercode > max_vercode):
max_version = version
max_vercode = vercode
max_package = package
return (max_version, max_vercode, max_package)
class BuildException(Exception): class BuildException(Exception):
def __init__(self, value, stdout = None, stderr = None): def __init__(self, value, stdout = None, stderr = None):
@ -1134,7 +1167,7 @@ def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, sdk_path,
# Generate (or update) the ant build file, build.xml... # Generate (or update) the ant build file, build.xml...
updatemode = build.get('update', '.') updatemode = build.get('update', '.')
if (updatemode != 'no' and if (updatemode != 'no' and
'maven' not in build): 'maven' not in build and 'gradle' not in build):
parms = [os.path.join(sdk_path, 'tools', 'android'), parms = [os.path.join(sdk_path, 'tools', 'android'),
'update', 'project', '-p', '.'] 'update', 'project', '-p', '.']
parms.append('--subprojects') parms.append('--subprojects')
@ -1575,14 +1608,9 @@ def isApkDebuggable(apkfile):
:param apkfile: full path to the apk to check""" :param apkfile: full path to the apk to check"""
if ('aapt_path' not in globals()): execfile('config.py', globals())
# (re-)read configuration
execfile('config.py', globals())
if not os.path.exists(aapt_path):
print "Missing aapt - check aapt_path in your config"
sys.exit(1)
p = subprocess.Popen([aapt_path, p = subprocess.Popen([os.path.join(sdk_path, 'build-tools', build_tools, 'aapt'),
'dump', 'xmltree', apkfile, 'AndroidManifest.xml'], 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'],
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
output = p.communicate()[0] output = p.communicate()[0]

View File

@ -267,10 +267,7 @@ def scan_apks(apps, apkcache, repodir, knownapks):
thisinfo['size'] = os.path.getsize(apkfile) thisinfo['size'] = os.path.getsize(apkfile)
thisinfo['permissions'] = [] thisinfo['permissions'] = []
thisinfo['features'] = [] thisinfo['features'] = []
if not os.path.exists(aapt_path): p = subprocess.Popen([os.path.join(sdk_path, 'build-tools', build_tools, 'aapt'),
print "Missing aapt - check aapt_path in your config"
sys.exit(1)
p = subprocess.Popen([aapt_path,
'dump', 'badging', apkfile], 'dump', 'badging', apkfile],
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
output = p.communicate()[0] output = p.communicate()[0]