Freeline適配kotlin-2 源碼修改
在上一部分我們梳理了java增量的邏輯
整體來講就是:
- 掃描變化的java文件
- 對它們進行單獨的javac編譯
- 然后打包成增量dex
- merge進去
其實現(xiàn)在大家基本上已經(jīng)有思路了
javac換成kotlinc就ok了嘛
磨刀霍霍向牛羊
打開我心愛的pycharm
1.增加對kotlin修改的掃描
self._changed_files[module_name] = {'libs': [], 'assets': [], 'res': [], 'src': [], 'manifest': [],
'config': [], 'so': [], 'cpp': [], 'kotlin': []}
# kotlin占坑
現(xiàn)在保存修改文件狀態(tài)的map里面增加kotlin字段 準備放置修改的kotlin的文件路徑
然后我們修改掃描代碼文件修改的方法
# scan src
src_dirs = self._config['project_source_sets'][module_name]['main_src_directory']
for src_dir in src_dirs:
if os.path.exists(src_dir):
for dirpath, dirnames, files in os.walk(src_dir):
if re.findall(r'[/\\+]androidTest[/\\+]', dirpath) or '/.' in dirpath:
continue
for fn in files:
# 之前只有對java后綴名文件的檢查
if fn.endswith('java'):
if fn.endswith('package-info.java') or fn.endswith('BuildConfig.java'):
continue
fpath = os.path.join(dirpath, fn)
if self.__check_changes(module_name, fpath, module_cache):
self._changed_files[module_name]['src'].append(fpath)
# 添加kotlin的增量檢查
elif fn.endswith('kt'):
fpath = os.path.join(dirpath, fn)
if self.__check_changes(module_name, fpath, module_cache):
self._changed_files[module_name]['kotlin'].append(fpath)
check_changes這個方法我們在上一節(jié)就已經(jīng)介紹了 先檢查修改時間在檢查md5
這些操作后 我們修改一個kotlin文件 然后跑一遍freeline的調(diào)試模式
{
"build_info": {
"last_clean_build_time": 1503822329.0,
"is_root_config_changed": false
},
"projects": {
"app": {
"src": [],
"so": [],
"assets": [],
"kotlin": [
"/Users/retrox/AndroidStudioProjects/One/app/src/main/java/com/twtstudio/one/view/VActivity.kt"
],
"libs": [],
"res": [],
"config": [],
"cpp": [],
"manifest": []
}
}
}
在kotlin數(shù)組里面已經(jīng)出現(xiàn)了我們修改的文件了
我們下一步要做的就是:對它進行kotlinc
對kt文件單獨增量編譯
流程類似于對java的增量
新建一個TaskCommand類用于對kotlin的增量編譯
class GradleIncKotlincCommand(IncKotlincCommand):
def __init__(self, module_name, invoker):
IncKotlincCommand.__init__(self, module_name, invoker)
def execute(self):
self._invoker.check_r_md5() # check if R.java has changed
# self._invoker.check_other_modules_resources()
should_run_kotlinc_task = self._invoker.check_kotlinc_task()
if not should_run_kotlinc_task:
self.debug('no need to execute kotlinc ')
return
self.debug('start to execute kotlinc command...')
self._invoker.append_r_file()
self._invoker.fill_classpaths()
self._invoker.clean_dex_cache()
self._invoker.run_kotlinc_task()
其實和java增量沒有什么區(qū)別 因為沒有加入kotlin的復雜特性支持(如kapt)所以某種角度看比java增量還簡單
然后我們看看run_kotlinc_task這個方法
#運行增量kotlinc
def run_kotlinc_task(self):
kotlincargs = self._generate_kotlin_compile_args()
self.debug('kotlinc exec: ' + ' '.join(kotlincargs))
output, err, code = cexec(kotlincargs, callback=None)
if code != 0:
raise FreelineException('incremental kotlinc compile failed.', '{}\n{}'.format(output, err))
這個方法只貼了核心代碼 當然還有一些R文件的增量什么的需要處理下(我只展示和kotlin相關的部分)
到了_generate_kotlin_compile_args這個方法
#kotlin增量命令的生成 暫時直接kotlinc了
def _generate_kotlin_compile_args(self, extra_javac_args_enabled=False):
# javacargs = [self._javac]
# test environment kotlinc
kotlincargs = ['kotlinc']
arguments = []
arguments.append('-cp')
# todo 適配classpath
self._classpaths.append('${projectDir}/app/build/tmp/kotlin-classes/debug')
arguments.append(os.pathsep.join(self._classpaths))
for fpath in self._changed_files['kotlin']:
arguments.append(fpath)
arguments.append('-d')
arguments.append(self._finder.get_patch_classes_cache_dir())
kotlincargs.extend(arguments)
return kotlincargs
kotlin的classpath要增加一些 因為kotlin的字節(jié)碼文件是單獨存放的 需要把那些字節(jié)碼文件也納入到classpath中來
然后把kotlin增量的操作添加到freeline執(zhí)行的操作鏈上
class GradleCompileCommand(CompileCommand):
def __init__(self, module, invoker):
self._module = module
CompileCommand.__init__(self, 'gradle_{}_compile_command'.format(module), invoker)
def _setup(self):
# 加一個kotlin即可
self.add_command(GradleIncKotlincCommand(self._module, self._invoker))
self.add_command(GradleIncJavacCommand(self._module, self._invoker))
self.add_command(GradleIncDexCommand(self._module, self._invoker))
其實就已經(jīng)很清晰了 增量kotlinc然后把字節(jié)碼存放到freeline文件夾 然后再統(tǒng)一dex打增量包 推送到手機
Dex增量打包
Dex增量工具會自己把字節(jié)碼文件夾的生成字節(jié)碼打包 而我們適配kotlin要做的是
設置標志位 為什么呢?
因為之前增量dex的標志位只針對了java的情況 修改kotlin并不能觸發(fā)標志位的變化
所以只改kotlin的話 dex是拒接增量打包推送的
所以我們要小小的修改下
def _mark_changed_flag(self):
info = self._changed_files.values()
cache_dir = self._config['build_cache_dir']
for bundle in info:
if not android_tools.is_src_changed(cache_dir) and len(bundle['src']) > 0:
android_tools.mark_src_changed(cache_dir)
if not android_tools.is_res_changed(cache_dir) and len(bundle['res']) > 0:
android_tools.mark_res_changed(cache_dir)
# kotlin增量flag
if not android_tools.is_src_changed(cache_dir) and len(bundle['kotlin']) > 0:
android_tools.mark_src_changed(cache_dir)
然后修改下kotlin 跑下freeline 成功增量!
freeline兼容適配之路還有很長要走
不如kapt kotlin和java混寫的相互依賴什么的 還有各種蜜汁問題
不過一個優(yōu)秀的增量工具就是這樣子一步步走下來的
希望這篇文章可以對你有所啟發(fā)