import subprocess
import time
import operator
-import Queue
import logging
import hashlib
import socket
import xml.etree.ElementTree as XMLElementTree
-from distutils.version import LooseVersion
+try:
+ # Python 2
+ from Queue import Queue
+except ImportError:
+ # Python 3
+ from queue import Queue
+
from zipfile import ZipFile
import metadata
'r9b': None,
'r10e': "$ANDROID_NDK",
},
- 'build_tools': "23.0.1",
+ 'build_tools': "23.0.2",
+ 'java_paths': {
+ '1.7': "/usr/lib/jvm/java-7-openjdk",
+ '1.8': None,
+ },
'ant': "ant",
'mvn3': "mvn",
'gradle': 'gradle',
thisconfig[k] = exp
thisconfig[k + '_orig'] = v
- for k in ['ndk_paths']:
+ for k in ['ndk_paths', 'java_paths']:
d = thisconfig[k]
for k2 in d.copy():
v = d[k2]
for n in ['ANDROID_HOME', 'ANDROID_SDK']:
env[n] = config['sdk_path']
+ for v in ['7', '8']:
+ cpath = config['java_paths']['1.%s' % v]
+ if cpath:
+ env['JAVA%s_HOME' % v] = cpath
+
for k in ["keystorepass", "keypass"]:
if k in config:
write_password_file(k)
return config
-def get_ndk_path(version):
- if version is None:
- version = 'r10e' # falls back to latest
- paths = config['ndk_paths']
- if version not in paths:
- return ''
- return paths[version] or ''
-
-
def find_sdk_tools_cmd(cmd):
'''find a working path to a tool from the Android SDK'''
command line argments
'''
filename = '.fdroid.' + pwtype + '.txt'
- fd = os.open(filename, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, 0600)
+ fd = os.open(filename, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, 0o600)
if password is None:
os.write(fd, config[pwtype])
else:
vc = vercodes[appid]
if not vc:
continue
- app['builds'] = [b for b in app['builds'] if b['vercode'] in vc]
- if len(app['builds']) != len(vercodes[appid]):
+ app.builds = [b for b in app.builds if b.vercode in vc]
+ if len(app.builds) != len(vercodes[appid]):
error = True
- allvcs = [b['vercode'] for b in app['builds']]
+ allvcs = [b.vercode for b in app.builds]
for v in vercodes[appid]:
if v not in allvcs:
logging.critical("No such vercode %s for app %s" % (v, appid))
return ext == f_ext
-apk_regex = None
+apk_regex = re.compile(r"^(.+)_([0-9]+)\.apk$")
def clean_description(description):
def apknameinfo(filename):
- global apk_regex
filename = os.path.basename(filename)
- if apk_regex is None:
- apk_regex = re.compile(r"^(.+)_([0-9]+)\.apk$")
m = apk_regex.match(filename)
try:
result = (m.group(1), m.group(2))
def getapkname(app, build):
- return "%s_%s.apk" % (app['id'], build['vercode'])
+ return "%s_%s.apk" % (app.id, build.vercode)
def getsrcname(app, build):
- return "%s_%s_src.tar.gz" % (app['id'], build['vercode'])
+ return "%s_%s_src.tar.gz" % (app.id, build.vercode)
def getappname(app):
- if app['Name']:
- return app['Name']
- if app['Auto Name']:
- return app['Auto Name']
- return app['id']
+ if app.Name:
+ return app.Name
+ if app.AutoName:
+ return app.AutoName
+ return app.id
def getcvname(app):
- return '%s (%s)' % (app['Current Version'], app['Current Version Code'])
+ return '%s (%s)' % (app.CurrentVersion, app.CurrentVersionCode)
def getvcs(vcstype, remote, local):
try:
self.gotorevisionx(rev)
- except FDroidException, e:
+ except FDroidException as e:
exc = e
# If necessary, write the .fdroidvcs file.
logging.debug("fetch_real_name: Checking manifest at " + path)
xml = parse_xml(path)
app = xml.find('application')
+ if app is None:
+ continue
if "{http://schemas.android.com/apk/res/android}label" not in app.attrib:
continue
label = app.attrib["{http://schemas.android.com/apk/res/android}label"].encode('utf-8')
def app_matches_packagename(app, package):
if not package:
return False
- appid = app['Update Check Name'] or app['id']
- if appid == "Ignore":
+ appid = app.UpdateCheckName or app.id
+ if appid is None or appid == "Ignore":
return True
return appid == package
# All values returned are strings.
def parse_androidmanifests(paths, app):
- ignoreversions = app['Update Check Ignore']
+ ignoreversions = app.UpdateCheckIgnore
ignoresearch = re.compile(ignoreversions).search if ignoreversions else None
if not paths:
def prepare_source(vcs, app, build, build_dir, srclib_dir, extlib_dir, onserver=False, refresh=True):
# Optionally, the actual app source can be in a subdirectory
- if build['subdir']:
- root_dir = os.path.join(build_dir, build['subdir'])
+ if build.subdir:
+ root_dir = os.path.join(build_dir, build.subdir)
else:
root_dir = build_dir
# Get a working copy of the right revision
- logging.info("Getting source for revision " + build['commit'])
- vcs.gotorevision(build['commit'], refresh)
+ logging.info("Getting source for revision " + build.commit)
+ vcs.gotorevision(build.commit, refresh)
# Initialise submodules if required
- if build['submodules']:
+ if build.submodules:
logging.info("Initialising submodules")
vcs.initsubmodules()
raise BuildException('Missing subdir ' + root_dir)
# Run an init command if one is required
- if build['init']:
- cmd = replace_config_vars(build['init'], build)
+ if build.init:
+ cmd = replace_config_vars(build.init, build)
logging.info("Running 'init' commands in %s" % root_dir)
p = FDroidPopen(['bash', '-x', '-c', cmd], cwd=root_dir)
if p.returncode != 0:
raise BuildException("Error running init command for %s:%s" %
- (app['id'], build['version']), p.output)
+ (app.id, build.version), p.output)
# Apply patches if any
- if build['patch']:
+ if build.patch:
logging.info("Applying patches")
- for patch in build['patch']:
+ for patch in build.patch:
patch = patch.strip()
logging.info("Applying " + patch)
- patch_path = os.path.join('metadata', app['id'], patch)
+ patch_path = os.path.join('metadata', app.id, patch)
p = FDroidPopen(['patch', '-p1', '-i', os.path.abspath(patch_path)], cwd=build_dir)
if p.returncode != 0:
raise BuildException("Failed to apply patch %s" % patch_path)
# Get required source libraries
srclibpaths = []
- if build['srclibs']:
+ if build.srclibs:
logging.info("Collecting source libraries")
- for lib in build['srclibs']:
+ for lib in build.srclibs:
srclibpaths.append(getsrclib(lib, srclib_dir, build, preponly=onserver, refresh=refresh))
for name, number, libpath in srclibpaths:
# Update the local.properties file
localprops = [os.path.join(build_dir, 'local.properties')]
- if build['subdir']:
- parts = build['subdir'].split(os.sep)
+ if build.subdir:
+ parts = build.subdir.split(os.sep)
cur = build_dir
for d in parts:
cur = os.path.join(cur, d)
logging.info("Creating local.properties file at %s" % path)
# Fix old-fashioned 'sdk-location' by copying
# from sdk.dir, if necessary
- if build['oldsdkloc']:
+ if build.oldsdkloc:
sdkloc = re.match(r".*^sdk.dir=(\S+)$.*", props,
re.S | re.M).group(1)
props += "sdk-location=%s\n" % sdkloc
else:
props += "sdk.dir=%s\n" % config['sdk_path']
props += "sdk-location=%s\n" % config['sdk_path']
- if build['ndk_path']:
+ ndk_path = build.ndk_path()
+ if ndk_path:
# Add ndk location
- props += "ndk.dir=%s\n" % build['ndk_path']
- props += "ndk-location=%s\n" % build['ndk_path']
+ props += "ndk.dir=%s\n" % ndk_path
+ props += "ndk-location=%s\n" % ndk_path
# Add java.encoding if necessary
- if build['encoding']:
- props += "java.encoding=%s\n" % build['encoding']
+ if build.encoding:
+ props += "java.encoding=%s\n" % build.encoding
with open(path, 'w') as f:
f.write(props)
flavours = []
- if build['type'] == 'gradle':
- flavours = build['gradle']
-
- gradlepluginver = None
-
- gradle_dirs = [root_dir]
+ if build.method() == 'gradle':
+ flavours = build.gradle
- # Parent dir build.gradle
- parent_dir = os.path.normpath(os.path.join(root_dir, '..'))
- if parent_dir.startswith(build_dir):
- gradle_dirs.append(parent_dir)
-
- for dir_path in gradle_dirs:
- if gradlepluginver:
- break
- if not os.path.isdir(dir_path):
- continue
- for filename in os.listdir(dir_path):
- if not filename.endswith('.gradle'):
- continue
- path = os.path.join(dir_path, filename)
- if not os.path.isfile(path):
- continue
- for line in file(path):
- match = gradle_version_regex.match(line)
- if match:
- gradlepluginver = match.group(1)
- break
-
- if gradlepluginver:
- build['gradlepluginver'] = LooseVersion(gradlepluginver)
- else:
- logging.warn("Could not fetch the gradle plugin version, defaulting to 0.11")
- build['gradlepluginver'] = LooseVersion('0.11')
-
- if build['target']:
- n = build["target"].split('-')[1]
+ if build.target:
+ n = build.target.split('-')[1]
regsub_file(r'compileSdkVersion[ =]+[0-9]+',
r'compileSdkVersion %s' % n,
os.path.join(root_dir, 'build.gradle'))
remove_debuggable_flags(root_dir)
# Insert version code and number into the manifest if necessary
- if build['forceversion']:
+ if build.forceversion:
logging.info("Changing the version name")
for path in manifest_paths(root_dir, flavours):
if not os.path.isfile(path):
continue
if has_extension(path, 'xml'):
regsub_file(r'android:versionName="[^"]*"',
- r'android:versionName="%s"' % build['version'],
+ r'android:versionName="%s"' % build.version,
path)
elif has_extension(path, 'gradle'):
regsub_file(r"""(\s*)versionName[\s'"=]+.*""",
- r"""\1versionName '%s'""" % build['version'],
+ r"""\1versionName '%s'""" % build.version,
path)
- if build['forcevercode']:
+ if build.forcevercode:
logging.info("Changing the version code")
for path in manifest_paths(root_dir, flavours):
if not os.path.isfile(path):
continue
if has_extension(path, 'xml'):
regsub_file(r'android:versionCode="[^"]*"',
- r'android:versionCode="%s"' % build['vercode'],
+ r'android:versionCode="%s"' % build.vercode,
path)
elif has_extension(path, 'gradle'):
regsub_file(r'versionCode[ =]+[0-9]+',
- r'versionCode %s' % build['vercode'],
+ r'versionCode %s' % build.vercode,
path)
# Delete unwanted files
- if build['rm']:
+ if build.rm:
logging.info("Removing specified files")
- for part in getpaths(build_dir, build['rm']):
+ for part in getpaths(build_dir, build.rm):
dest = os.path.join(build_dir, part)
logging.info("Removing {0}".format(part))
if os.path.lexists(dest):
remove_signing_keys(build_dir)
# Add required external libraries
- if build['extlibs']:
+ if build.extlibs:
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']:
+ for lib in build.extlibs:
lib = lib.strip()
logging.info("...installing extlib {0}".format(lib))
libf = os.path.basename(lib)
shutil.copyfile(libsrc, os.path.join(libsdir, libf))
# Run a pre-build command if one is required
- if build['prebuild']:
+ if build.prebuild:
logging.info("Running 'prebuild' commands in %s" % root_dir)
- cmd = replace_config_vars(build['prebuild'], build)
+ cmd = replace_config_vars(build.prebuild, build)
# Substitute source library paths into prebuild commands
for name, number, libpath in srclibpaths:
p = FDroidPopen(['bash', '-x', '-c', cmd], cwd=root_dir)
if p.returncode != 0:
raise BuildException("Error running prebuild command for %s:%s" %
- (app['id'], build['version']), p.output)
+ (app.id, build.version), p.output)
# Generate (or update) the ant build file, build.xml...
- if build['update'] and build['update'] != ['no'] and build['type'] == 'ant':
+ if build.method() == 'ant' and build.update != ['no']:
parms = ['android', 'update', 'lib-project']
lparms = ['android', 'update', 'project']
- if build['target']:
- parms += ['-t', build['target']]
- lparms += ['-t', build['target']]
- if build['update'] == ['auto']:
- update_dirs = ant_subprojects(root_dir) + ['.']
+ if build.target:
+ parms += ['-t', build.target]
+ lparms += ['-t', build.target]
+ if build.update:
+ update_dirs = build.update
else:
- update_dirs = build['update']
+ update_dirs = ant_subprojects(root_dir) + ['.']
for d in update_dirs:
subdir = os.path.join(root_dir, d)
full_path = os.path.join(build_dir, p)
full_path = os.path.normpath(full_path)
paths[p] = [r[len(build_dir) + 1:] for r in glob.glob(full_path)]
+ if not paths[p]:
+ raise FDroidException("glob path '%s' did not match any files/dirs" % p)
return paths
try:
p = subprocess.Popen(commands, cwd=cwd, shell=False, env=env,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- except OSError, e:
+ except OSError as e:
raise BuildException("OSError while trying to execute " +
' '.join(commands) + ': ' + str(e))
- stdout_queue = Queue.Queue()
+ stdout_queue = Queue()
stdout_reader = AsynchronousFileReader(p.stdout, stdout_queue)
# Check the queue for output (until there is no more to get)
cmd = cmd.replace('$$NDK$$', env['ANDROID_NDK'])
cmd = cmd.replace('$$MVN3$$', config['mvn3'])
if build is not None:
- cmd = cmd.replace('$$COMMIT$$', build['commit'])
- cmd = cmd.replace('$$VERSION$$', build['version'])
- cmd = cmd.replace('$$VERCODE$$', build['vercode'])
+ cmd = cmd.replace('$$COMMIT$$', build.commit)
+ cmd = cmd.replace('$$VERSION$$', build.version)
+ cmd = cmd.replace('$$VERCODE$$', build.vercode)
return cmd
repos = []
for root, dirs, files in os.walk(os.getcwd()):
for d in dirs:
- print 'checking', root, 'for', d
+ print('checking', root, 'for', d)
if d in ('archive', 'metadata', 'repo', 'srclibs', 'tmp'):
# standard parts of an fdroid repo, so never packageNames
continue