【ROS一键编译脚本】基于colcon与catkin的辅助一键懒人脚本

前言

  • 最近在项目中反复运用到colcon build编译指令,为了方便,这里分享一个编写的一键编译脚本。
  • 最终效果如下:


完整代码实现

  • 老规矩先看全部代码
bash 复制代码
#!/bin/bash

WS_DIR=$(pwd)
SRC_DIR="$WS_DIR/src"
BUILD_DIR="$WS_DIR/build"
INSTALL_DIR="$WS_DIR/install"
LOG_DIR="$WS_DIR/log"

COLCON_BASE_CMD="colcon build --event-handlers console_direct+ --parallel-workers 4"

echo "===================================="
echo "      ROS2 COLCON BUILD HELPER      "
echo "===================================="
echo "1) 全部编译"
echo "2) 编译指定功能包"
echo "3) 轻量修复(推荐)"
echo "4) 强制修复(clean rebuild)"
echo "5) 时间戳修复(touch 全工程)"
echo "===================================="
read -p "请选择操作: " main_choice

# =========================
# 获取 package 列表
# =========================
get_packages() {
    packages=()
    index=1

    for pkg in $(colcon list --names-only); do
        packages+=("$pkg")
        echo "$index) $pkg"
        index=$((index + 1))
    done
}

# =========================
# 全部编译
# =========================
build_all() {
    echo "全部编译..."
    $COLCON_BASE_CMD
}

# =========================
# 指定包编译
# =========================
build_selected() {
    echo "扫描功能包..."
    get_packages

    echo ""
    read -p "输入编号(如 1 2 3,空格隔开): " pkg_indexes

    selected_packages=""
    for i in $pkg_indexes; do
        pkg_name="${packages[$((i-1))]}"
        selected_packages="$selected_packages $pkg_name"
    done

    echo "编译: $selected_packages"

    $COLCON_BASE_CMD --packages-select $selected_packages
}

# =========================
# 轻量修复(推荐)
# =========================
light_fix() {
    echo "===================================="
    echo "1) 修复全部包(轻量)"
    echo "2) 修复指定包(轻量)"
    echo "===================================="
    read -p "请选择: " fix_choice

    if [ "$fix_choice" == "1" ]; then
        echo "轻量修复:clean cache + force configure"
        $COLCON_BASE_CMD --cmake-clean-cache

    elif [ "$fix_choice" == "2" ]; then
        echo "扫描功能包..."
        get_packages

        read -p "输入编号(如 1 2 3,空格隔开) " pkg_indexes

        selected_packages=""
        for i in $pkg_indexes; do
            pkg_name="${packages[$((i-1))]}"
            selected_packages="$selected_packages $pkg_name"
        done

        echo "轻量修复包: $selected_packages"

        $COLCON_BASE_CMD \
            --packages-select $selected_packages \
            --cmake-force-configure
    else
        echo "无效"
    fi
}

# =========================
# 强制修复(最干净)
# =========================
hard_fix() {
    echo "===================================="
    echo "1) 修复全部(删除重建)"
    echo "2) 修复指定包(删除重建)"
    echo "===================================="
    read -p "请选择: " fix_choice

    if [ "$fix_choice" == "1" ]; then
        echo "删除全部 build/install/log"
        rm -rf "$BUILD_DIR" "$INSTALL_DIR" "$LOG_DIR"

        echo "重新全量编译"
        $COLCON_BASE_CMD

    elif [ "$fix_choice" == "2" ]; then
        echo "扫描功能包..."
        get_packages

        read -p "输入编号(如 1 2 3,空格隔开)" pkg_indexes

        selected_packages=""
        for i in $pkg_indexes; do
            pkg_name="${packages[$((i-1))]}"
            selected_packages="$selected_packages $pkg_name"
        done

        echo "删除指定包 build/install/log"

        for pkg in $selected_packages; do
            rm -rf "$BUILD_DIR/$pkg"
            rm -rf "$INSTALL_DIR/$pkg"
            rm -rf "$LOG_DIR/$pkg"
        done

        echo "重新编译: $selected_packages"
        $COLCON_BASE_CMD --packages-select $selected_packages
    else
        echo "无效"
    fi
}
# =========================
# 时间戳修复(touch 全工程)
# =========================
time_fix() {
    echo "===================================="
    echo "时间戳修复(Clock Skew Fix)"
    echo "===================================="

    echo "执行 find . -exec touch {} +"

    find "$WS_DIR" -exec touch {} + 2>/dev/null

    echo "✔workspace 时间戳已刷新"

}
# =========================
# 主入口
# =========================
case $main_choice in
    1)
        build_all
        ;;
    2)
        build_selected
        ;;
    3)
        light_fix
        ;;
    4)
        hard_fix
        ;;
    5)
        time_fix
        ;;
    *)
        echo "无效选择"
        ;;
    
esac
  • 这里也提供一个ros1的实现
bash 复制代码
#!/bin/bash

WS_DIR=$(pwd)
SRC_DIR="$WS_DIR/src"
BUILD_DIR="$WS_DIR/build"
DEVEL_DIR="$WS_DIR/devel"
LOG_DIR="$WS_DIR/log"

CATKIN_BASE_CMD="catkin_make -j4"

echo "===================================="
echo "      ROS1 CATKIN BUILD HELPER      "
echo "===================================="
echo "1) 全部编译"
echo "2) 编译指定功能包"
echo "3) 轻量修复(推荐)"
echo "4) 强制修复(clean rebuild)"
echo "5) 时间戳修复(touch 全工程)"
echo "===================================="

read -p "请选择操作: " main_choice

# =========================
# 获取 package 列表
# =========================
get_packages() {
    packages=()
    index=1

    for pkg in $(catkin_topological_order "$SRC_DIR" 2>/dev/null); do
        pkg_name=$(basename "$pkg")

        packages+=("$pkg_name")

        echo "$index) $pkg_name"

        index=$((index + 1))
    done
}

# =========================
# 全部编译
# =========================
build_all() {
    echo "全部编译..."
    $CATKIN_BASE_CMD
}

# =========================
# 编译指定包
# =========================
build_selected() {
    echo "扫描功能包..."
    get_packages

    echo ""

    read -p "输入编号(如 1 2 3,空格隔开): " pkg_indexes

    selected_packages=""

    for i in $pkg_indexes; do
        pkg_name="${packages[$((i-1))]}"
        selected_packages="$selected_packages $pkg_name"
    done

    echo "编译:$selected_packages"

    catkin_make --pkg $selected_packages -j4
}

# =========================
# 轻量修复
# =========================
light_fix() {
    echo "===================================="
    echo "1) 修复全部包(轻量)"
    echo "2) 修复指定包(轻量)"
    echo "===================================="

    read -p "请选择: " fix_choice

    if [ "$fix_choice" == "1" ]; then

        echo "轻量修复:删除 CMakeCache"

        rm -f "$BUILD_DIR/CMakeCache.txt"

        $CATKIN_BASE_CMD

    elif [ "$fix_choice" == "2" ]; then

        echo "扫描功能包..."
        get_packages

        read -p "输入编号(如 1 2 3,空格隔开): " pkg_indexes

        selected_packages=""

        for i in $pkg_indexes; do
            pkg_name="${packages[$((i-1))]}"
            selected_packages="$selected_packages $pkg_name"
        done

        echo "轻量修复:$selected_packages"

        catkin_make --pkg $selected_packages -j4

    else
        echo "无效"
    fi
}

# =========================
# 强制修复
# =========================
hard_fix() {
    echo "===================================="
    echo "1) 修复全部(删除重建)"
    echo "2) 修复指定包(删除重建)"
    echo "===================================="

    read -p "请选择: " fix_choice

    if [ "$fix_choice" == "1" ]; then

        echo "删除 build/devel/log"

        rm -rf "$BUILD_DIR"
        rm -rf "$DEVEL_DIR"
        rm -rf "$LOG_DIR"

        echo "重新全量编译"

        $CATKIN_BASE_CMD

    elif [ "$fix_choice" == "2" ]; then

        echo "扫描功能包..."
        get_packages

        read -p "输入编号(如 1 2 3,空格隔开): " pkg_indexes

        selected_packages=""

        for i in $pkg_indexes; do
            pkg_name="${packages[$((i-1))]}"
            selected_packages="$selected_packages $pkg_name"
        done

        echo "删除指定包 build"

        for pkg in $selected_packages; do
            rm -rf "$BUILD_DIR/$pkg"
        done

        echo "重新编译:$selected_packages"

        catkin_make --pkg $selected_packages -j4

    else
        echo "无效"
    fi
}

# =========================
# 时间戳修复
# =========================
time_fix() {
    echo "===================================="
    echo "时间戳修复(Clock Skew Fix)"
    echo "===================================="

    echo "执行 find . -exec touch {} +"

    find "$WS_DIR" -exec touch {} + 2>/dev/null

    echo "✔ workspace 时间戳已刷新"
}

# =========================
# 主入口
# =========================
case $main_choice in
    1)
        build_all
        ;;
    2)
        build_selected
        ;;
    3)
        light_fix
        ;;
    4)
        hard_fix
        ;;
    5)
        time_fix
        ;;
    *)
        echo "无效选择"
        ;;
esac
  • 下面我们分模块解析实现

1-1 基础参数
bash 复制代码
WS_DIR=$(pwd)
SRC_DIR="$WS_DIR/src"
BUILD_DIR="$WS_DIR/build"
INSTALL_DIR="$WS_DIR/install"
LOG_DIR="$WS_DIR/log"

COLCON_BASE_CMD="colcon build --event-handlers console_direct+ --parallel-workers 4"
  • 这里获取了当前的工作空间$(pwd),同时获取了功能包应该存在的src和编译后的三大输出目录:
    • build:编译中间文件
    • install:最终安装结果
    • log:编译日志
  • COLCON_BASE_CMD="colcon build --event-handlers console_direct+ --parallel-workers 4"
    • --event-handlers console_direct+:实时输出编译日志(不缓存)
    • --parallel-workers 4:并行编译 4 个 package

1-2 读取用户选择
bash 复制代码
echo "===================================="
echo "      ROS2 COLCON BUILD HELPER      "
echo "===================================="
echo "1) 全部编译"
echo "2) 编译指定功能包"
echo "3) 轻量修复(推荐)"
echo "4) 强制修复(clean rebuild)"
echo "5) 时间戳修复(touch 全工程)"
echo "===================================="
read -p "请选择操作: " main_choice
  • 读取用户输入然后赋值给main_choice

1-3 获取 package 列表
bash 复制代码
get_packages() {
    packages=()
    index=1

    for pkg in $(colcon list --names-only); do
        packages+=("$pkg")
        echo "$index) $pkg"
        index=$((index + 1))
    done
}
  • 区别于直接搜寻src下的全部目录,这里借助colcon list获取 package,避免了src还存在其他非ros2的目录影响判断
  • packages+=("$pkg"):数组,保存所有 package 名称

1-4 指定包编译
bash 复制代码
build_all() {
    echo "全部编译..."
    $COLCON_BASE_CMD
}
build_selected() {
    echo "扫描功能包..."
    get_packages

    echo ""
    read -p "输入编号(如 1 2 3,空格隔开): " pkg_indexes

    selected_packages=""
    for i in $pkg_indexes; do
        pkg_name="${packages[$((i-1))]}"
        selected_packages="$selected_packages $pkg_name"
    done

    echo "编译: $selected_packages"

    $COLCON_BASE_CMD --packages-select $selected_packages
}
  • 调用get_packages更新全部包的数组packages
  • 读取用户输入的编号,然后便利存储有全部包的数组packages
    • pkg_name="${packages[$((i-1))]}":把编号转换为对应的包名
    • selected_packages="$selected_packages $pkg_name"拼接字符串,变成pkgA pkgB pkgC
  • 最后执行编译

1-5 轻量修复
bash 复制代码
light_fix() {
    echo "===================================="
    echo "1) 修复全部包(轻量)"
    echo "2) 修复指定包(轻量)"
    echo "===================================="
    read -p "请选择: " fix_choice

    if [ "$fix_choice" == "1" ]; then
        echo "轻量修复:clean cache + force configure"
        $COLCON_BASE_CMD --cmake-clean-cache

    elif [ "$fix_choice" == "2" ]; then
        echo "扫描功能包..."
        get_packages

        read -p "输入编号(如 1 2 3,空格隔开) " pkg_indexes

        selected_packages=""
        for i in $pkg_indexes; do
            pkg_name="${packages[$((i-1))]}"
            selected_packages="$selected_packages $pkg_name"
        done

        echo "轻量修复包: $selected_packages"

        $COLCON_BASE_CMD \
            --packages-select $selected_packages \
            --cmake-force-configure
    else
        echo "无效"
    fi
}
  • 轻量修复主要用于如下情况:
    • CMakeLists改了
    • find_package失败
    • dependency变化
  • 核心主要依赖于
    • --cmake-clean-cache:删除 CMakeCache.txt,重新 configure
    • --cmake-force-configure:--cmake-force-configure

1-6 强制修复
bash 复制代码
hard_fix() {
    echo "===================================="
    echo "1) 修复全部(删除重建)"
    echo "2) 修复指定包(删除重建)"
    echo "===================================="
    read -p "请选择: " fix_choice

    if [ "$fix_choice" == "1" ]; then
        echo "删除全部 build/install/log"
        rm -rf "$BUILD_DIR" "$INSTALL_DIR" "$LOG_DIR"

        echo "重新全量编译"
        $COLCON_BASE_CMD

    elif [ "$fix_choice" == "2" ]; then
        echo "扫描功能包..."
        get_packages

        read -p "输入编号(如 1 2 3,空格隔开)" pkg_indexes

        selected_packages=""
        for i in $pkg_indexes; do
            pkg_name="${packages[$((i-1))]}"
            selected_packages="$selected_packages $pkg_name"
        done

        echo "删除指定包 build/install/log"

        for pkg in $selected_packages; do
            rm -rf "$BUILD_DIR/$pkg"
            rm -rf "$INSTALL_DIR/$pkg"
            rm -rf "$LOG_DIR/$pkg"
        done

        echo "重新编译: $selected_packages"
        $COLCON_BASE_CMD --packages-select $selected_packages
    else
        echo "无效"
    fi
}
  • 强制修复是最暴力的方式,核心原理就是:
bash 复制代码
rm -rf build install log
  • 彻底清空 colcon 产物,然后重新编译

1-7 时间戳修复
bash 复制代码
time_fix() {
    echo "===================================="
    echo "时间戳修复(Clock Skew Fix)"
    echo "===================================="

    echo "执行 find . -exec touch {} +"

    find "$WS_DIR" -exec touch {} + 2>/dev/null

    echo "✔workspace 时间戳已刷新"

}
bash 复制代码
Clock skew detected. Your build may be incomplete

1-8 主入口
bash 复制代码
case $main_choice in
    1)
        build_all
        ;;
    2)
        build_selected
        ;;
    3)
        light_fix
        ;;
    4)
        hard_fix
        ;;
    5)
        time_fix
        ;;
    *)
        echo "无效选择"
        ;;
    
esac
  • switch结构,没了

测试

  • 我们搭建如下测试
bash 复制代码
.
├── build
├── colcon_helper.sh
├── install
├── log
└── src
    ├── test01
    ├── test_package01
    │   ├── CMakeLists.txt
    │   ├── include
    │   ├── package.xml
    │   └── src
    ├── test_package02
    │   ├── CMakeLists.txt
    │   ├── include
    │   ├── package.xml
    │   └── src
    └── test_package03
        ├── CMakeLists.txt
        ├── include
        ├── package.xml
        └── src
  • 其中test_package01test_package02test_package03均为ros2功能包,test01只是普通目录
  • colcon_helper.sh放在工作空间下即可


总结

  • 本文实现了一个基于 colcon 的 ROS2 一键编译与分级修复管理脚本,支持 package 选择编译、轻量修复、强制重建以及时间戳修复,用于提升工作空间编译效率与稳定性。
  • 如有错误!欢迎指出!
  • 感谢支持!
相关推荐
a珍爱上了a强14 小时前
__attribute__((constructor))
linux
lightgis14 小时前
使用工作站电脑
linux·电脑
z2023050814 小时前
RDMA之NVIDIA Zero Touch RoCE (ZTR),和RTT的应用(9)
linux·服务器·网络·人工智能·ai
code monkey.14 小时前
【Linux之旅】Linux TCP Socket 编程实战:从单连接到线程池,构建高并发服务端
linux·网络·tcp/ip
H Journey14 小时前
总结Linux下查看IP地址的相关命令
linux·运维·ip address
Cloud_Shy61814 小时前
Linux 系统定时任务Cron(d)服务应用实践(三:定时任务调试技巧及故障分析解决)
linux·网络·centos·云计算·github·运维开发
晚风予卿云月14 小时前
【Linux】初步构建框架—虚拟地址空间(三)—进程与内存管理的解耦优势、深入理解vm_area_struct
linux·运维·服务器·面试
音视频牛哥14 小时前
大牛直播SDK(SmartMediaKit)Windows平台多路RTSP转RTMP推流集成说明
windows·大牛直播sdk·rtsp2rtmp·rtsp转rtmp推送·rtsp转发rtmp·多路rtsp转rtmp推流·c# rtsp转rtmp
私人珍藏库14 小时前
[Android] 全能语音计算器v4.6
人工智能·windows·语音识别·工具·软件·多功能