#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" # shellcheck source=../action/action.sh source "$PROJECT_ROOT/scripts/action/action.sh" LIBRARY_GIT_SOURCED=0 # INTERNAL: library_source_git() # 作用: 按需加载 Git 操作函数。 # 参数: 无。 # 返回: 无。 # 注意: list-ops/list-cmake-ops/list-git-ops/--help 不需要加载 git.sh。Windows 下如果 git.sh 或其换行存在问题,不能影响操作枚举。 library_source_git() { if [[ "$LIBRARY_GIT_SOURCED" == "0" ]]; then # shellcheck source=../git/git.sh source "$PROJECT_ROOT/scripts/git/git.sh" LIBRARY_GIT_SOURCED=1 fi } library_usage() { cat <<'USAGE' usage: library.sh list-ops library.sh list-cmake-ops library.sh list-git-ops library.sh cmake-options --data-dir [--config ] [target...] library.sh --data-dir [--config ] [target...] operations are implemented in Bash. CMake only prepares registry/global data and invokes this script. USAGE } library_list_cmake_self_ops() { # 每个 echo 独立一行,不使用 here-doc 或反斜杠续行,避免 Windows/Git Bash 下 CRLF 影响操作枚举输出。 # 这些是不递归依赖的单对象操作;传入哪个库 id,就只操作哪个库 id。 echo 'source' echo 'build' echo 'install' echo 'auto' echo 'rebuild' echo 'reinstall' echo 'reset' echo 'clean-source' echo 'clean-build' echo 'clean-install' echo 'clean-all' } library_list_git_self_ops() { # Git 操作默认也是单对象操作,不递归依赖。 echo 'git-fetch' echo 'git-pull' echo 'git-checkout' echo 'git-branches' echo 'git-current-branch' } library_list_cmake_ops() { # 查询操作不递归执行,不生成 with_rely_ 版本。 echo 'list-targets' echo 'deps' echo 'cmake-options' library_list_cmake_self_ops library_list_cmake_self_ops | action_list_with_rely_ops_from } library_list_git_ops() { library_list_git_self_ops library_list_git_self_ops | action_list_with_rely_ops_from } library_list_ops() { library_list_cmake_ops library_list_git_ops } # INTERNAL: library_strip_line_cr() # 作用: 去掉从 Windows CRLF 文本文件读取时残留在行尾的 CR。 # 参数: value 是原始行或原始字段。 # 返回: stdout 输出去掉一个行尾 CR 后的值。 # 注意: 只清理文件行尾 CR,不等同于 codec 解码;编码值里的 %0D 会保留给 library_codec_decode 还原。 library_strip_line_cr() { local value="$1" # Windows/Git Bash may read CMake-generated files with CRLF. # Some test/workflow paths can rewrite CRLF repeatedly and produce CRCRLF. # Strip every trailing CR so values never become `cmake.exe\r`, `core\r`, or `%00\r`. while [[ "$value" == *$'\r' ]]; do value="${value%$'\r'}" done printf '%s' "$value" } library_codec_decode() { local value="$1" value="$(library_strip_line_cr "$value")" if [[ "$value" == "%00" ]]; then printf '' return 0 fi value="${value//%0D/$'\r'}" value="${value//%0A/$'\n'}" value="${value//%7C/|}" value="${value//%3B/;}" value="${value//%5C/\\}" value="${value//%25/%}" printf '%s' "$value" } library_map_get_raw() { local file="$1" local key="$2" local line rest line_key value while IFS= read -r line || [[ -n "$line" ]]; do line="$(library_strip_line_cr "$line")" [[ "$line" == K\|* ]] || continue rest="${line#K|}" line_key="${rest%%|*}" value="${rest#*|}" if [[ "$line_key" == "$key" ]]; then printf '%s' "$value" return 0 fi done <"$file" printf '' } library_map_get() { local file="$1" local key="$2" local raw raw="$(library_map_get_raw "$file" "$key")" library_codec_decode "$raw" } library_global_get() { local key="$1" library_map_get "$LIBRARY_DATA_DIR/globals.txt" "$key" } library_registry_graph() { printf '%s/registry/graph.txt' "$LIBRARY_DATA_DIR" } library_registry_map_id() { local target="$1" local line rest node data while IFS= read -r line || [[ -n "$line" ]]; do line="$(library_strip_line_cr "$line")" [[ "$line" == N\|* ]] || continue rest="${line#N|}" node="${rest%%|*}" data="${rest#*|}" if [[ "$node" == "$target" ]]; then printf '%s' "$data" return 0 fi done <"$(library_registry_graph)" printf '' } library_target_node_dir() { local target="$1" local map_id map_id="$(library_registry_map_id "$target")" printf '%s/registry/nodes/%s' "$LIBRARY_DATA_DIR" "$map_id" } library_target_map_file() { local target="$1" printf '%s/map.txt' "$(library_target_node_dir "$target")" } library_registry_get() { local target="$1" local key="$2" library_map_get "$(library_target_map_file "$target")" "$key" } library_target_get_or_global() { local target="$1" local key="$2" local value value="$(library_registry_get "$target" "$key")" if [[ -z "$value" ]]; then value="$(library_global_get "$key")" fi printf '%s' "$value" } library_all_targets() { local line while IFS= read -r line || [[ -n "$line" ]]; do line="$(library_strip_line_cr "$line")" [[ -n "$line" ]] && printf '%s\n' "$line" done <"$LIBRARY_DATA_DIR/build_order.txt" } library_direct_deps() { local target="$1" local line rest from to while IFS= read -r line || [[ -n "$line" ]]; do line="$(library_strip_line_cr "$line")" [[ "$line" == E\|* ]] || continue rest="${line#E|}" from="${rest%%|*}" rest="${rest#*|}" to="${rest%%|*}" if [[ "$from" == "$target" ]]; then printf '%s\n' "$to" fi done <"$(library_registry_graph)" } library_resolve_targets() { if [[ "$#" -eq 0 ]]; then library_all_targets return 0 fi local requested=() local target dep line declare -A want=() requested=("$@") for target in "${requested[@]}"; do want["$target"]=1 if [[ -f "$LIBRARY_DATA_DIR/dependencies/$target.txt" ]]; then while IFS= read -r dep || [[ -n "$dep" ]]; do dep="$(library_strip_line_cr "$dep")" [[ -n "$dep" ]] && want["$dep"]=1 done <"$LIBRARY_DATA_DIR/dependencies/$target.txt" fi done while IFS= read -r line || [[ -n "$line" ]]; do line="$(library_strip_line_cr "$line")" [[ -n "$line" ]] || continue if [[ -n "${want[$line]+x}" ]]; then printf '%s\n' "$line" fi done <"$LIBRARY_DATA_DIR/build_order.txt" } library_resolve_self_targets() { if [[ "$#" -eq 0 ]]; then library_all_targets return 0 fi local target for target in "$@"; do target="$(library_strip_line_cr "$target")" [[ -n "$target" ]] && printf '%s\n' "$target" done } library_resolve_dependencies() { if [[ "$#" -eq 0 ]]; then library_all_targets return 0 fi local target dep line declare -A want=() for target in "$@"; do if [[ -f "$LIBRARY_DATA_DIR/dependencies/$target.txt" ]]; then while IFS= read -r dep || [[ -n "$dep" ]]; do dep="$(library_strip_line_cr "$dep")" [[ -n "$dep" ]] && want["$dep"]=1 done <"$LIBRARY_DATA_DIR/dependencies/$target.txt" fi done while IFS= read -r line || [[ -n "$line" ]]; do line="$(library_strip_line_cr "$line")" [[ -n "$line" ]] || continue if [[ -n "${want[$line]+x}" ]]; then printf '%s\n' "$line" fi done <"$LIBRARY_DATA_DIR/build_order.txt" } library_cache_root() { local key="$1" local value value="$(library_global_get "$key")" printf '%s' "$value" } library_source_dir() { printf '%s/%s' "$(library_cache_root SOURCE_CACHE_ROOT)" "$1" } library_build_dir() { printf '%s/%s' "$(library_cache_root BUILD_CACHE_ROOT)" "$1" } library_install_dir() { printf '%s/%s' "$(library_cache_root INSTALL_CACHE_ROOT)" "$1" } library_lock_file() { local step="$1" local target="$2" local dir case "$step" in source) dir="$(library_source_dir "$target")" ;; build) dir="$(library_build_dir "$target")" ;; install) dir="$(library_install_dir "$target")" ;; *) dir="$(library_target_node_dir "$target")" ;; esac printf '%s/%s.success' "$dir" "$step" } library_log_file() { local step="$1" local target="$2" printf '%s/%s.log' "$(library_target_node_dir "$target")" "$step" } library_log() { local step="$1" local target="$2" local message="$3" local log log="$(library_log_file "$step" "$target")" mkdir -p "$(dirname "$log")" printf '[%s][%s][%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$target" "$step" "$message" | tee -a "$log" } library_mark_success() { local step="$1" local target="$2" local lock lock="$(library_lock_file "$step" "$target")" mkdir -p "$(dirname "$lock")" printf 'success %s %s\n' "$step" "$(date '+%Y-%m-%d %H:%M:%S')" >"$lock" } library_is_success() { local step="$1" local target="$2" [[ -f "$(library_lock_file "$step" "$target")" ]] } library_print_command() { printf '[command]' printf ' %q' "$@" printf '\n' } library_run_logged() { local step="$1" local target="$2" shift 2 local log rc log="$(library_log_file "$step" "$target")" mkdir -p "$(dirname "$log")" library_print_command "$@" | tee -a "$log" set +e "$@" 2>&1 | tee -a "$log" rc=${PIPESTATUS[0]} set -e if [[ "$rc" -ne 0 ]]; then library_log "$step" "$target" "command failed with exit code $rc" return "$rc" fi return 0 } library_run_logged_in_dir() { local step="$1" local target="$2" local dir="$3" shift 3 local log rc log="$(library_log_file "$step" "$target")" mkdir -p "$(dirname "$log")" printf '[cwd] %s\n' "$dir" | tee -a "$log" library_print_command "$@" | tee -a "$log" set +e (cd "$dir" && "$@") 2>&1 | tee -a "$log" rc=${PIPESTATUS[0]} set -e if [[ "$rc" -ne 0 ]]; then library_log "$step" "$target" "command failed with exit code $rc" return "$rc" fi return 0 } library_source_one() { local target="$1" local dest local_source git_url git_branch dest="$(library_source_dir "$target")" if library_is_success source "$target"; then library_log source "$target" 'source cache already successful, skip' return 0 fi mkdir -p "$dest" local_source="$(library_registry_get "$target" LOCAL_SOURCE_DIR)" git_url="$(library_registry_get "$target" GIT_URL)" git_branch="$(library_registry_get "$target" GIT_BRANCH)" library_log source "$target" "source start: dest=$dest" if [[ -n "$local_source" ]]; then mkdir -p "$dest" library_run_logged source "$target" cp -a "$local_source/." "$dest/" elif [[ -n "$git_url" ]]; then library_source_git library_run_logged source "$target" library_git_clone_or_update "$git_url" "$git_branch" "$dest" else library_log source "$target" 'no LOCAL_SOURCE_DIR or GIT_URL; created empty source cache' fi library_mark_success source "$target" } library_split_semicolon_words() { local value="$1" local old_ifs="$IFS" IFS=';' # shellcheck disable=SC2206 LIBRARY_SPLIT_RESULT=($value) IFS="$old_ifs" } library_cmake_command() { local value value="$(library_global_get CMAKE_COMMAND)" if [[ -z "$value" ]]; then value="cmake" fi printf '%s' "$value" } library_build_config() { local target="$1" local value value="$LIBRARY_CLI_CONFIG" if [[ -z "$value" ]]; then value="$(library_target_get_or_global "$target" CMAKE_BUILD_CONFIG)" fi if [[ -z "$value" ]]; then value="$(library_target_get_or_global "$target" CMAKE_BUILD_TYPE)" fi printf '%s' "$value" } library_build_one() { local target="$1" local source source_root source_subdir build install cmake_cmd generator platform toolset toolchain build_type options configure_preset build_preset config dep dep_options source_root="$(library_source_dir "$target")" source_subdir="$(library_registry_get "$target" CMAKE_SOURCE_SUBDIR)" source="$source_root" if [[ -n "$source_subdir" ]]; then source="$source_root/$source_subdir" fi build="$(library_build_dir "$target")" install="$(library_install_dir "$target")" cmake_cmd="$(library_cmake_command)" if library_is_success build "$target"; then library_log build "$target" 'build cache already successful, skip' return 0 fi mkdir -p "$build" "$install" generator="$(library_target_get_or_global "$target" CMAKE_GENERATOR)" platform="$(library_target_get_or_global "$target" CMAKE_GENERATOR_PLATFORM)" toolset="$(library_target_get_or_global "$target" CMAKE_GENERATOR_TOOLSET)" toolchain="$(library_target_get_or_global "$target" CMAKE_TOOLCHAIN_FILE)" build_type="$(library_target_get_or_global "$target" CMAKE_BUILD_TYPE)" options="$(library_registry_get "$target" CMAKE_OPTIONS)" configure_preset="$(library_target_get_or_global "$target" CMAKE_CONFIGURE_PRESET)" build_preset="$(library_target_get_or_global "$target" CMAKE_BUILD_PRESET)" config="$(library_build_config "$target")" local configure_cmd if [[ -n "$configure_preset" ]]; then configure_cmd=("$cmake_cmd" -S "$source" -B "$build" --preset "$configure_preset" -DCMAKE_INSTALL_PREFIX="$install") else configure_cmd=("$cmake_cmd" -S "$source" -B "$build" -DCMAKE_INSTALL_PREFIX="$install") [[ -n "$generator" ]] && configure_cmd+=(-G "$generator") [[ -n "$platform" ]] && configure_cmd+=(-A "$platform") [[ -n "$toolset" ]] && configure_cmd+=(-T "$toolset") [[ -n "$toolchain" ]] && configure_cmd+=(-DCMAKE_TOOLCHAIN_FILE="$toolchain") [[ -n "$build_type" ]] && configure_cmd+=(-DCMAKE_BUILD_TYPE="$build_type") fi # OTHER_USE_OPT 是依赖库提供给“使用者”的 CMake configure 参数。 # 构建当前 target 时,先按 dependencies/.txt 的依赖闭包顺序追加依赖库 OTHER_USE_OPT, # 再追加当前 target 自己的 CMAKE_OPTIONS,让当前库的显式配置可以覆盖依赖库给出的默认使用参数。 while IFS= read -r dep || [[ -n "$dep" ]]; do dep_options="$(library_registry_get "$dep" OTHER_USE_OPT)" library_split_semicolon_words "$dep_options" configure_cmd+=("${LIBRARY_SPLIT_RESULT[@]}") done < <(library_resolve_dependencies "$target") if [[ -n "$options" ]]; then library_split_semicolon_words "$options" configure_cmd+=("${LIBRARY_SPLIT_RESULT[@]}") fi library_log build "$target" "configure/build start: source_root=$source_root source_subdir=$source_subdir source=$source build=$build install=$install configure_preset=$configure_preset build_preset=$build_preset config=$config" library_run_logged build "$target" "${configure_cmd[@]}" local build_cmd if [[ -n "$build_preset" ]]; then build_cmd=("$cmake_cmd" --build --preset "$build_preset") [[ -n "$config" ]] && build_cmd+=(--config "$config") library_run_logged_in_dir build "$target" "$source" "${build_cmd[@]}" else build_cmd=("$cmake_cmd" --build "$build") [[ -n "$config" ]] && build_cmd+=(--config "$config") library_run_logged build "$target" "${build_cmd[@]}" fi library_mark_success build "$target" } library_cmake_options_one() { local target="$1" local source source_root source_subdir build install cmake_cmd generator platform toolset toolchain build_type options configure_preset config dep dep_options source_root="$(library_source_dir "$target")" if [[ ! -d "$source_root" ]]; then rm -f "$(library_lock_file source "$target")" fi library_source_one "$target" source_subdir="$(library_registry_get "$target" CMAKE_SOURCE_SUBDIR)" source="$source_root" if [[ -n "$source_subdir" ]]; then source="$source_root/$source_subdir" fi build="$(library_build_dir "$target")" install="$(library_install_dir "$target")" cmake_cmd="$(library_cmake_command)" mkdir -p "$build" "$install" generator="$(library_target_get_or_global "$target" CMAKE_GENERATOR)" platform="$(library_target_get_or_global "$target" CMAKE_GENERATOR_PLATFORM)" toolset="$(library_target_get_or_global "$target" CMAKE_GENERATOR_TOOLSET)" toolchain="$(library_target_get_or_global "$target" CMAKE_TOOLCHAIN_FILE)" build_type="$(library_target_get_or_global "$target" CMAKE_BUILD_TYPE)" options="$(library_registry_get "$target" CMAKE_OPTIONS)" configure_preset="$(library_target_get_or_global "$target" CMAKE_CONFIGURE_PRESET)" config="$(library_build_config "$target")" local configure_cmd if [[ -n "$configure_preset" ]]; then configure_cmd=("$cmake_cmd" -S "$source" -B "$build" --preset "$configure_preset" -DCMAKE_INSTALL_PREFIX="$install") else configure_cmd=("$cmake_cmd" -S "$source" -B "$build" -DCMAKE_INSTALL_PREFIX="$install") [[ -n "$generator" ]] && configure_cmd+=(-G "$generator") [[ -n "$platform" ]] && configure_cmd+=(-A "$platform") [[ -n "$toolset" ]] && configure_cmd+=(-T "$toolset") [[ -n "$toolchain" ]] && configure_cmd+=(-DCMAKE_TOOLCHAIN_FILE="$toolchain") [[ -n "$build_type" ]] && configure_cmd+=(-DCMAKE_BUILD_TYPE="$build_type") fi while IFS= read -r dep || [[ -n "$dep" ]]; do dep_options="$(library_registry_get "$dep" OTHER_USE_OPT)" library_split_semicolon_words "$dep_options" configure_cmd+=("${LIBRARY_SPLIT_RESULT[@]}") done < <(library_resolve_dependencies "$target") if [[ -n "$options" ]]; then library_split_semicolon_words "$options" configure_cmd+=("${LIBRARY_SPLIT_RESULT[@]}") fi library_log cmake-options "$target" "configure/options start: source=$source build=$build install=$install configure_preset=$configure_preset config=$config" library_run_logged cmake-options "$target" "${configure_cmd[@]}" library_run_logged cmake-options "$target" "$cmake_cmd" -LAH -N "$build" } library_install_one() { local target="$1" local build install cmake_cmd install_preset config source_root source_subdir source build="$(library_build_dir "$target")" install="$(library_install_dir "$target")" cmake_cmd="$(library_cmake_command)" install_preset="$(library_target_get_or_global "$target" CMAKE_INSTALL_PRESET)" config="$(library_build_config "$target")" source_root="$(library_source_dir "$target")" source_subdir="$(library_registry_get "$target" CMAKE_SOURCE_SUBDIR)" source="$source_root" if [[ -n "$source_subdir" ]]; then source="$source_root/$source_subdir" fi if library_is_success install "$target"; then library_log install "$target" 'install cache already successful, skip' return 0 fi mkdir -p "$install" library_log install "$target" "install start: build=$build install=$install install_preset=$install_preset config=$config" local install_cmd if [[ -n "$install_preset" ]]; then install_cmd=("$cmake_cmd" --install --preset "$install_preset") [[ -n "$config" ]] && install_cmd+=(--config "$config") library_run_logged_in_dir install "$target" "$source" "${install_cmd[@]}" else install_cmd=("$cmake_cmd" --install "$build" --prefix "$install") [[ -n "$config" ]] && install_cmd+=(--config "$config") library_run_logged install "$target" "${install_cmd[@]}" fi library_mark_success install "$target" } library_auto_one() { local target="$1" if library_is_success install "$target"; then library_log install "$target" 'auto: install successful, skip target' return 0 fi if ! library_is_success build "$target"; then if ! library_is_success source "$target"; then library_source_one "$target" fi library_build_one "$target" fi library_install_one "$target" } library_clean_one() { local step="$1" local target="$2" case "$step" in source) rm -rf "$(library_source_dir "$target")" rm -f "$(library_lock_file source "$target")" ;; build) rm -rf "$(library_build_dir "$target")" rm -f "$(library_lock_file build "$target")" ;; install) rm -rf "$(library_install_dir "$target")" rm -f "$(library_lock_file install "$target")" ;; all) rm -rf "$(library_source_dir "$target")" "$(library_build_dir "$target")" "$(library_install_dir "$target")" rm -f "$(library_lock_file source "$target")" "$(library_lock_file build "$target")" "$(library_lock_file install "$target")" ;; esac } library_git_repo_for_target() { local target="$1" printf '%s' "$(library_source_dir "$target")" } library_git_fetch_one() { library_source_git local target="$1" local repo repo="$(library_git_repo_for_target "$target")" library_log git-fetch "$target" "git fetch start: repo=$repo" library_run_logged git-fetch "$target" library_git_fetch "$repo" library_mark_success git-fetch "$target" } library_git_pull_one() { library_source_git local target="$1" local repo repo="$(library_git_repo_for_target "$target")" library_log git-pull "$target" "git pull start: repo=$repo" library_run_logged git-pull "$target" library_git_pull "$repo" library_mark_success git-pull "$target" } library_git_checkout_one() { library_source_git local target="$1" local repo branch repo="$(library_git_repo_for_target "$target")" branch="$(library_registry_get "$target" GIT_BRANCH)" if [[ -n "$branch" ]]; then library_log git-checkout "$target" "git checkout start: repo=$repo branch=$branch" library_run_logged git-checkout "$target" library_git_checkout "$repo" "$branch" library_mark_success git-checkout "$target" else library_log git-checkout "$target" 'GIT_BRANCH is empty, skip checkout' library_mark_success git-checkout "$target" fi } library_git_branches_one() { library_source_git local target="$1" local repo repo="$(library_git_repo_for_target "$target")" echo "[$target]" library_log git-branches "$target" "git branch list: repo=$repo" library_run_logged git-branches "$target" library_git_branch_list "$repo" library_mark_success git-branches "$target" } library_git_current_branch_one() { library_source_git local target="$1" local repo repo="$(library_git_repo_for_target "$target")" library_log git-current-branch "$target" "git current branch: repo=$repo" library_run_logged git-current-branch "$target" library_git_branch_current "$repo" library_mark_success git-current-branch "$target" } library_run_operation() { local op="$1" shift local targets=() local with_rely=0 if action_is_with_rely "$op"; then with_rely=1 op="$(action_strip_with_rely "$op")" fi case "$op" in list-targets) library_all_targets return 0 ;; deps) library_resolve_dependencies "$@" return 0 ;; cmake-options) local t mapfile -t targets < <(library_resolve_self_targets "$@") for t in "${targets[@]}"; do library_cmake_options_one "$t" done return 0 ;; esac if [[ "$with_rely" == "1" ]]; then mapfile -t targets < <(library_resolve_targets "$@") else mapfile -t targets < <(library_resolve_self_targets "$@") fi case "$op" in source) local t for t in "${targets[@]}"; do library_source_one "$t" done ;; build) local t for t in "${targets[@]}"; do library_source_one "$t" library_build_one "$t" done ;; install) local t for t in "${targets[@]}"; do library_source_one "$t" library_build_one "$t" library_install_one "$t" done ;; auto) local t for t in "${targets[@]}"; do library_auto_one "$t" done ;; rebuild) local t for t in "${targets[@]}"; do library_clean_one build "$t" library_clean_one install "$t" library_source_one "$t" library_build_one "$t" library_install_one "$t" done ;; reinstall) local t for t in "${targets[@]}"; do library_clean_one install "$t" library_install_one "$t" done ;; reset) local t for t in "${targets[@]}"; do library_clean_one all "$t" library_source_one "$t" library_build_one "$t" library_install_one "$t" done ;; clean-source) local t for t in "${targets[@]}"; do library_clean_one source "$t" done ;; clean-build) local t for t in "${targets[@]}"; do library_clean_one build "$t" done ;; clean-install) local t for t in "${targets[@]}"; do library_clean_one install "$t" done ;; clean-all) local t for t in "${targets[@]}"; do library_clean_one all "$t" done ;; git-fetch) local t for t in "${targets[@]}"; do library_git_fetch_one "$t" done ;; git-pull) local t for t in "${targets[@]}"; do library_git_pull_one "$t" done ;; git-checkout) local t for t in "${targets[@]}"; do library_git_checkout_one "$t" done ;; git-branches) local t for t in "${targets[@]}"; do library_git_branches_one "$t" done ;; git-current-branch) local t for t in "${targets[@]}"; do library_git_current_branch_one "$t" done ;; *) echo "unknown operation: $op" >&2 return 2 ;; esac } main() { if [[ "$#" -eq 0 ]]; then library_usage >&2 return 2 fi local op="$1" shift case "$op" in list-ops) library_list_ops return 0 ;; list-cmake-ops) library_list_cmake_ops return 0 ;; list-git-ops) library_list_git_ops return 0 ;; -h | --help | help) library_usage return 0 ;; esac LIBRARY_DATA_DIR="" LIBRARY_CLI_CONFIG="" local targets=() while [[ "$#" -gt 0 ]]; do case "$1" in --data-dir) LIBRARY_DATA_DIR="$2" shift 2 ;; --config) LIBRARY_CLI_CONFIG="$2" shift 2 ;; --) shift while [[ "$#" -gt 0 ]]; do targets+=("$1") shift done ;; *) targets+=("$1") shift ;; esac done if [[ -z "$LIBRARY_DATA_DIR" ]]; then echo "--data-dir is required for operation: $op" >&2 return 2 fi if [[ -z "$LIBRARY_CLI_CONFIG" ]]; then LIBRARY_CLI_CONFIG="$(library_global_get CMAKE_BUILD_CONFIG)" fi library_run_operation "$op" "${targets[@]}" } main "$@" exit $?