commit c5befd94cb130301cfd6c8ed336fc837b3e7ecf9 Author: debuggerx Date: Tue Jun 23 21:43:19 2020 +0800 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b243c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Files and directories created by pub +.dart_tool/ +.packages + +# Conventional directory for build outputs +build/ + +# Directory created by dartdoc +doc/api/ + +.idea diff --git a/.hooks/after_script.sh b/.hooks/after_script.sh new file mode 100755 index 0000000..ba271e6 --- /dev/null +++ b/.hooks/after_script.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo "test bash after" diff --git a/.hooks/pre_script.sh b/.hooks/pre_script.sh new file mode 100755 index 0000000..49b9827 --- /dev/null +++ b/.hooks/pre_script.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo "test bash pre" diff --git a/README.md b/README.md new file mode 100644 index 0000000..836806b --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Flutter工程条件编译打包脚本 + +## 原理 +通过将 flutter run/build [--option] 命令替换为 bash flutter.sh run/build [--option] ,在原有flutter运行/打包流程前后执行内置及用户自定义脚本,从而实现对打包流程的自定义控制,默认内置功能为根据命令参数中的 --debug/release 以及 --flavor 渠道名,对代码和资源文件的条件编译 + +## 用法语法 +### 代码: +代码中使用形如以下的注释来进行代码块的条件标记 +```dart +void main(List arguments) { + print(1); + // #[release[google]] + // print(2); + // #[release[!asd]] + // print(3); + // #[debug] + // print(4); + // #[release[test]] + // print(5); + // #[[test asd]] + // print(6); + // #[[]] + // print(7) + // #[[]] +} +``` +基本注释标记语法为 **// #[debug/release[flavorName1 flavorName2 ...]]** ,外层方括号内填写debug模式或release模式,可以留空表示同时匹配两种模式;内层方括号内填写flavor名的列表,打包命令中 --flavor flavorName 参数传递进来的渠道名在列表中则匹配成功。 + + 特殊的,如果渠道名列表中的名称前加'!'表示取反,命令参数中传入的渠道名不在该列表中时匹配成功(ps: 注意普通渠道名条件列表与带取反的列表不能同时存在) + +注释标记后紧跟该条件下的代码,并注意用 '// ' 进行代码注释,脚本解析时从上到下,某个注释标记的条件匹配成功,就会将紧跟的代码片段覆盖最近的默认代码块,并忽略掉两者之间的其他条件代码块。 +默认代码块的前后需要用 '// #[[]]' 注释作为标记。 + +### 文件: +类似代码中注释条件标记的语法,文件名中使用形如 'abc[debug/release[flavorName1 flavorName2 ...]].xxx' 的形式进行文件命名,并用对应的 'abc.xxx' 文件名作为默认情况下使用的文件,即可在编译运行的时候自动根据传入参数将合适的文件替换为实际使用的文件,并在编译完成后自动还原。 + +# **WIP** \ No newline at end of file diff --git a/bin/after_script.dart b/bin/after_script.dart new file mode 100644 index 0000000..4959228 --- /dev/null +++ b/bin/after_script.dart @@ -0,0 +1,16 @@ +import 'dart:io'; + +void main(List arguments) { + print("running default after_script."); + var rootDir = Directory('./'); + rootDir.listSync(recursive: true).forEach((p) { + if (p.path.endsWith('.bak')) { + File(p.path.substring(0, p.path.length - 4)).deleteSync(); + p.renameSync(p.path.substring(0, p.path.length - 4)); + } + if (p.path.endsWith('.default')) { + File(p.path.substring(0, p.path.length - 8)).deleteSync(); + p.renameSync(p.path.substring(0, p.path.length - 8)); + } + }); +} diff --git a/bin/parse_arguments.dart b/bin/parse_arguments.dart new file mode 100644 index 0000000..6c19ef2 --- /dev/null +++ b/bin/parse_arguments.dart @@ -0,0 +1,27 @@ +class Args { + Args(this.mode, this.flavor); + + String mode; + String flavor; + + @override + String toString() { + return 'Current mode is $mode, and flavor is ${flavor == '' ? 'default' : flavor}'; + } +} + +Args parse(arguments) { + var args = Args('debug', 'default'); + for (var value in arguments) { + if (value == '--release') { + args.mode = 'release'; + } else if (value == '--debug') { + args.mode = 'debug'; + } + if (value == '--flavor') { + args.flavor = arguments[arguments.indexOf('--flavor') + 1]; + } + } + print(args); + return args; +} diff --git a/bin/path_utils.dart b/bin/path_utils.dart new file mode 100644 index 0000000..ef4fe43 --- /dev/null +++ b/bin/path_utils.dart @@ -0,0 +1,11 @@ +import 'dart:io'; + +class PathUtils { + static String baseName(FileSystemEntity file) { + return file.path.substring(file.parent.path.length + 1); + } +} + +void main() { + print(PathUtils.baseName(File('./path_utils.dart'))); +} \ No newline at end of file diff --git a/bin/pre_script.dart b/bin/pre_script.dart new file mode 100644 index 0000000..f706961 --- /dev/null +++ b/bin/pre_script.dart @@ -0,0 +1,120 @@ +import 'dart:io'; +import 'parse_arguments.dart'; +import 'path_utils.dart'; + +String mode; +List flavors; +Args args; + +enum STATE { + none, + notMatch, + caching, + cached, + replace, +} + +void main(List arguments) { + print("Running default pre_script."); + args = parse(arguments); + + var rootDir = Directory('./'); + rootDir.listSync().forEach(walkPath); +} + +File file; +StringBuffer sb = StringBuffer(); +StringBuffer tmp = StringBuffer(); +STATE state = STATE.none; +RegExp re = RegExp(r'// #\[(debug|release)?(?:\[(.*)\])?\]'); +RegExp pathRe = RegExp(r'\[(debug|release)?(?:\[(.*)\])?\]'); +Match ma; + +void walkPath(FileSystemEntity path) { + var stat = path.statSync(); + if (stat.type == FileSystemEntityType.directory) { + Directory(path.path).listSync().forEach(walkPath); + } else if (stat.type == FileSystemEntityType.file) { + ma = pathRe.firstMatch(PathUtils.baseName(path)); + if (ma != null) { + mode = ma.group(1); + flavors = (ma.group(2) ?? '').split(' ').where((ele) => ele.trim() != '').toList(); + if (checkModeAndFlavors()) { + var defaultFilePath = path.path.replaceFirst(ma.group(0), ''); + var defaultFile = File(defaultFilePath); + if (defaultFile.existsSync()) { + defaultFile.renameSync(defaultFilePath + '.default'); + } + File(path.path).copySync(defaultFilePath); + } + return; + } + + // 文件名不带规则,逐行读取后进行处理 + file = File(path.path); + sb.clear(); + try { + file.readAsLinesSync().forEach((line) { + ma = re.firstMatch(line); + if (ma != null) { + mode = ma.group(1); + flavors = (ma.group(2) ?? '').split(' ').where((ele) => ele.trim() != '').toList(); + + // 默认的代码块 + if (mode == null && flavors.length == 0) { + // 前一状态为replace,此时应该将缓存内容写入替换区域,并将状态复位为none + if (state == STATE.replace) { + sb.write(tmp); + state = STATE.none; + } + // 前一状态为缓存中或缓存结束,此时应该将状态改为替换,用于跳过默认内容 + else if (state == STATE.caching || state == STATE.cached) { + state = STATE.replace; + } + } else { + // 带有条件的替换代码块 + if (state == STATE.caching) { + // 此时状态为缓存中,将状态改为已缓存 + state = STATE.cached; + } else if (state == STATE.none || state == STATE.notMatch) { + // 此时状态为none或者未匹配,需要进行匹配判断 + if (checkModeAndFlavors()) { + state = STATE.caching; + tmp.clear(); + } else { + state = STATE.notMatch; + } + } + } + } else { + // none状态时直接将line写入sb + if (state == STATE.none) + sb.writeln(line); + // 缓存中状态,将用于替换的内容移除注释后写入缓存 + else if (state == STATE.caching) tmp.writeln(line.replaceFirst('// ', '')); + // 这样就跳过了没有匹配上的替换代码块和默认内容 + } + }); + file.renameSync(path.path + '.bak'); + File(path.path).writeAsStringSync(sb.toString(), flush: true); + } catch (e) { + if (!(e is FileSystemException)) { + rethrow; + } + } + } +} + +bool checkModeAndFlavors() { + if (mode != null && mode != args.mode) { + return false; + } + + if (flavors.isEmpty) return true; + + if (flavors[0].startsWith('!')) { + return !flavors.map((e) => e.replaceFirst('!', '')).toList().contains(args.flavor); + } else { + return flavors.contains(args.flavor); + } +} diff --git a/flutter.sh b/flutter.sh new file mode 100644 index 0000000..97ac71e --- /dev/null +++ b/flutter.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +SCRIPT_ABS=$(readlink -f "$0") +SCRIPT_DIR=$(dirname "$SCRIPT_ABS") + +# shellcheck disable=SC2005 +DART_EXE=$(echo "$(command -v flutter)" | awk '{print substr($0,0,length()-7)}')cache/dart-sdk/bin/dart + +if [[ ! -x "$DART_EXE" ]]; then + echo "Can't find dart executable file !" + return 1 +fi + + +if [[ -f "./.hooks/pre_script.dart" ]]; then + ${DART_EXE} ./.hooks/pre_script.dart "$@" +fi + +if [[ -f "./pre_script.dart" ]]; then + ${DART_EXE} ./.hooks/pre_script.dart "$@" +fi + +if [[ -f "./.hooks/pre_script.sh" ]]; then + ./.hooks/pre_script.sh "$@" +fi + +if [[ -f "./pre_script.sh" ]]; then + ./pre_script.sh "$@" +fi + +${DART_EXE} "$SCRIPT_DIR"/bin/pre_script.dart "$@" + +#flutter "$@" + +${DART_EXE} "$SCRIPT_DIR"/bin/after_script.dart "$@" + +if [[ -f "./.hooks/after_script.dart" ]]; then + ${DART_EXE} ./.hooks/after_script.dart "$@" +fi + +if [[ -f "./after_script.dart" ]]; then + ${DART_EXE} ./after_script.dart "$@" +fi + +if [[ -f "./.hooks/after_script.sh" ]]; then + ./.hooks/after_script.sh "$@" +fi + +if [[ -f "./after_script.sh" ]]; then + ./after_script.sh "$@" +fi + diff --git a/test/avatar.png b/test/avatar.png new file mode 100644 index 0000000..b8f5e2c Binary files /dev/null and b/test/avatar.png differ diff --git a/test/avatar[release].png b/test/avatar[release].png new file mode 100644 index 0000000..6b6d162 Binary files /dev/null and b/test/avatar[release].png differ diff --git a/test/test.dart b/test/test.dart new file mode 100644 index 0000000..3581f28 --- /dev/null +++ b/test/test.dart @@ -0,0 +1,16 @@ +void main(List arguments) { + print(1); + // #[release[google]] + // print(2); + // #[release[!asd]] + // print(3); + // #[debug] + // print(4); + // #[release[test]] + // print(5); + // #[[test asd]] + // print(6); + // #[[]] + // print(7) + // #[[]] +}