#!/usr/bin/python __author__ = 'mmin18' __version__ = '1.50922' __plugin__ = '1' from subprocess import Popen, PIPE, check_call from distutils.version import LooseVersion import argparse import sys import os import io import re import time import shutil import json import zipfile # http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) def which(program): import os fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): path = path.strip('"') exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None def cexec_fail_exit(args, code, stdout, stderr): if code != 0: print('Fail to exec %s'%args) print(stdout) print(stderr) exit(code) def cexec(args, callback = cexec_fail_exit, addPath = None, exitcode=1): env = None if addPath: import copy env = copy.copy(os.environ) env['PATH'] = addPath + os.path.pathsep + env['PATH'] p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env) output, err = p.communicate() code = p.returncode if code and exitcode: code = exitcode if callback: callback(args, code, output, err) return output def curl(url, body=None, ignoreError=False, exitcode=1): import sys try: if sys.version_info >= (3, 0): import urllib.request return urllib.request.urlopen(url, data=body).read().decode('utf-8').strip() else: import urllib2 return urllib2.urlopen(url, data=body).read().decode('utf-8').strip() except Exception as e: if ignoreError: return None else: print(e) exit(exitcode) def open_as_text(path): if not path or not os.path.isfile(path): return '' with io.open(path, 'r', errors='replace') as f: data = f.read() return data print('fail to open %s'%path) return '' def is_gradle_project(dir): return os.path.isfile(os.path.join(dir, 'build.gradle')) def parse_properties(path): return os.path.isfile(path) and dict(line.strip().split('=') for line in open(path) if ('=' in line and not line.startswith('#'))) or {} def balanced_braces(arg): if '{' not in arg: return '' chars = [] n = 0 for c in arg: if c == '{': if n > 0: chars.append(c) n += 1 elif c == '}': n -= 1 if n > 0: chars.append(c) elif n == 0: return ''.join(chars).lstrip().rstrip() elif n > 0: chars.append(c) return '' def remove_comments(str): # remove comments in groovy return re.sub(r'''(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)''', '', str) def __deps_list_eclipse(list, project): prop = parse_properties(os.path.join(project, 'project.properties')) for i in range(1,100): dep = prop.get('android.library.reference.%d' % i) if dep: absdep = os.path.abspath(os.path.join(project, dep)) __deps_list_eclipse(list, absdep) if not absdep in list: list.append(absdep) def __deps_list_gradle(list, project): str = open_as_text(os.path.join(project, 'build.gradle')) str = remove_comments(str) ideps = [] # for depends in re.findall(r'dependencies\s*\{.*?\}', str, re.DOTALL | re.MULTILINE): for m in re.finditer(r'dependencies\s*\{', str): depends = balanced_braces(str[m.start():]) for proj in re.findall(r'''compile\s+project\s*\(.*['"]:(.+)['"].*\)''', depends): ideps.append(proj.replace(':', os.path.sep)) if len(ideps) == 0: return path = project for i in range(1, 3): path = os.path.abspath(os.path.join(path, os.path.pardir)) b = True deps = [] for idep in ideps: dep = os.path.join(path, idep) if not os.path.isdir(dep): b = False break deps.append(dep) if b: for dep in deps: __deps_list_gradle(list, dep) if not dep in list: list.append(dep) break def deps_list(dir): if is_gradle_project(dir): list = [] __deps_list_gradle(list, dir) return list else: list = [] __deps_list_eclipse(list, dir) return list def manifestpath(dir): if os.path.isfile(os.path.join(dir, 'AndroidManifest.xml')): return os.path.join(dir, 'AndroidManifest.xml') if os.path.isfile(os.path.join(dir, 'src', 'main', 'AndroidManifest.xml')): return os.path.join(dir, 'src', 'main', 'AndroidManifest.xml') def package_name(dir): path = manifestpath(dir) data = open_as_text(path) for pn in re.findall('package=\"([\w\d_\.]+)\"', data): return pn def get_apk_path(dir): if not is_gradle_project(dir): apkpath = os.path.join(dir,'bin') else: apkpath = os.path.join(dir,'build','outputs','apk') #Get the lastmodified *.apk file maxt = 0 maxd = None for dirpath, dirnames, files in os.walk(apkpath): for fn in files: if fn.endswith('.apk') and not fn.endswith('-unaligned.apk') and not fn.endswith('-unsigned.apk'): lastModified = os.path.getmtime(os.path.join(dirpath, fn)) if lastModified > maxt: maxt = lastModified maxd = os.path.join(dirpath, fn) return maxd def package_name_fromapk(dir, sdkdir): #Get the package name from maxd aaptpath = get_aapt(sdkdir) if aaptpath: apkpath = get_apk_path(dir) if apkpath: aaptargs = [aaptpath, 'dump','badging', apkpath] output = cexec(aaptargs, callback=None) for pn in re.findall('package: name=\'([^\']+)\'', output): return pn return package_name(dir) def get_latest_packagename(dirlist,sdkdir): maxt = 0 maxd = None for dir in dirlist: if dir: apkfile= get_apk_path(dir) if apkfile: lastModified = os.path.getmtime(apkfile) if lastModified > maxt: maxt = lastModified maxd = dir if maxd: return package_name_fromapk(maxd,sdkdir) def isResName(name): if name=='drawable' or name.startswith('drawable-'): return 2 if name=='layout' or name.startswith('layout-'): return 2 if name=='values' or name.startswith('values-'): return 2 if name=='anim' or name.startswith('anim-'): return 1 if name=='color' or name.startswith('color-'): return 1 if name=='menu' or name.startswith('menu-'): return 1 if name=='raw' or name.startswith('raw-'): return 1 if name=='xml' or name.startswith('xml-'): return 1 if name=='mipmap' or name.startswith('mipmap-'): return 1 if name=='animator' or name.startswith('animator-'): return 1 return 0 def countResDir(dir): c = 0 d = 0 if os.path.isdir(dir): for subd in os.listdir(dir): v = isResName(subd) if v>1: d+=1 if v>0: c+=1 if d==0: return 0 return c def countAssetDir(dir): a = 0 if os.path.isdir(dir): for subd in os.listdir(dir): if not subd.startswith('.'): a+=1 return a def resdir(dir): dir1 = os.path.join(dir, 'res') dir2 = os.path.join(dir, 'src', 'main', 'res') a = countResDir(dir1) b = countResDir(dir2) if b==0 and a==0: return None elif b>a: return dir2 else: return dir1 def assetdir(dir): dir1 = os.path.join(dir, 'assets') dir2 = os.path.join(dir, 'src', 'main', 'assets') a = countAssetDir(dir1) b = countAssetDir(dir2) if b==0 and a==0: return None elif b>a: return dir2 else: return dir1 def get_asset_from_apk(apk_filename, dest_dir): with zipfile.ZipFile(apk_filename) as zf: for member in zf.infolist(): path = dest_dir if member.filename.startswith('assets/'): zf.extract(member,path) def countSrcDir2(dir, lastBuild=0, list=None): count = 0 lastModified = 0 for dirpath, dirnames, files in os.walk(dir): if re.findall(r'[/\\+]androidTest[/\\+]', dirpath) or '/.' in dirpath: continue for fn in files: if fn.endswith('.java'): count += 1 mt = os.path.getmtime(os.path.join(dirpath, fn)) lastModified = max(lastModified, mt) if list!=None and mt>lastBuild: list.append(os.path.join(dirpath, fn)) return (count, lastModified) def srcdir2(dir, lastBuild=0, list=None): for srcdir in [os.path.join(dir, 'src', 'main', 'java'), os.path.join(dir, 'src')]: olist = None if list!=None: olist = [] (count, lastModified) = countSrcDir2(srcdir, lastBuild=lastBuild, list=olist) if count>0: if list!=None: list.extend(olist) return (srcdir, count, lastModified) return (None, 0, 0) def libdir(dir): ddir = os.path.join(dir, 'libs') if os.path.isdir(ddir): return ddir else: return None def is_launchable_project(dir): if is_gradle_project(dir): data = open_as_text(os.path.join(dir, 'build.gradle')) data = remove_comments(data) if re.findall(r'''apply\s+plugin:\s*['"]com.android.application['"]''', data, re.MULTILINE): return True elif os.path.isfile(os.path.join(dir, 'project.properties')): data = open_as_text(os.path.join(dir, 'project.properties')) if re.findall(r'''^\s*target\s*=.*$''', data, re.MULTILINE) and not re.findall(r'''^\s*android.library\s*=\s*true\s*$''', data, re.MULTILINE): return True return False def __append_project(list, dir, depth): if package_name(dir): list.append(dir) elif depth > 0: for cname in os.listdir(dir): if cname=='build' or cname=='bin': continue cdir = os.path.join(dir, cname) if os.path.isdir(cdir): __append_project(list, cdir, depth-1) def list_projects(dir): list = [] if os.path.isfile(os.path.join(dir, 'settings.gradle')): data = open_as_text(os.path.join(dir, 'settings.gradle')) for line in re.findall(r'''include\s*(.+)''', data): for proj in re.findall(r'''[\s,]+['"](.*?)['"]''', ','+line): dproj = (proj.startswith(':') and proj[1:] or proj).replace(':', os.path.sep) cdir = os.path.join(dir, dproj) if package_name(cdir): list.append(cdir) else: __append_project(list, dir, 2) return list def list_aar_projects(dir, deps): pnlist = [package_name(i) for i in deps] pnlist.append(package_name(dir)) list1 = [] if os.path.isdir(os.path.join(dir, 'build', 'intermediates', 'incremental', 'mergeResources')): for dirpath, dirnames, files in os.walk(os.path.join(dir, 'build', 'intermediates', 'incremental', 'mergeResources')): if re.findall(r'[/\\+]androidTest[/\\+]', dirpath): continue for fn in files: if fn=='merger.xml': data = open_as_text(os.path.join(dirpath, fn)) for s in re.findall(r'''path="([^"]+)"''', data): (parent, child) = os.path.split(s) if child.endswith('.xml') or child.endswith('.png') or child.endswith('.jpg'): (parent, child) = os.path.split(parent) if isResName(child) and not parent in list1: list1.append(parent) elif os.path.isdir(s) and not s in list1 and countResDir(s) > 0: list1.append(s) list2 = [] for ppath in list1: parpath = os.path.abspath(os.path.join(ppath, os.pardir)) pn = package_name(parpath) if pn and not pn in pnlist: list2.append(ppath) return list2 def get_android_jar(path): if not os.path.isdir(path): return None platforms = os.path.join(path, 'platforms') if not os.path.isdir(platforms): return None api = 0 result = None for pd in os.listdir(platforms): pd = os.path.join(platforms, pd) if os.path.isdir(pd) and os.path.isfile(os.path.join(pd, 'source.properties')) and os.path.isfile(os.path.join(pd, 'android.jar')): s = open_as_text(os.path.join(pd, 'source.properties')) m = re.search(r'^AndroidVersion.ApiLevel\s*[=:]\s*(.*)$', s, re.MULTILINE) if m: a = int(m.group(1)) if a > api: api = a result = os.path.join(pd, 'android.jar') return result def get_adb(path): execname = os.name=='nt' and 'adb.exe' or 'adb' if os.path.isdir(path) and is_exe(os.path.join(path, 'platform-tools', execname)): return os.path.join(path, 'platform-tools', execname) def get_aapt(path): execname = os.name=='nt' and 'aapt.exe' or 'aapt' if os.path.isdir(path) and os.path.isdir(os.path.join(path, 'build-tools')): btpath = os.path.join(path, 'build-tools') minv = LooseVersion('0') minp = None for pn in os.listdir(btpath): if is_exe(os.path.join(btpath, pn, execname)): if LooseVersion(pn) > minv: minv = LooseVersion(pn) minp = os.path.join(btpath, pn, execname) return minp def get_dx(path): execname = os.name=='nt' and 'dx.bat' or 'dx' if os.path.isdir(path) and os.path.isdir(os.path.join(path, 'build-tools')): btpath = os.path.join(path, 'build-tools') minv = LooseVersion('0') minp = None for pn in os.listdir(btpath): if is_exe(os.path.join(btpath, pn, execname)): if LooseVersion(pn) > minv: minv = LooseVersion(pn) minp = os.path.join(btpath, pn, execname) return minp def get_android_sdk(dir, condf = get_android_jar): s = open_as_text(os.path.join(dir, 'local.properties')) m = re.search(r'^sdk.dir\s*[=:]\s*(.*)$', s, re.MULTILINE) if m: val = m.group(1).replace('\\:', ':').replace('\\=', '=').replace('\\\\', '\\') if os.path.isdir(val) and condf(val): return val path = os.getenv('ANDROID_HOME') if path and os.path.isdir(path) and condf(path): return path path = os.getenv('ANDROID_SDK') if path and os.path.isdir(path) and condf(path): return path # mac path = os.path.expanduser('~/Library/Android/sdk') if path and os.path.isdir(path) and condf(path): return path # windows path = os.path.expanduser('~/AppData/Local/Android/sdk') if path and os.path.isdir(path) and condf(path): return path def get_javac(dir): execname = os.name=='nt' and 'javac.exe' or 'javac' if dir and os.path.isfile(os.path.join(dir, 'bin', execname)): return os.path.join(dir, 'bin', execname) wpath = which(execname) if wpath: return wpath path = os.getenv('JAVA_HOME') if path and is_exe(os.path.join(path, 'bin', execname)): return os.path.join(path, 'bin', execname) if os.name=='nt': btpath = 'C:\\Program Files\\Java' if os.path.isdir(btpath): minv = '' minp = None for pn in os.listdir(btpath): path = os.path.join(btpath, pn, 'bin', execname) if is_exe(path): if pn > minv: minv = pn minp = path return minp else: for btpath in ['/Library/Java/JavaVirtualMachines', '/System/Library/Java/JavaVirtualMachines']: if os.path.isdir(btpath): minv = '' minp = None for pn in os.listdir(btpath): path = os.path.join(btpath, pn, 'Contents', 'Home', 'bin', execname) if is_exe(path): if pn > minv: minv = pn minp = path if minp: return minp def search_path(dir, filename): dir0 = filename if os.path.sep in filename: dir0 = filename[0:filename.index(os.path.sep)] list = [] for dirpath, dirnames, files in os.walk(dir): if re.findall(r'[/\\+]androidTest[/\\+]', dirpath) or '/.' in dirpath: continue if dir0 in dirnames and os.path.isfile(os.path.join(dirpath, filename)): list.append(dirpath) if len(list) == 1: return list[0] elif len(list) > 1: maxt = 0 maxd = None for ddir in list: lastModified = 0 for dirpath, dirnames, files in os.walk(dir): for fn in files: if fn.endswith('.class'): lastModified = os.path.getmtime(os.path.join(dirpath, fn)) if lastModified > maxt: maxt = lastModified maxd = ddir return maxd else: return os.path.join(dir, 'debug') def get_maven_libs(projs): maven_deps = [] for proj in projs: str = open_as_text(os.path.join(proj, 'build.gradle')) str = remove_comments(str) for m in re.finditer(r'dependencies\s*\{', str): depends = balanced_braces(str[m.start():]) for mvndep in re.findall(r'''compile\s+['"](.+:.+:.+)(?:@*)?['"]''', depends): mvndeps = mvndep.split(':') if not mvndeps in maven_deps: maven_deps.append(mvndeps) return maven_deps def get_maven_jars(libs): if not libs: return [] jars = [] maven_path_prefix = [] # ~/.gralde/caches gradle_home = os.path.join(os.path.expanduser('~'), '.gradle', 'caches') for dirpath, dirnames, files in os.walk(gradle_home): # search in ~/.gradle/**/GROUP_ID/ARTIFACT_ID/VERSION/**/*.jar for mvndeps in libs: if mvndeps[0] in dirnames: dir1 = os.path.join(dirpath, mvndeps[0], mvndeps[1]) if os.path.isdir(dir1): dir2 = os.path.join(dir1, mvndeps[2]) if os.path.isdir(dir2): maven_path_prefix.append(dir2) else: prefix = mvndeps[2] if '+' in prefix: prefix = prefix[0:prefix.index('+')] maxdir = '' for subd in os.listdir(dir1): if subd.startswith(prefix) and subd>maxdir: maxdir = subd if maxdir: maven_path_prefix.append(os.path.join(dir1, maxdir)) for dirprefix in maven_path_prefix: if dirpath.startswith(dirprefix): for fn in files: if fn.endswith('.jar') and not fn.startswith('.') and not fn.endswith('-sources.jar') and not fn.endswith('-javadoc.jar'): jars.append(os.path.join(dirpath, fn)) break return jars def scan_port(adbpath, pnlist, projlist): port = 0 prodir = None packagename = None for i in range(0,10): cexec([adbpath, 'forward', 'tcp:%d'%(41128+i), 'tcp:%d'%(41128+i)]) output = curl('http://127.0.0.1:%d/packagename'%(41128+i), ignoreError=True) if output and output in pnlist : index = pnlist.index(output) # index of this app in projlist state = curl('http://127.0.0.1:%d/appstate'%(41128+i), ignoreError=True) if state and int(state) >= 2: port = 41128+i prodir = projlist[index] packagename = output break for i in range(0, 10): if (41128+i) != port: cexec([adbpath, 'forward', '--remove', 'tcp:%d'%(41128+i)], callback=None) return port, prodir, packagename if __name__ == "__main__": dir = '.' sdkdir = None jdkdir = None starttime = time.time() if len(sys.argv) > 1: parser = argparse.ArgumentParser() parser.add_argument('--sdk', help='specify Android SDK path') parser.add_argument('--jdk', help='specify JDK path') parser.add_argument('project') args = parser.parse_args() if args.sdk: sdkdir = args.sdk if args.jdk: jdkdir = args.jdk if args.project: dir = args.project projlist = [i for i in list_projects(dir) if is_launchable_project(i)] if not sdkdir: sdkdir = get_android_sdk(dir) if not sdkdir: print('android sdk not found, specify in local.properties or export ANDROID_HOME') exit(2) if not projlist: print('no valid android project found in '+os.path.abspath(dir)) exit(3) pnlist = [package_name_fromapk(i,sdkdir) for i in projlist] portlist = [0 for i in pnlist] adbpath = get_adb(sdkdir) if not adbpath: print('adb not found in %s/platform-tools'%sdkdir) exit(4) port, dir, packagename = scan_port(adbpath, pnlist, projlist) if port == 0: #launch app latest_package = get_latest_packagename(projlist, sdkdir) if latest_package: cexec([adbpath,'shell','monkey','-p',latest_package,'-c','android.intent.category.LAUNCHER','1'], callback=None) for i in range(0, 6): # try 6 times to wait the application launches port, dir, packagename = scan_port(adbpath, pnlist, projlist) if port: break time.sleep(0.25) if port == 0: print('package %s not found, make sure your project is properly setup and running'%(len(pnlist)==1 and pnlist[0] or pnlist)) exit(5) is_gradle = is_gradle_project(dir) android_jar = get_android_jar(sdkdir) if not android_jar: print('android.jar not found !!!\nUse local.properties or set ANDROID_HOME env') exit(7) deps = deps_list(dir) bindir = is_gradle and os.path.join(dir, 'build', 'lcast') or os.path.join(dir, 'bin', 'lcast') # check if the /res and /src has changed lastBuild = 0 rdir = is_gradle and os.path.join(dir, 'build', 'outputs', 'apk') or os.path.join(dir, 'bin') if os.path.isdir(rdir): for fn in os.listdir(rdir): if fn.endswith('.apk') and not '-androidTest' in fn: fpath = os.path.join(rdir, fn) lastBuild = max(lastBuild, os.path.getmtime(fpath)) adeps = [] adeps.extend(deps) adeps.append(dir) latestResModified = 0 latestSrcModified = 0 srcs = [] msrclist = [] assetdirs = [] for dep in adeps: adir = assetdir(dep) if adir: latestModified = os.path.getmtime(adir) for dirpath, dirnames, files in os.walk(adir): for dirname in dirnames: if not dirname.startswith('.'): latestModified = max(latestModified, os.path.getmtime(os.path.join(dirpath, dirname))) for fn in files: if not fn.startswith('.'): fpath = os.path.join(dirpath, fn) latestModified = max(latestModified, os.path.getmtime(fpath)) latestResModified = max(latestResModified, latestModified) if latestModified > lastBuild: assetdirs.append(adir) rdir = resdir(dep) if rdir: for subd in os.listdir(rdir): if os.path.isdir(os.path.join(rdir, subd)) and isResName(subd): for fn in os.listdir(os.path.join(rdir, subd)): fpath = os.path.join(rdir, subd, fn) if os.path.isfile(fpath) and not fn.startswith('.'): latestResModified = max(latestResModified, os.path.getmtime(fpath)) (sdir, scount, smt) = srcdir2(dep, lastBuild=lastBuild, list=msrclist) if sdir: srcs.append(sdir) latestSrcModified = max(latestSrcModified, smt) resModified = latestResModified > lastBuild srcModified = latestSrcModified > lastBuild targets = '' if resModified and srcModified: targets = 'both /res and /src' elif resModified: targets = '/res' elif srcModified: targets = '/src' else: print('%s has no /res or /src changes'%(packagename)) exit(0) if is_gradle: print('cast %s:%d as gradle project with %s changed (v%s)'%(packagename, port, targets, __version__)) else: print('cast %s:%d as eclipse project with %s changed (v%s)'%(packagename, port, targets, __version__)) # prepare to reset if srcModified: curl('http://127.0.0.1:%d/pcast'%port, ignoreError=True) if resModified: binresdir = os.path.join(bindir, 'res') if not os.path.exists(os.path.join(binresdir, 'values')): os.makedirs(os.path.join(binresdir, 'values')) data = curl('http://127.0.0.1:%d/ids.xml'%port,exitcode=8) with open(os.path.join(binresdir, 'values/ids.xml'), 'w') as fp: fp.write(data) data = curl('http://127.0.0.1:%d/public.xml'%port,exitcode=9) with open(os.path.join(binresdir, 'values/public.xml'), 'w') as fp: fp.write(data) #Get the assets path apk_path = get_apk_path(dir) if apk_path: assets_path = os.path.join(bindir,"assets") if os.path.isdir(assets_path): shutil.rmtree(assets_path) get_asset_from_apk(apk_path, bindir) aaptpath = get_aapt(sdkdir) if not aaptpath: print('aapt not found in %s/build-tools'%sdkdir) exit(10) aaptargs = [aaptpath, 'package', '-f', '--auto-add-overlay', '-F', os.path.join(bindir, 'res.zip')] aaptargs.append('-S') aaptargs.append(binresdir) rdir = resdir(dir) if rdir: aaptargs.append('-S') aaptargs.append(rdir) for dep in reversed(deps): rdir = resdir(dep) if rdir: aaptargs.append('-S') aaptargs.append(rdir) if is_gradle: for dep in reversed(list_aar_projects(dir, deps)): aaptargs.append('-S') aaptargs.append(dep) for assetdir in assetdirs: aaptargs.append('-A') aaptargs.append(assetdir) if os.path.isdir(assets_path): aaptargs.append('-A') aaptargs.append(assets_path) aaptargs.append('-M') aaptargs.append(manifestpath(dir)) aaptargs.append('-I') aaptargs.append(android_jar) cexec(aaptargs,exitcode=18) with open(os.path.join(bindir, 'res.zip'), 'rb') as fp: curl('http://127.0.0.1:%d/pushres'%port, body=fp.read(),exitcode=11) if srcModified: vmversion = curl('http://127.0.0.1:%d/vmversion'%port, ignoreError=True) if vmversion==None: vmversion = '' if vmversion.startswith('1'): print('cast dex to dalvik vm is not supported, you need ART in Android 5.0') elif vmversion.startswith('2'): javac = get_javac(jdkdir) if not javac: print('javac is required to compile java code, config your PATH to include javac') exit(12) launcher = curl('http://127.0.0.1:%d/launcher'%port,exitcode = 13) classpath = [android_jar] for dep in adeps: dlib = libdir(dep) if dlib: for fjar in os.listdir(dlib): if fjar.endswith('.jar'): classpath.append(os.path.join(dlib, fjar)) if is_gradle: # jars from maven cache maven_libs = get_maven_libs(adeps) maven_libs_cache_file = os.path.join(bindir, 'cache-javac-maven.json') maven_libs_cache = {} if os.path.isfile(maven_libs_cache_file): try: with open(maven_libs_cache_file, 'r') as fp: maven_libs_cache = json.load(fp) except: pass if maven_libs_cache.get('version') != 1 or not maven_libs_cache.get('from') or sorted(maven_libs_cache['from']) != sorted(maven_libs): if os.path.isfile(maven_libs_cache_file): os.remove(maven_libs_cache_file) maven_libs_cache = {} maven_jars = [] if maven_libs_cache: maven_jars = maven_libs_cache.get('jars') elif maven_libs: maven_jars = get_maven_jars(maven_libs) cache = {'version':1, 'from':maven_libs, 'jars':maven_jars} try: with open(maven_libs_cache_file, 'w') as fp: json.dump(cache, fp) except: pass if maven_jars: classpath.extend(maven_jars) # aars from exploded-aar darr = os.path.join(dir, 'build', 'intermediates', 'exploded-aar') # TODO: use the max version for dirpath, dirnames, files in os.walk(darr): if re.findall(r'[/\\+]androidTest[/\\+]', dirpath) or '/.' in dirpath: continue for fn in files: if fn.endswith('.jar'): classpath.append(os.path.join(dirpath, fn)) # R.class classesdir = search_path(os.path.join(dir, 'build', 'intermediates', 'classes'), launcher and launcher.replace('.', os.path.sep)+'.class' or '$') classpath.append(classesdir) else: # R.class classpath.append(os.path.join(dir, 'bin', 'classes')) binclassesdir = os.path.join(bindir, 'classes') shutil.rmtree(binclassesdir, ignore_errors=True) os.makedirs(binclassesdir) javacargs = [javac, '-target', '1.7', '-source', '1.7', '-encoding', 'UTF-8'] javacargs.append('-cp') javacargs.append(os.pathsep.join(classpath)) javacargs.append('-d') javacargs.append(binclassesdir) javacargs.append('-sourcepath') javacargs.append(os.pathsep.join(srcs)) javacargs.extend(msrclist) # remove all cache if javac fail def remove_cache_and_exit(args, code, stdout, stderr): if code: maven_libs_cache_file = os.path.join(bindir, 'cache-javac-maven.json') if os.path.isfile(maven_libs_cache_file): os.remove(maven_libs_cache_file) cexec_fail_exit(args, code, stdout, stderr) cexec(javacargs, callback=remove_cache_and_exit,exitcode=19) dxpath = get_dx(sdkdir) if not dxpath: print('dx not found in %s/build-tools'%sdkdir) exit(14) dxoutput = os.path.join(bindir, 'classes.dex') if os.path.isfile(dxoutput): os.remove(dxoutput) addPath = None if os.name == 'nt': # fix system32 java.exe issue addPath = os.path.abspath(os.path.join(javac, os.pardir)) cexec([dxpath, '--dex', '--output=%s'%dxoutput, binclassesdir], addPath = addPath,exitcode=20) with open(dxoutput, 'rb') as fp: curl('http://127.0.0.1:%d/pushdex'%port, body=fp.read(),exitcode=15) else: if is_gradle: print('LayoutCast library out of date, please sync your project with gradle') else: print('libs/lcast.jar is out of date, please update') curl('http://127.0.0.1:%d/lcast'%port, ignoreError=True) cexec([adbpath, 'forward', '--remove', 'tcp:%d'%port], callback=None) elapsetime = time.time() - starttime print('finished in %dms'%(elapsetime*1000))