diff --git a/.github/workflows/multi-platform-build.yml b/.github/workflows/multi-platform-build.yml index 72dafa5..630bfaf 100644 --- a/.github/workflows/multi-platform-build.yml +++ b/.github/workflows/multi-platform-build.yml @@ -2,16 +2,21 @@ name: Multi-Platform Conda Build on: push: - branches: [ main, dev ] - tags: [ 'v*' ] + branches: [main, dev] + tags: ['v*'] pull_request: - branches: [ main, dev ] + branches: [main, dev] workflow_dispatch: inputs: platforms: description: '选择构建平台 (逗号分隔): linux-64, osx-64, osx-arm64, win-64' required: false default: 'osx-arm64' + upload_to_anaconda: + description: '是否上传到Anaconda.org' + required: false + default: false + type: boolean jobs: build: @@ -19,18 +24,18 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-latest - platform: linux-64 - env_file: unilabos-linux-64.yaml - - os: macos-13 # Intel - platform: osx-64 - env_file: unilabos-osx-64.yaml - - os: macos-latest # ARM64 - platform: osx-arm64 - env_file: unilabos-osx-arm64.yaml - - os: windows-latest - platform: win-64 - env_file: unilabos-win64.yaml + - os: ubuntu-latest + platform: linux-64 + env_file: unilabos-linux-64.yaml + - os: macos-13 # Intel + platform: osx-64 + env_file: unilabos-osx-64.yaml + - os: macos-latest # ARM64 + platform: osx-arm64 + env_file: unilabos-osx-arm64.yaml + - os: windows-latest + platform: win-64 + env_file: unilabos-win64.yaml runs-on: ${{ matrix.os }} @@ -39,94 +44,112 @@ jobs: shell: bash -l {0} steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: Check if platform should be built - id: should_build - run: | - if [[ "${{ github.event_name }}" != "workflow_dispatch" ]]; then - echo "should_build=true" >> $GITHUB_OUTPUT - elif [[ -z "${{ github.event.inputs.platforms }}" ]]; then - echo "should_build=true" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.inputs.platforms }}" == *"${{ matrix.platform }}"* ]]; then - echo "should_build=true" >> $GITHUB_OUTPUT - else - echo "should_build=false" >> $GITHUB_OUTPUT - fi + - name: Check if platform should be built + id: should_build + run: | + if [[ "${{ github.event_name }}" != "workflow_dispatch" ]]; then + echo "should_build=true" >> $GITHUB_OUTPUT + elif [[ -z "${{ github.event.inputs.platforms }}" ]]; then + echo "should_build=true" >> $GITHUB_OUTPUT + elif [[ "${{ github.event.inputs.platforms }}" == *"${{ matrix.platform }}"* ]]; then + echo "should_build=true" >> $GITHUB_OUTPUT + else + echo "should_build=false" >> $GITHUB_OUTPUT + fi - - name: Setup Miniconda - if: steps.should_build.outputs.should_build == 'true' - uses: conda-incubator/setup-miniconda@v3 - with: - miniconda-version: "latest" - channels: conda-forge,robostack-staging,defaults - channel-priority: strict - activate-environment: build-env - auto-activate-base: false - auto-update-conda: false - show-channel-urls: true + - name: Setup Miniconda + if: steps.should_build.outputs.should_build == 'true' + uses: conda-incubator/setup-miniconda@v3 + with: + miniconda-version: 'latest' + channels: conda-forge,robostack-staging,defaults + channel-priority: strict + activate-environment: build-env + auto-activate-base: false + auto-update-conda: false + show-channel-urls: true - - name: Install boa and build tools - if: steps.should_build.outputs.should_build == 'true' - run: | - conda install -c conda-forge boa conda-build + - name: Install rattler-build and anaconda-client + if: steps.should_build.outputs.should_build == 'true' + run: | + conda install -c conda-forge rattler-build anaconda-client - - name: Show environment info - if: steps.should_build.outputs.should_build == 'true' - run: | - conda info - conda list | grep -E "(boa|conda-build)" - echo "Platform: ${{ matrix.platform }}" - echo "OS: ${{ matrix.os }}" + - name: Show environment info + if: steps.should_build.outputs.should_build == 'true' + run: | + conda info + conda list | grep -E "(rattler-build|anaconda-client)" + echo "Platform: ${{ matrix.platform }}" + echo "OS: ${{ matrix.os }}" - - name: Build conda package - if: steps.should_build.outputs.should_build == 'true' - run: | - if [[ "${{ matrix.platform }}" == "osx-arm64" ]]; then - boa build -m ./recipes/conda_build_config.yaml -m ./recipes/macos_sdk_config.yaml ./recipes/ros-humble-unilabos-msgs - else - boa build -m ./recipes/conda_build_config.yaml ./recipes/ros-humble-unilabos-msgs - fi + - name: Build conda package + if: steps.should_build.outputs.should_build == 'true' + run: | + if [[ "${{ matrix.platform }}" == "osx-arm64" ]]; then + rattler-build build -m ./recipes/conda_build_config.yaml -m ./recipes/macos_sdk_config.yaml ./recipes/msgs + else + rattler-build build -m ./recipes/conda_build_config.yaml ./recipes/msgs + fi - - name: List built packages - if: steps.should_build.outputs.should_build == 'true' - run: | - echo "Built packages in conda-bld:" - find $CONDA_PREFIX/conda-bld -name "*.tar.bz2" | head -10 - ls -la $CONDA_PREFIX/conda-bld/${{ matrix.platform }}/ || echo "${{ matrix.platform }} directory not found" - ls -la $CONDA_PREFIX/conda-bld/noarch/ || echo "noarch directory not found" - echo "CONDA_PREFIX: $CONDA_PREFIX" - echo "Full path would be: $CONDA_PREFIX/conda-bld/**/*.tar.bz2" + - name: List built packages + if: steps.should_build.outputs.should_build == 'true' + run: | + echo "Built packages in output directory:" + find ./recipes/msgs/output -name "*.conda" | head -10 + ls -la ./recipes/msgs/output/${{ matrix.platform }}/ || echo "${{ matrix.platform }} directory not found" + ls -la ./recipes/msgs/output/noarch/ || echo "noarch directory not found" + echo "Output directory structure:" + find ./recipes/msgs/output -type f -name "*.conda" - - name: Prepare artifacts for upload - if: steps.should_build.outputs.should_build == 'true' - run: | - mkdir -p ${{ runner.temp }}/conda-packages - find $CONDA_PREFIX/conda-bld -name "*.tar.bz2" -exec cp {} ${{ runner.temp }}/conda-packages/ \; - echo "Copied files to temp directory:" - ls -la ${{ runner.temp }}/conda-packages/ + - name: Prepare artifacts for upload + if: steps.should_build.outputs.should_build == 'true' + run: | + mkdir -p ${{ runner.temp }}/conda-packages + find ./recipes/msgs/output -name "*.conda" -exec cp {} ${{ runner.temp }}/conda-packages/ \; + echo "Copied files to temp directory:" + ls -la ${{ runner.temp }}/conda-packages/ - - name: Upload conda package artifacts - if: steps.should_build.outputs.should_build == 'true' - uses: actions/upload-artifact@v4 - with: - name: conda-package-${{ matrix.platform }} - path: ${{ runner.temp }}/conda-packages - if-no-files-found: warn - retention-days: 30 + - name: Upload conda package artifacts + if: steps.should_build.outputs.should_build == 'true' + uses: actions/upload-artifact@v4 + with: + name: conda-package-${{ matrix.platform }} + path: ${{ runner.temp }}/conda-packages + if-no-files-found: warn + retention-days: 30 - - name: Create release assets (on tags) - if: steps.should_build.outputs.should_build == 'true' && startsWith(github.ref, 'refs/tags/') - run: | - mkdir -p release-assets - find $CONDA_PREFIX/conda-bld -name "*.tar.bz2" -exec cp {} release-assets/ \; + - name: Login to Anaconda + if: steps.should_build.outputs.should_build == 'true' && (github.event.inputs.upload_to_anaconda == 'true' || startsWith(github.ref, 'refs/tags/')) + run: | + anaconda login --username ${{ secrets.ANACONDA_USERNAME }} --password ${{ secrets.ANACONDA_PASSWORD }} - - name: Upload to release - if: steps.should_build.outputs.should_build == 'true' && startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@v1 - with: - files: release-assets/* - draft: false - prerelease: false + - name: Upload to Anaconda.org + if: steps.should_build.outputs.should_build == 'true' && (github.event.inputs.upload_to_anaconda == 'true' || startsWith(github.ref, 'refs/tags/')) + run: | + for package in $(find ./recipes/msgs/output -name "*.conda"); do + echo "Uploading $package to Anaconda.org..." + anaconda upload --user ${{ secrets.ANACONDA_ORG }} --force "$package" + done + + - name: Logout from Anaconda + if: always() && steps.should_build.outputs.should_build == 'true' && (github.event.inputs.upload_to_anaconda == 'true' || startsWith(github.ref, 'refs/tags/')) + run: | + anaconda logout || true + + - name: Create release assets (on tags) + if: steps.should_build.outputs.should_build == 'true' && startsWith(github.ref, 'refs/tags/') + run: | + mkdir -p release-assets + find ./recipes/msgs/output -name "*.conda" -exec cp {} release-assets/ \; + + - name: Upload to release + if: steps.should_build.outputs.should_build == 'true' && startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: release-assets/* + draft: false + prerelease: false diff --git a/recipes/ros-humble-unilabos-msgs/bld_ament_cmake.bat b/recipes/msgs/bld_ament_cmake.bat similarity index 62% rename from recipes/ros-humble-unilabos-msgs/bld_ament_cmake.bat rename to recipes/msgs/bld_ament_cmake.bat index 9bf0155..dfa3672 100644 --- a/recipes/ros-humble-unilabos-msgs/bld_ament_cmake.bat +++ b/recipes/msgs/bld_ament_cmake.bat @@ -34,8 +34,32 @@ cmake ^ -DCMAKE_OBJECT_PATH_MAX=255 ^ -DPYTHON_INSTALL_DIR=%SP_DIR_FORWARDSLASHES% ^ --compile-no-warning-as-error ^ - %SRC_DIR%\%PKG_NAME%\src\work + %SRC_DIR%\src if errorlevel 1 exit 1 +set "infile=%SRC_DIR%\build\cmake_install.cmake" +set "tmpfile=%infile%.tmp" + +rem 模式串 +set "pattern=echo %PYTHON%" + +> "%tmpfile%" ( + for /f "usebackq delims=" %%L in ("%infile%") do ( + set "line=%%L" + rem 把 pattern 从 line 里“删除”,看看结果是否变化 + set "test=!line:%pattern%=!" + if "!test!" neq "!line!" ( + rem 含有 echo %PYTHON%,对整行做 \→\\ 全局替换 + echo !line:\=\\! + ) else ( + rem 不含,原样输出 + echo !line! + ) + ) +) + +rem 用 tmp 覆盖原文件 +move /Y "%tmpfile%" "%infile%" > nul + cmake --build . --config Release --target install if errorlevel 1 exit 1 diff --git a/recipes/ros-humble-unilabos-msgs/build_ament_cmake.sh b/recipes/msgs/build_ament_cmake.sh similarity index 98% rename from recipes/ros-humble-unilabos-msgs/build_ament_cmake.sh rename to recipes/msgs/build_ament_cmake.sh index 52baa99..4fa1669 100644 --- a/recipes/ros-humble-unilabos-msgs/build_ament_cmake.sh +++ b/recipes/msgs/build_ament_cmake.sh @@ -66,6 +66,6 @@ cmake \ -DBUILD_TESTING=OFF \ -DCMAKE_OSX_DEPLOYMENT_TARGET=$OSX_DEPLOYMENT_TARGET \ --compile-no-warning-as-error \ - $SRC_DIR/$PKG_NAME/src/work + $SRC_DIR/src cmake --build . --config Release --target install diff --git a/recipes/msgs/recipe.yaml b/recipes/msgs/recipe.yaml new file mode 100644 index 0000000..a4289c6 --- /dev/null +++ b/recipes/msgs/recipe.yaml @@ -0,0 +1,76 @@ +package: + name: ros-humble-unilabos-msgs + version: 0.10.1 +source: + path: ../../unilabos_msgs + target_directory: src + +build: + script: + - if: win + then: + - copy %RECIPE_DIR%\bld_ament_cmake.bat %SRC_DIR% + - call %SRC_DIR%\bld_ament_cmake.bat + - if: unix + then: + - cp $RECIPE_DIR/build_ament_cmake.sh $SRC_DIR + - bash $SRC_DIR/build_ament_cmake.sh + +about: + repository: https://github.com/dptech-corp/Uni-Lab-OS + license: BSD-3-Clause + description: "ros-humble-unilabos-msgs is a package that provides message definitions for Uni-Lab-OS." + +requirements: + build: + - if: build_platform != target_platform + then: + - pkg-config + - python =3.11.11 + - cross-python_${{ target_platform }} + - numpy + - ${{ compiler('cxx') }} + - ${{ compiler('c') }} + - if: linux and x86_64 + then: + - sysroot_linux-64 2.17 + - ninja + - setuptools + - cython + - cmake + - if: unix + then: + - make + - coreutils + - if: osx + then: + - tapi + - if: win + then: + - vs2022_win-64 + host: + - numpy + - pip + - if: build_platform == target_platform + then: + - pkg-config + - robostack-staging::ros-humble-action-msgs + - robostack-staging::ros-humble-ament-cmake + - robostack-staging::ros-humble-ament-lint-auto + - robostack-staging::ros-humble-ament-lint-common + - robostack-staging::ros-humble-ros-environment + - robostack-staging::ros-humble-ros-workspace + - robostack-staging::ros-humble-rosidl-default-generators + - robostack-staging::ros-humble-std-msgs + - robostack-staging::ros-humble-geometry-msgs + - robostack-staging::ros2-distro-mutex=0.6 + run: + - robostack-staging::ros-humble-action-msgs + - robostack-staging::ros-humble-ros-workspace + - robostack-staging::ros-humble-rosidl-default-runtime + - robostack-staging::ros-humble-std-msgs + - robostack-staging::ros-humble-geometry-msgs + - robostack-staging::ros2-distro-mutex=0.6 + - if: osx and x86_64 + then: + - __osx >= {{ MACOSX_DEPLOYMENT_TARGET|default('10.14') }} \ No newline at end of file diff --git a/recipes/ros-humble-unilabos-msgs/recipe.yaml b/recipes/ros-humble-unilabos-msgs/recipe.yaml deleted file mode 100644 index 258047c..0000000 --- a/recipes/ros-humble-unilabos-msgs/recipe.yaml +++ /dev/null @@ -1,61 +0,0 @@ -package: - name: ros-humble-unilabos-msgs - version: 0.10.1 -source: - path: ../../unilabos_msgs - folder: ros-humble-unilabos-msgs/src/work - -build: - script: - sel(win): bld_ament_cmake.bat - sel(unix): build_ament_cmake.sh - number: 5 -about: - home: https://www.ros.org/ - license: BSD-3-Clause - summary: | - Robot Operating System - -extra: - recipe-maintainers: - - ros-forge - -requirements: - build: - - "{{ compiler('cxx') }}" - - "{{ compiler('c') }}" - - sel(linux64): sysroot_linux-64 2.17 - - ninja - - setuptools - - sel(unix): make - - sel(unix): coreutils - - sel(osx): tapi - - sel(build_platform != target_platform): pkg-config - - cmake - - cython - - sel(win): vs2022_win-64 - - sel(build_platform != target_platform): python - - sel(build_platform != target_platform): cross-python_{{ target_platform }} - - sel(build_platform != target_platform): numpy - host: - - numpy - - pip - - sel(build_platform == target_platform): pkg-config - - robostack-staging::ros-humble-action-msgs - - robostack-staging::ros-humble-ament-cmake - - robostack-staging::ros-humble-ament-lint-auto - - robostack-staging::ros-humble-ament-lint-common - - robostack-staging::ros-humble-ros-environment - - robostack-staging::ros-humble-ros-workspace - - robostack-staging::ros-humble-rosidl-default-generators - - robostack-staging::ros-humble-std-msgs - - robostack-staging::ros-humble-geometry-msgs - - robostack-staging::ros2-distro-mutex=0.6.* - run: - - robostack-staging::ros-humble-action-msgs - - robostack-staging::ros-humble-ros-workspace - - robostack-staging::ros-humble-rosidl-default-runtime - - robostack-staging::ros-humble-std-msgs - - robostack-staging::ros-humble-geometry-msgs -# - robostack-staging::ros2-distro-mutex=0.6.* - - sel(osx and x86_64): __osx >={{ MACOSX_DEPLOYMENT_TARGET|default('10.14') }} diff --git a/unilabos/app/main.py b/unilabos/app/main.py index 9a1d5dc..4f49dcc 100644 --- a/unilabos/app/main.py +++ b/unilabos/app/main.py @@ -43,10 +43,11 @@ def convert_argv_dashes_to_underscores(args: argparse.ArgumentParser): for i, arg in enumerate(sys.argv): for option_string in option_strings: if arg.startswith(option_string): - new_arg = arg[:2] + arg[2:len(option_string)].replace("-", "_") + arg[len(option_string):] + new_arg = arg[:2] + arg[2 : len(option_string)].replace("-", "_") + arg[len(option_string) :] sys.argv[i] = new_arg break + def parse_args(): """解析命令行参数""" parser = argparse.ArgumentParser(description="Start Uni-Lab Edge server.") @@ -128,6 +129,13 @@ def parse_args(): default="", help="实验室唯一ID,也可通过环境变量 UNILABOS.MQCONFIG.LABID 设置或传入--config设置", ) + parser.add_argument( + "--loglevel", + type=str, + choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], + default="INFO", + help="设置日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL),默认为 INFO", + ) return parser @@ -151,17 +159,22 @@ def main(): if not os.path.exists(config_path): print_status( f"当前工作目录 {working_dir} 未找到local_config.py,请通过 --config 传入 local_config.py 文件路径", - "error") + "error", + ) os._exit(1) elif os.path.exists(working_dir) and os.path.exists(os.path.join(working_dir, "local_config.py")): config_path = os.path.join(working_dir, "local_config.py") - elif not config_path and (not os.path.exists(working_dir) or not os.path.exists(os.path.join(working_dir, "local_config.py"))): + elif not config_path and ( + not os.path.exists(working_dir) or not os.path.exists(os.path.join(working_dir, "local_config.py")) + ): print_status(f"未指定config路径,可通过 --config 传入 local_config.py 文件路径", "info") print_status(f"您是否为第一次使用?并将当前路径 {working_dir} 作为工作目录? (Y/n)", "info") if input() != "n": os.makedirs(working_dir, exist_ok=True) config_path = os.path.join(working_dir, "local_config.py") - shutil.copy(os.path.join(os.path.dirname(os.path.dirname(__file__)), "config", "example_config.py"), config_path) + shutil.copy( + os.path.join(os.path.dirname(os.path.dirname(__file__)), "config", "example_config.py"), config_path + ) print_status(f"已创建 local_config.py 路径: {config_path}", "info") print_status(f"请在文件夹中配置lab_id,放入下载的CA.crt、lab.crt、lab.key重新启动本程序", "info") os._exit(1) diff --git a/unilabos/utils/log.py b/unilabos/utils/log.py index 61c95a1..a680101 100644 --- a/unilabos/utils/log.py +++ b/unilabos/utils/log.py @@ -144,11 +144,29 @@ class ColoredFormatter(logging.Formatter): # 配置日志处理器 -def configure_logger(): - """配置日志记录器""" +def configure_logger(loglevel=None): + """配置日志记录器 + + Args: + loglevel: 日志级别,可以是字符串('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') + 或logging模块的常量(如logging.DEBUG) + """ # 获取根日志记录器 root_logger = logging.getLogger() - root_logger.setLevel(logging.DEBUG) # 修改为DEBUG以显示所有级别 + + # 设置日志级别 + if loglevel is not None: + if isinstance(loglevel, str): + # 将字符串转换为logging级别 + numeric_level = getattr(logging, loglevel.upper(), None) + if not isinstance(numeric_level, int): + print(f"警告: 无效的日志级别 '{loglevel}',使用默认级别 DEBUG") + numeric_level = logging.DEBUG + else: + numeric_level = loglevel + root_logger.setLevel(numeric_level) + else: + root_logger.setLevel(logging.DEBUG) # 默认级别 # 移除已存在的处理器 for handler in root_logger.handlers[:]: @@ -156,7 +174,7 @@ def configure_logger(): # 创建控制台处理器 console_handler = logging.StreamHandler() - console_handler.setLevel(logging.DEBUG) # 修改为DEBUG以显示所有级别 + console_handler.setLevel(root_logger.level) # 使用与根记录器相同的级别 # 使用自定义的颜色格式化器 color_formatter = ColoredFormatter() diff --git a/unilabos_msgs/package.xml b/unilabos_msgs/package.xml index 4ccb3a9..819cc8c 100644 --- a/unilabos_msgs/package.xml +++ b/unilabos_msgs/package.xml @@ -2,7 +2,7 @@ unilabos_msgs - 0.0.5 + 0.10.1 ROS2 Messages package for unilabos devices Junhan Chang MIT