|
| 1 | +#!/usr/bin/env bash |
| 2 | +# fast-compile-fe.sh — 增量编译 FE 并更新 doris-fe.jar |
| 3 | +# |
| 4 | +# 用法: |
| 5 | +# ./fast-compile-fe.sh # 自动检测改动文件并编译 |
| 6 | +# ./fast-compile-fe.sh Foo.java Bar.java # 指定编译某些文件 |
| 7 | +# |
| 8 | +# 依赖: javac, jar(JDK 8+),mvn(首次获取 classpath 时需要) |
| 9 | + |
| 10 | +set -euo pipefail |
| 11 | + |
| 12 | +DORIS_HOME="$(cd "$(dirname "$0")/.." && pwd)" |
| 13 | +FE_CORE="$DORIS_HOME/fe/fe-core" |
| 14 | +SRC_ROOT="$FE_CORE/src/main/java" |
| 15 | +TARGET_CLASSES="$FE_CORE/target/classes" |
| 16 | +TARGET_LIB="$FE_CORE/target/lib" |
| 17 | +OUTPUT_JAR="$DORIS_HOME/output/fe/lib/doris-fe.jar" |
| 18 | +TARGET_JAR="$FE_CORE/target/doris-fe.jar" |
| 19 | +CP_CACHE="$FE_CORE/target/fast-compile-cp.txt" |
| 20 | + |
| 21 | +# 生成的源码目录(protobuf/thrift/annotation processor 生成的 java 文件) |
| 22 | +GEN_SOURCES=( |
| 23 | + "$FE_CORE/target/generated-sources/doris" |
| 24 | + "$FE_CORE/target/generated-sources/org" |
| 25 | + "$FE_CORE/target/generated-sources/java" |
| 26 | + "$FE_CORE/target/generated-sources/annotations" |
| 27 | + "$FE_CORE/target/generated-sources/antlr4" |
| 28 | +) |
| 29 | + |
| 30 | +# ─── 颜色输出 ──────────────────────────────────────────────────────────────── |
| 31 | +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' |
| 32 | +info() { echo -e "${GREEN}[INFO]${NC} $*"; } |
| 33 | +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } |
| 34 | +error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } |
| 35 | + |
| 36 | +# ─── 检查环境 ───────────────────────────────────────────────────────────────── |
| 37 | +check_env() { |
| 38 | + if [[ ! -d "$TARGET_CLASSES" ]]; then |
| 39 | + error "target/classes 不存在,请先完整编译一次: cd $DORIS_HOME && mvn package -pl fe/fe-core -DskipTests -T4" |
| 40 | + exit 1 |
| 41 | + fi |
| 42 | + if [[ ! -f "$OUTPUT_JAR" && ! -f "$TARGET_JAR" ]]; then |
| 43 | + error "doris-fe.jar 不存在,请先完整编译一次" |
| 44 | + exit 1 |
| 45 | + fi |
| 46 | +} |
| 47 | + |
| 48 | +# ─── 获取 classpath(带缓存)───────────────────────────────────────────────── |
| 49 | +get_classpath() { |
| 50 | + # 如果 pom.xml 比缓存新,则重新生成 classpath |
| 51 | + if [[ ! -f "$CP_CACHE" || ! -s "$CP_CACHE" || "$FE_CORE/pom.xml" -nt "$CP_CACHE" ]]; then |
| 52 | + info "生成 classpath 缓存..." |
| 53 | + # 直接使用 target/lib(本次完整构建产生的依赖,比 .m2 仓库更可靠) |
| 54 | + find "$TARGET_LIB" -name "*.jar" | tr '\n' ':' | sed 's/:$//' > "$CP_CACHE" |
| 55 | + info "classpath 缓存已保存到 $CP_CACHE" |
| 56 | + fi |
| 57 | + |
| 58 | + # classpath = 依赖 jars + target/classes(项目内部依赖) |
| 59 | + echo "$(cat "$CP_CACHE"):$TARGET_CLASSES" |
| 60 | +} |
| 61 | + |
| 62 | +# ─── 找出需要编译的 java 文件 ──────────────────────────────────────────────── |
| 63 | +find_stale_java_files() { |
| 64 | + local stale_files=() |
| 65 | + |
| 66 | + while IFS= read -r java_file; do |
| 67 | + # java_file: /path/to/src/main/java/org/apache/doris/Foo.java |
| 68 | + # 转换为 class 文件路径 |
| 69 | + local rel_path="${java_file#$SRC_ROOT/}" # org/apache/doris/Foo.java |
| 70 | + local class_path="$TARGET_CLASSES/${rel_path%.java}.class" |
| 71 | + |
| 72 | + if [[ ! -f "$class_path" ]]; then |
| 73 | + # class 文件不存在,肯定需要编译 |
| 74 | + stale_files+=("$java_file") |
| 75 | + elif [[ "$java_file" -nt "$class_path" ]]; then |
| 76 | + # java 文件比主 class 文件新 |
| 77 | + stale_files+=("$java_file") |
| 78 | + fi |
| 79 | + done < <(find "$SRC_ROOT" -name "*.java") |
| 80 | + |
| 81 | + printf '%s\n' "${stale_files[@]}" |
| 82 | +} |
| 83 | + |
| 84 | +# ─── 编译 java 文件 ─────────────────────────────────────────────────────────── |
| 85 | +compile_files() { |
| 86 | + local classpath="$1" |
| 87 | + shift |
| 88 | + local java_files=("$@") |
| 89 | + |
| 90 | + # 构建源码路径(包含生成的源码目录) |
| 91 | + local source_path="$SRC_ROOT" |
| 92 | + for gen_src in "${GEN_SOURCES[@]}"; do |
| 93 | + [[ -d "$gen_src" ]] && source_path="$source_path:$gen_src" |
| 94 | + done |
| 95 | + |
| 96 | + info "编译 ${#java_files[@]} 个文件..." |
| 97 | + for f in "${java_files[@]}"; do |
| 98 | + echo " → ${f#$DORIS_HOME/}" |
| 99 | + done |
| 100 | + |
| 101 | + # javac 编译 |
| 102 | + javac \ |
| 103 | + -source 8 -target 8 \ |
| 104 | + -encoding UTF-8 \ |
| 105 | + -cp "$classpath" \ |
| 106 | + -sourcepath "$source_path" \ |
| 107 | + -d "$TARGET_CLASSES" \ |
| 108 | + "${java_files[@]}" 2>&1 |
| 109 | + |
| 110 | + info "编译完成" |
| 111 | +} |
| 112 | + |
| 113 | +# ─── 收集需要更新到 jar 的 class 文件 ──────────────────────────────────────── |
| 114 | +collect_updated_classes() { |
| 115 | + local java_files=("$@") |
| 116 | + local class_files=() |
| 117 | + |
| 118 | + for java_file in "${java_files[@]}"; do |
| 119 | + local rel_path="${java_file#$SRC_ROOT/}" |
| 120 | + local class_prefix="$TARGET_CLASSES/${rel_path%.java}" |
| 121 | + local dir |
| 122 | + dir="$(dirname "$class_prefix")" |
| 123 | + local base |
| 124 | + base="$(basename "$class_prefix")" |
| 125 | + |
| 126 | + # 主 class 文件 |
| 127 | + [[ -f "$class_prefix.class" ]] && class_files+=("$class_prefix.class") |
| 128 | + |
| 129 | + # 内部类和匿名类:Foo$Bar.class, Foo$1.class 等 |
| 130 | + while IFS= read -r inner; do |
| 131 | + class_files+=("$inner") |
| 132 | + done < <(find "$dir" -maxdepth 1 -name "${base}\$*.class" 2>/dev/null) |
| 133 | + done |
| 134 | + |
| 135 | + printf '%s\n' "${class_files[@]}" |
| 136 | +} |
| 137 | + |
| 138 | +# ─── 更新 jar ───────────────────────────────────────────────────────────────── |
| 139 | +update_jar() { |
| 140 | + local class_files=("$@") |
| 141 | + |
| 142 | + info "更新 jar(共 ${#class_files[@]} 个 class 文件)..." |
| 143 | + |
| 144 | + # 将 class 文件路径转为相对于 TARGET_CLASSES 的路径,供 jar 命令使用 |
| 145 | + local tmpfile |
| 146 | + tmpfile="$(mktemp)" |
| 147 | + trap "rm -f $tmpfile" EXIT |
| 148 | + |
| 149 | + for cf in "${class_files[@]}"; do |
| 150 | + echo "${cf#$TARGET_CLASSES/}" >> "$tmpfile" |
| 151 | + done |
| 152 | + |
| 153 | + # 在 TARGET_CLASSES 目录下执行 jar uf,使 jar 内路径正确 |
| 154 | + pushd "$TARGET_CLASSES" > /dev/null |
| 155 | + |
| 156 | + # 更新 target/doris-fe.jar |
| 157 | + if [[ -f "$TARGET_JAR" ]]; then |
| 158 | + xargs jar uf "$TARGET_JAR" < "$tmpfile" |
| 159 | + info "已更新 $TARGET_JAR" |
| 160 | + fi |
| 161 | + |
| 162 | + # 更新 output/fe/lib/doris-fe.jar |
| 163 | + if [[ -f "$OUTPUT_JAR" ]]; then |
| 164 | + xargs jar uf "$OUTPUT_JAR" < "$tmpfile" |
| 165 | + info "已更新 $OUTPUT_JAR" |
| 166 | + fi |
| 167 | + |
| 168 | + popd > /dev/null |
| 169 | +} |
| 170 | + |
| 171 | +# ─── 主流程 ─────────────────────────────────────────────────────────────────── |
| 172 | +main() { |
| 173 | + check_env |
| 174 | + |
| 175 | + local java_files=() |
| 176 | + |
| 177 | + if [[ $# -gt 0 ]]; then |
| 178 | + # 用户直接指定文件 |
| 179 | + for arg in "$@"; do |
| 180 | + # 支持相对路径和绝对路径 |
| 181 | + local abs_path |
| 182 | + if [[ "$arg" = /* ]]; then |
| 183 | + abs_path="$arg" |
| 184 | + else |
| 185 | + abs_path="$(pwd)/$arg" |
| 186 | + fi |
| 187 | + if [[ ! -f "$abs_path" ]]; then |
| 188 | + # 尝试在 SRC_ROOT 下搜索 |
| 189 | + local found |
| 190 | + found="$(find "$SRC_ROOT" -name "$(basename "$arg")" | head -1)" |
| 191 | + if [[ -z "$found" ]]; then |
| 192 | + error "文件不存在: $arg" |
| 193 | + exit 1 |
| 194 | + fi |
| 195 | + abs_path="$found" |
| 196 | + fi |
| 197 | + java_files+=("$abs_path") |
| 198 | + done |
| 199 | + info "手动指定 ${#java_files[@]} 个文件" |
| 200 | + else |
| 201 | + # 自动检测改动 |
| 202 | + info "扫描改动的 Java 文件..." |
| 203 | + while IFS= read -r f; do |
| 204 | + [[ -n "$f" ]] && java_files+=("$f") |
| 205 | + done < <(find_stale_java_files) |
| 206 | + |
| 207 | + if [[ ${#java_files[@]} -eq 0 ]]; then |
| 208 | + info "没有发现需要编译的文件,已是最新状态" |
| 209 | + exit 0 |
| 210 | + fi |
| 211 | + info "发现 ${#java_files[@]} 个文件需要重新编译" |
| 212 | + fi |
| 213 | + |
| 214 | + local start_time |
| 215 | + start_time=$(date +%s) |
| 216 | + |
| 217 | + # 获取 classpath |
| 218 | + local classpath |
| 219 | + classpath="$(get_classpath)" |
| 220 | + |
| 221 | + # 编译 |
| 222 | + compile_files "$classpath" "${java_files[@]}" |
| 223 | + |
| 224 | + # 收集 class 文件(含内部类)π |
| 225 | + local class_files=() |
| 226 | + while IFS= read -r cf; do |
| 227 | + [[ -n "$cf" ]] && class_files+=("$cf") |
| 228 | + done < <(collect_updated_classes "${java_files[@]}") |
| 229 | + |
| 230 | + if [[ ${#class_files[@]} -eq 0 ]]; then |
| 231 | + warn "未找到编译产物,跳过 jar 更新" |
| 232 | + exit 0 |
| 233 | + fi |
| 234 | + |
| 235 | + # 更新 jar |
| 236 | + update_jar "${class_files[@]}" |
| 237 | + |
| 238 | + local end_time |
| 239 | + end_time=$(date +%s) |
| 240 | + info "完成!耗时 $((end_time - start_time)) 秒" |
| 241 | +} |
| 242 | + |
| 243 | +main "$@" |
0 commit comments