Vins_Fusion_gpu中source setup.bash

文章目录

  • source setup.bash
    • setup.bash
    • setup.sh
      • 脚本的主要功能
      • 脚本的详细解释
          1. **初始化和检查**
          1. **检测操作系统**
          1. **设置环境变量**
          1. **记住 shell 类型**
          1. **调用 Python 脚本生成环境变量**
          1. **加载环境钩子**
          1. **清理**
      • 总结
    • _setup_util.py
      • `_setup_util.py` 的完整功能解析
      • 文件结构与功能
          1. **全局变量和常量**
          1. **环境变量子目录映射**
          1. **命令行参数解析**
          1. **主程序逻辑**
      • **主程序的主要步骤**
      • 关键函数解析
          1. **`rollback_env_variables`**
          1. **`_rollback_env_variable`**
          1. **`_get_workspaces`**
          1. **`prepend_env_variables`**
          1. **`_prefix_env_variable`**
          1. **`find_env_hooks`**
      • 总结

source setup.bash

每次运行都需要source setup.bash,不太懂这个文件干什么的,就学习了一下。

setup.bash

bash 复制代码
#!/usr/bin/env bash
# generated from catkin/cmake/templates/setup.bash.in

CATKIN_SHELL=bash

# source setup.sh from same directory as this file
_CATKIN_SETUP_DIR=$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)
. "$_CATKIN_SETUP_DIR/setup.sh"

该文件就是调用setup.sh

setup.sh

bash 复制代码
#!/usr/bin/env sh
# generated from catkin/cmake/template/setup.sh.in

# Sets various environment variables and sources additional environment hooks.
# It tries it's best to undo changes from a previously sourced setup file before.
# Supported command line options:
# --extend: skips the undoing of changes from a previously sourced setup file
# --local: only considers this workspace but not the chained ones
# In plain sh shell which doesn't support arguments for sourced scripts you can
# set the environment variable `CATKIN_SETUP_UTIL_ARGS=--extend/--local` instead.

# since this file is sourced either use the provided _CATKIN_SETUP_DIR
# or fall back to the destination set at configure time
: ${_CATKIN_SETUP_DIR:=/home/ssmec/catkin_ws/devel}
_SETUP_UTIL="$_CATKIN_SETUP_DIR/_setup_util.py"
unset _CATKIN_SETUP_DIR

if [ ! -f "$_SETUP_UTIL" ]; then
  echo "Missing Python script: $_SETUP_UTIL"
  return 22
fi

# detect if running on Darwin platform
_UNAME=`uname -s`
_IS_DARWIN=0
if [ "$_UNAME" = "Darwin" ]; then
  _IS_DARWIN=1
fi
unset _UNAME

# make sure to export all environment variables
export CMAKE_PREFIX_PATH
if [ $_IS_DARWIN -eq 0 ]; then
  export LD_LIBRARY_PATH
else
  export DYLD_LIBRARY_PATH
fi
unset _IS_DARWIN
export PATH
export PKG_CONFIG_PATH
export PYTHONPATH

# remember type of shell if not already set
if [ -z "$CATKIN_SHELL" ]; then
  CATKIN_SHELL=sh
fi

# invoke Python script to generate necessary exports of environment variables
# use TMPDIR if it exists, otherwise fall back to /tmp
if [ -d "${TMPDIR:-}" ]; then
  _TMPDIR="${TMPDIR}"
else
  _TMPDIR=/tmp
fi
_SETUP_TMP=`mktemp "${_TMPDIR}/setup.sh.XXXXXXXXXX"`
unset _TMPDIR
if [ $? -ne 0 -o ! -f "$_SETUP_TMP" ]; then
  echo "Could not create temporary file: $_SETUP_TMP"
  return 1
fi
CATKIN_SHELL=$CATKIN_SHELL "$_SETUP_UTIL" $@ ${CATKIN_SETUP_UTIL_ARGS:-} >> "$_SETUP_TMP"
_RC=$?
if [ $_RC -ne 0 ]; then
  if [ $_RC -eq 2 ]; then
    echo "Could not write the output of '$_SETUP_UTIL' to temporary file '$_SETUP_TMP': may be the disk if full?"
  else
    echo "Failed to run '\"$_SETUP_UTIL\" $@': return code $_RC"
  fi
  unset _RC
  unset _SETUP_UTIL
  rm -f "$_SETUP_TMP"
  unset _SETUP_TMP
  return 1
fi
unset _RC
unset _SETUP_UTIL
. "$_SETUP_TMP"
rm -f "$_SETUP_TMP"
unset _SETUP_TMP

# source all environment hooks
_i=0
while [ $_i -lt $_CATKIN_ENVIRONMENT_HOOKS_COUNT ]; do
  eval _envfile=\$_CATKIN_ENVIRONMENT_HOOKS_$_i
  unset _CATKIN_ENVIRONMENT_HOOKS_$_i
  eval _envfile_workspace=\$_CATKIN_ENVIRONMENT_HOOKS_${_i}_WORKSPACE
  unset _CATKIN_ENVIRONMENT_HOOKS_${_i}_WORKSPACE
  # set workspace for environment hook
  CATKIN_ENV_HOOK_WORKSPACE=$_envfile_workspace
  . "$_envfile"
  unset CATKIN_ENV_HOOK_WORKSPACE
  _i=$((_i + 1))
done
unset _i

unset _CATKIN_ENVIRONMENT_HOOKS_COUNT

这个脚本是 ROS(Robot Operating System)中 catkin 工作空间的 setup.sh 文件,它的主要作用是设置和更新环境变量,使得用户可以在当前终端会话中使用该工作空间中的工具、库和包。它还会处理一些复杂的环境配置,例如确保多个工作空间之间的依赖关系正确,以及撤销之前可能已经设置的其他工作空间的环境变量。

脚本的主要功能

  1. 设置环境变量 :脚本会设置一系列环境变量,如 CMAKE_PREFIX_PATHLD_LIBRARY_PATH(Linux)、DYLD_LIBRARY_PATH(macOS)、PATHPKG_CONFIG_PATHPYTHONPATH。这些环境变量对于找到编译工具、库文件、可执行文件和其他资源非常重要。

  2. 处理多工作空间链 :ROS 支持多个 catkin 工作空间的叠加使用。每个工作空间可以依赖于其他工作空间中的包。setup.sh 会根据命令行参数(如 --extend--local)来决定是否覆盖或扩展已有的环境变量。--extend 参数允许当前工作空间的环境变量与之前工作空间的环境变量共存,而 --local 参数则只考虑当前工作空间,忽略链中的其他工作空间。

  3. 调用 Python 脚本生成环境变量 :脚本会调用一个 Python 脚本 _setup_util.py 来生成必要的环境变量导出命令。这个 Python 脚本会根据当前工作空间的状态动态生成正确的环境变量设置,并将结果写入一个临时文件。然后,setup.sh 会读取并执行这个临时文件中的命令。

  4. 加载环境钩子catkin 允许包提供"环境钩子"(environment hooks),即一些额外的脚本,用于在 setup.sh 执行时进一步修改环境。这些钩子可以用来设置特定包所需的环境变量或其他配置。setup.sh 会遍历所有找到的环境钩子,并依次执行它们。

  5. 处理平台差异 :脚本会检测当前操作系统(如 Linux 或 macOS),并根据平台的不同设置相应的环境变量。例如,在 Linux 上设置 LD_LIBRARY_PATH,而在 macOS 上设置 DYLD_LIBRARY_PATH

脚本的详细解释

1. 初始化和检查
sh 复制代码
: ${_CATKIN_SETUP_DIR:=/home/ssmec/catkin_ws/devel}
_SETUP_UTIL="$_CATKIN_SETUP_DIR/_setup_util.py"
unset _CATKIN_SETUP_DIR

if [ ! -f "$_SETUP_UTIL" ]; then
  echo "Missing Python script: $_SETUP_UTIL"
  return 22
fi
  • 这段代码首先设置了 _CATKIN_SETUP_DIR 变量,指向当前 catkin 工作空间的 devel 目录。如果该变量已经存在,则保持不变;否则,使用默认路径 /home/ssmec/catkin_ws/devel
  • 然后,它定义了 _SETUP_UTIL 变量,指向 devel 目录下的 _setup_util.py Python 脚本。
  • 接下来,它检查 _setup_util.py 是否存在。如果不存在,脚本会输出错误信息并返回状态码 22,表示失败。
2. 检测操作系统
sh 复制代码
_UNAME=`uname -s`
_IS_DARWIN=0
if [ "$_UNAME" = "Darwin" ]; then
  _IS_DARWIN=1
fi
unset _UNAME
  • 这段代码使用 uname -s 命令检测当前操作系统。如果操作系统是 macOS(Darwin),则将 _IS_DARWIN 设置为 1,否则保持为 0
  • 之后,它清除了 _UNAME 变量,因为它不再需要。
3. 设置环境变量
sh 复制代码
export CMAKE_PREFIX_PATH
if [ $_IS_DARWIN -eq 0 ]; then
  export LD_LIBRARY_PATH
else
  export DYLD_LIBRARY_PATH
fi
unset _IS_DARWIN
export PATH
export PKG_CONFIG_PATH
export PYTHONPATH
  • 这段代码导出了几个关键的环境变量:
    • CMAKE_PREFIX_PATH:用于指定 CMake 应查找的安装前缀。
    • LD_LIBRARY_PATH(Linux)或 DYLD_LIBRARY_PATH(macOS):用于指定动态链接器应查找的库路径。
    • PATH:用于指定可执行文件的搜索路径。
    • PKG_CONFIG_PATH:用于指定 pkg-config 应查找的 .pc 文件路径。
    • PYTHONPATH:用于指定 Python 解释器应查找的模块路径。
4. 记住 shell 类型
sh 复制代码
if [ -z "$CATKIN_SHELL" ]; then
  CATKIN_SHELL=sh
fi
  • 如果 CATKIN_SHELL 环境变量未设置,则将其设置为 sh。这个变量用于告诉 _setup_util.py 当前使用的 shell 类型,以便生成正确的 shell 语法。
5. 调用 Python 脚本生成环境变量
sh 复制代码
if [ -d "${TMPDIR:-}" ]; then
  _TMPDIR="${TMPDIR}"
else
  _TMPDIR=/tmp
fi
_SETUP_TMP=`mktemp "${_TMPDIR}/setup.sh.XXXXXXXXXX"`
unset _TMPDIR
if [ $? -ne 0 -o ! -f "$_SETUP_TMP" ]; then
  echo "Could not create temporary file: $_SETUP_TMP"
  return 1
fi
CATKIN_SHELL=$CATKIN_SHELL "$_SETUP_UTIL" $@ ${CATKIN_SETUP_UTIL_ARGS:-} >> "$_SETUP_TMP"
_RC=$?
if [ $_RC -ne 0 ]; then
  if [ $_RC -eq 2 ]; then
    echo "Could not write the output of '$_SETUP_UTIL' to temporary file '$_SETUP_TMP': may be the disk if full?"
  else
    echo "Failed to run '\"$_SETUP_UTIL\" $@': return code $_RC"
  fi
  unset _RC
  unset _SETUP_UTIL
  rm -f "$_SETUP_TMP"
  unset _SETUP_TMP
  return 1
fi
unset _RC
unset _SETUP_UTIL
. "$_SETUP_TMP"
rm -f "$_SETUP_TMP"
unset _SETUP_TMP
  • 这段代码创建了一个临时文件 _SETUP_TMP,用于存储 _setup_util.py 生成的环境变量导出命令。
  • 它调用 _setup_util.py,并将生成的命令追加到临时文件中。$@ 表示传递给 setup.sh 的所有命令行参数,CATKIN_SETUP_UTIL_ARGS 是一个环境变量,允许用户通过设置它来传递额外的参数(如 --extend--local)。
  • 如果 _setup_util.py 执行失败,脚本会输出错误信息并返回非零状态码。
  • 如果成功,脚本会读取并执行临时文件中的命令,从而应用生成的环境变量设置。
  • 最后,它删除临时文件并清理相关变量。
6. 加载环境钩子
sh 复制代码
_i=0
while [ $_i -lt $_CATKIN_ENVIRONMENT_HOOKS_COUNT ]; do
  eval _envfile=\$_CATKIN_ENVIRONMENT_HOOKS_$_i
  unset _CATKIN_ENVIRONMENT_HOOKS_$_i
  eval _envfile_workspace=\$_CATKIN_ENVIRONMENT_HOOKS_${_i}_WORKSPACE
  unset _CATKIN_ENVIRONMENT_HOOKS_${_i}_WORKSPACE
  # set workspace for environment hook
  CATKIN_ENV_HOOK_WORKSPACE=$_envfile_workspace
  . "$_envfile"
  unset CATKIN_ENV_HOOK_WORKSPACE
  _i=$((_i + 1))
done
unset _i
  • 这段代码遍历所有找到的环境钩子(通过 _CATKIN_ENVIRONMENT_HOOKS_COUNT 变量确定数量),并依次执行每个钩子。
  • 每个钩子是一个脚本文件,_envfile 包含钩子的路径,_envfile_workspace 包含钩子所属的工作空间路径。
  • 在执行每个钩子之前,脚本会设置 CATKIN_ENV_HOOK_WORKSPACE 变量,以便钩子知道它属于哪个工作空间。
  • 执行完钩子后,脚本会清除相关变量,继续处理下一个钩子。
7. 清理
sh 复制代码
unset _CATKIN_ENVIRONMENT_HOOKS_COUNT
  • 最后,脚本会清除 _CATKIN_ENVIRONMENT_HOOKS_COUNT 变量,以确保没有残留的环境变量。

总结

setup.sh 是一个非常重要的脚本,它负责为当前终端会话设置正确的环境变量,使得用户可以在该工作空间中使用 ROS 包和工具。它不仅设置了基本的环境变量,还处理了多工作空间的依赖关系,并加载了所有相关的环境钩子。通过这种方式,setup.sh 确保了 ROS 环境的正确配置,使得开发和运行 ROS 节点变得更加方便。

_setup_util.py

py 复制代码
#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Software License Agreement (BSD License)
#
# Copyright (c) 2012, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above
#    copyright notice, this list of conditions and the following
#    disclaimer in the documentation and/or other materials provided
#    with the distribution.
#  * Neither the name of Willow Garage, Inc. nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""This file generates shell code for the setup.SHELL scripts to set environment variables."""

from __future__ import print_function

import argparse
import copy
import errno
import os
import platform
import sys

CATKIN_MARKER_FILE = '.catkin'

system = platform.system()
IS_DARWIN = (system == 'Darwin')
IS_WINDOWS = (system == 'Windows')

PATH_TO_ADD_SUFFIX = ['bin']
if IS_WINDOWS:
    # while catkin recommends putting dll's into bin, 3rd party packages often put dll's into lib
    # since Windows finds dll's via the PATH variable, prepend it with path to lib
    PATH_TO_ADD_SUFFIX.extend([['lib', os.path.join('lib', 'aarch64-linux-gnu')]])

# subfolder of workspace prepended to CMAKE_PREFIX_PATH
ENV_VAR_SUBFOLDERS = {
    'CMAKE_PREFIX_PATH': '',
    'LD_LIBRARY_PATH' if not IS_DARWIN else 'DYLD_LIBRARY_PATH': ['lib', os.path.join('lib', 'aarch64-linux-gnu')],
    'PATH': PATH_TO_ADD_SUFFIX,
    'PKG_CONFIG_PATH': [os.path.join('lib', 'pkgconfig'), os.path.join('lib', 'aarch64-linux-gnu', 'pkgconfig')],
    'PYTHONPATH': 'lib/python3/dist-packages',
}


def rollback_env_variables(environ, env_var_subfolders):
    """
    Generate shell code to reset environment variables.

    by unrolling modifications based on all workspaces in CMAKE_PREFIX_PATH.
    This does not cover modifications performed by environment hooks.
    """
    lines = []
    unmodified_environ = copy.copy(environ)
    for key in sorted(env_var_subfolders.keys()):
        subfolders = env_var_subfolders[key]
        if not isinstance(subfolders, list):
            subfolders = [subfolders]
        value = _rollback_env_variable(unmodified_environ, key, subfolders)
        if value is not None:
            environ[key] = value
            lines.append(assignment(key, value))
    if lines:
        lines.insert(0, comment('reset environment variables by unrolling modifications based on all workspaces in CMAKE_PREFIX_PATH'))
    return lines


def _rollback_env_variable(environ, name, subfolders):
    """
    For each catkin workspace in CMAKE_PREFIX_PATH remove the first entry from env[NAME] matching workspace + subfolder.

    :param subfolders: list of str '' or subfoldername that may start with '/'
    :returns: the updated value of the environment variable.
    """
    value = environ[name] if name in environ else ''
    env_paths = [path for path in value.split(os.pathsep) if path]
    value_modified = False
    for subfolder in subfolders:
        if subfolder:
            if subfolder.startswith(os.path.sep) or (os.path.altsep and subfolder.startswith(os.path.altsep)):
                subfolder = subfolder[1:]
            if subfolder.endswith(os.path.sep) or (os.path.altsep and subfolder.endswith(os.path.altsep)):
                subfolder = subfolder[:-1]
        for ws_path in _get_workspaces(environ, include_fuerte=True, include_non_existing=True):
            path_to_find = os.path.join(ws_path, subfolder) if subfolder else ws_path
            path_to_remove = None
            for env_path in env_paths:
                env_path_clean = env_path[:-1] if env_path and env_path[-1] in [os.path.sep, os.path.altsep] else env_path
                if env_path_clean == path_to_find:
                    path_to_remove = env_path
                    break
            if path_to_remove:
                env_paths.remove(path_to_remove)
                value_modified = True
    new_value = os.pathsep.join(env_paths)
    return new_value if value_modified else None


def _get_workspaces(environ, include_fuerte=False, include_non_existing=False):
    """
    Based on CMAKE_PREFIX_PATH return all catkin workspaces.

    :param include_fuerte: The flag if paths starting with '/opt/ros/fuerte' should be considered workspaces, ``bool``
    """
    # get all cmake prefix paths
    env_name = 'CMAKE_PREFIX_PATH'
    value = environ[env_name] if env_name in environ else ''
    paths = [path for path in value.split(os.pathsep) if path]
    # remove non-workspace paths
    workspaces = [path for path in paths if os.path.isfile(os.path.join(path, CATKIN_MARKER_FILE)) or (include_fuerte and path.startswith('/opt/ros/fuerte')) or (include_non_existing and not os.path.exists(path))]
    return workspaces


def prepend_env_variables(environ, env_var_subfolders, workspaces):
    """Generate shell code to prepend environment variables for the all workspaces."""
    lines = []
    lines.append(comment('prepend folders of workspaces to environment variables'))

    paths = [path for path in workspaces.split(os.pathsep) if path]

    prefix = _prefix_env_variable(environ, 'CMAKE_PREFIX_PATH', paths, '')
    lines.append(prepend(environ, 'CMAKE_PREFIX_PATH', prefix))

    for key in sorted(key for key in env_var_subfolders.keys() if key != 'CMAKE_PREFIX_PATH'):
        subfolder = env_var_subfolders[key]
        prefix = _prefix_env_variable(environ, key, paths, subfolder)
        lines.append(prepend(environ, key, prefix))
    return lines


def _prefix_env_variable(environ, name, paths, subfolders):
    """
    Return the prefix to prepend to the environment variable NAME.

    Adding any path in NEW_PATHS_STR without creating duplicate or empty items.
    """
    value = environ[name] if name in environ else ''
    environ_paths = [path for path in value.split(os.pathsep) if path]
    checked_paths = []
    for path in paths:
        if not isinstance(subfolders, list):
            subfolders = [subfolders]
        for subfolder in subfolders:
            path_tmp = path
            if subfolder:
                path_tmp = os.path.join(path_tmp, subfolder)
            # skip nonexistent paths
            if not os.path.exists(path_tmp):
                continue
            # exclude any path already in env and any path we already added
            if path_tmp not in environ_paths and path_tmp not in checked_paths:
                checked_paths.append(path_tmp)
    prefix_str = os.pathsep.join(checked_paths)
    if prefix_str != '' and environ_paths:
        prefix_str += os.pathsep
    return prefix_str


def assignment(key, value):
    if not IS_WINDOWS:
        return 'export %s="%s"' % (key, value)
    else:
        return 'set %s=%s' % (key, value)


def comment(msg):
    if not IS_WINDOWS:
        return '# %s' % msg
    else:
        return 'REM %s' % msg


def prepend(environ, key, prefix):
    if key not in environ or not environ[key]:
        return assignment(key, prefix)
    if not IS_WINDOWS:
        return 'export %s="%s$%s"' % (key, prefix, key)
    else:
        return 'set %s=%s%%%s%%' % (key, prefix, key)


def find_env_hooks(environ, cmake_prefix_path):
    """Generate shell code with found environment hooks for the all workspaces."""
    lines = []
    lines.append(comment('found environment hooks in workspaces'))

    generic_env_hooks = []
    generic_env_hooks_workspace = []
    specific_env_hooks = []
    specific_env_hooks_workspace = []
    generic_env_hooks_by_filename = {}
    specific_env_hooks_by_filename = {}
    generic_env_hook_ext = 'bat' if IS_WINDOWS else 'sh'
    specific_env_hook_ext = environ['CATKIN_SHELL'] if not IS_WINDOWS and 'CATKIN_SHELL' in environ and environ['CATKIN_SHELL'] else None
    # remove non-workspace paths
    workspaces = [path for path in cmake_prefix_path.split(os.pathsep) if path and os.path.isfile(os.path.join(path, CATKIN_MARKER_FILE))]
    for workspace in reversed(workspaces):
        env_hook_dir = os.path.join(workspace, 'etc', 'catkin', 'profile.d')
        if os.path.isdir(env_hook_dir):
            for filename in sorted(os.listdir(env_hook_dir)):
                if filename.endswith('.%s' % generic_env_hook_ext):
                    # remove previous env hook with same name if present
                    if filename in generic_env_hooks_by_filename:
                        i = generic_env_hooks.index(generic_env_hooks_by_filename[filename])
                        generic_env_hooks.pop(i)
                        generic_env_hooks_workspace.pop(i)
                    # append env hook
                    generic_env_hooks.append(os.path.join(env_hook_dir, filename))
                    generic_env_hooks_workspace.append(workspace)
                    generic_env_hooks_by_filename[filename] = generic_env_hooks[-1]
                elif specific_env_hook_ext is not None and filename.endswith('.%s' % specific_env_hook_ext):
                    # remove previous env hook with same name if present
                    if filename in specific_env_hooks_by_filename:
                        i = specific_env_hooks.index(specific_env_hooks_by_filename[filename])
                        specific_env_hooks.pop(i)
                        specific_env_hooks_workspace.pop(i)
                    # append env hook
                    specific_env_hooks.append(os.path.join(env_hook_dir, filename))
                    specific_env_hooks_workspace.append(workspace)
                    specific_env_hooks_by_filename[filename] = specific_env_hooks[-1]
    env_hooks = generic_env_hooks + specific_env_hooks
    env_hooks_workspace = generic_env_hooks_workspace + specific_env_hooks_workspace
    count = len(env_hooks)
    lines.append(assignment('_CATKIN_ENVIRONMENT_HOOKS_COUNT', count))
    for i in range(count):
        lines.append(assignment('_CATKIN_ENVIRONMENT_HOOKS_%d' % i, env_hooks[i]))
        lines.append(assignment('_CATKIN_ENVIRONMENT_HOOKS_%d_WORKSPACE' % i, env_hooks_workspace[i]))
    return lines


def _parse_arguments(args=None):
    parser = argparse.ArgumentParser(description='Generates code blocks for the setup.SHELL script.')
    parser.add_argument('--extend', action='store_true', help='Skip unsetting previous environment variables to extend context')
    parser.add_argument('--local', action='store_true', help='Only consider this prefix path and ignore other prefix path in the environment')
    return parser.parse_known_args(args=args)[0]


if __name__ == '__main__':
    try:
        try:
            args = _parse_arguments()
        except Exception as e:
            print(e, file=sys.stderr)
            sys.exit(1)

        if not args.local:
            # environment at generation time
            CMAKE_PREFIX_PATH = r'/home/ssmec/catkin_ws/install;/home/ssmec/catkin_ws/devel;/opt/ros/noetic'.split(';')
        else:
            # don't consider any other prefix path than this one
            CMAKE_PREFIX_PATH = []
        # prepend current workspace if not already part of CPP
        base_path = os.path.dirname(__file__)
        # CMAKE_PREFIX_PATH uses forward slash on all platforms, but __file__ is platform dependent
        # base_path on Windows contains backward slashes, need to be converted to forward slashes before comparison
        if os.path.sep != '/':
            base_path = base_path.replace(os.path.sep, '/')

        if base_path not in CMAKE_PREFIX_PATH:
            CMAKE_PREFIX_PATH.insert(0, base_path)
        CMAKE_PREFIX_PATH = os.pathsep.join(CMAKE_PREFIX_PATH)

        environ = dict(os.environ)
        lines = []
        if not args.extend:
            lines += rollback_env_variables(environ, ENV_VAR_SUBFOLDERS)
        lines += prepend_env_variables(environ, ENV_VAR_SUBFOLDERS, CMAKE_PREFIX_PATH)
        lines += find_env_hooks(environ, CMAKE_PREFIX_PATH)
        print('\n'.join(lines))

        # need to explicitly flush the output
        sys.stdout.flush()
    except IOError as e:
        # and catch potential "broken pipe" if stdout is not writable
        # which can happen when piping the output to a file but the disk is full
        if e.errno == errno.EPIPE:
            print(e, file=sys.stderr)
            sys.exit(2)
        raise

    sys.exit(0)

_setup_util.py 的完整功能解析

_setup_util.py 是 ROS(Robot Operating System)中 catkin 工作空间的一部分,它的主要职责是生成用于设置环境变量的 shell 代码。这个 Python 脚本被 setup.sh 调用,以确保当前终端会话中的环境变量正确配置,使得用户可以在该工作空间中使用 ROS 包和工具。

文件结构与功能

1. 全局变量和常量
python 复制代码
CATKIN_MARKER_FILE = '.catkin'
system = platform.system()
IS_DARWIN = (system == 'Darwin')
IS_WINDOWS = (system == 'Windows')
PATH_TO_ADD_SUFFIX = ['bin']
if IS_WINDOWS:
    PATH_TO_ADD_SUFFIX.extend([['lib', os.path.join('lib', 'aarch64-linux-gnu')]])
  • CATKIN_MARKER_FILE :定义了标记文件 .catkin,用于识别 catkin 工作空间。
  • system:获取当前操作系统名称。
  • IS_DARWINIS_WINDOWS:分别标识是否为 macOS 和 Windows 系统。
  • PATH_TO_ADD_SUFFIX :定义了要添加到 PATH 环境变量的子目录,默认是 bin。在 Windows 上,还会添加 liblib/aarch64-linux-gnu
2. 环境变量子目录映射
python 复制代码
ENV_VAR_SUBFOLDERS = {
    'CMAKE_PREFIX_PATH': '',
    'LD_LIBRARY_PATH' if not IS_DARWIN else 'DYLD_LIBRARY_PATH': ['lib', os.path.join('lib', 'aarch64-linux-gnu')],
    'PATH': PATH_TO_ADD_SUFFIX,
    'PKG_CONFIG_PATH': [os.path.join('lib', 'pkgconfig'), os.path.join('lib', 'aarch64-linux-gnu', 'pkgconfig')],
    'PYTHONPATH': 'lib/python3/dist-packages',
}
  • ENV_VAR_SUBFOLDERS :定义了每个环境变量对应的子目录。例如,CMAKE_PREFIX_PATH 没有子目录,而 LD_LIBRARY_PATH(或 macOS 上的 DYLD_LIBRARY_PATH)对应 liblib/aarch64-linux-gnu 子目录。PATH 对应 binPKG_CONFIG_PATH 对应 lib/pkgconfiglib/aarch64-linux-gnu/pkgconfigPYTHONPATH 对应 lib/python3/dist-packages
3. 命令行参数解析
python 复制代码
def _parse_arguments(args=None):
    parser = argparse.ArgumentParser(description='Generates code blocks for the setup.SHELL script.')
    parser.add_argument('--extend', action='store_true', help='Skip unsetting previous environment variables to extend context')
    parser.add_argument('--local', action='store_true', help='Only consider this prefix path and ignore other prefix path in the environment')
    return parser.parse_known_args(args=args)[0]
  • _parse_arguments :解析命令行参数。支持两个选项:
    • --extend:跳过撤销之前设置的环境变量,直接扩展当前环境。
    • --local:只考虑当前工作空间,忽略链中的其他工作空间。
4. 主程序逻辑
python 复制代码
if __name__ == '__main__':
    try:
        try:
            args = _parse_arguments()
        except Exception as e:
            print(e, file=sys.stderr)
            sys.exit(1)

        if not args.local:
            # environment at generation time
            CMAKE_PREFIX_PATH = r'/home/ssmec/catkin_ws/install;/home/ssmec/catkin_ws/devel;/opt/ros/noetic'.split(';')
        else:
            # don't consider any other prefix path than this one
            CMAKE_PREFIX_PATH = []

        # prepend current workspace if not already part of CPP
        base_path = os.path.dirname(__file__)
        # CMAKE_PREFIX_PATH uses forward slash on all platforms, but __file__ is platform dependent
        # base_path on Windows contains backward slashes, need to be converted to forward slashes before comparison
        if os.path.sep != '/':
            base_path = base_path.replace(os.path.sep, '/')

        if base_path not in CMAKE_PREFIX_PATH:
            CMAKE_PREFIX_PATH.insert(0, base_path)
        CMAKE_PREFIX_PATH = os.pathsep.join(CMAKE_PREFIX_PATH)

        environ = dict(os.environ)
        lines = []
        if not args.extend:
            lines += rollback_env_variables(environ, ENV_VAR_SUBFOLDERS)
        lines += prepend_env_variables(environ, ENV_VAR_SUBFOLDERS, CMAKE_PREFIX_PATH)
        lines += find_env_hooks(environ, CMAKE_PREFIX_PATH)
        print('\n'.join(lines))

        # need to explicitly flush the output
        sys.stdout.flush()
    except IOError as e:
        # and catch potential "broken pipe" if stdout is not writable
        # which can happen when piping the output to a file but the disk is full
        if e.errno == errno.EPIPE:
            print(e, file=sys.stderr)
            sys.exit(2)
        raise

    sys.exit(0)

主程序的主要步骤

  1. 解析命令行参数

    • 使用 _parse_arguments 函数解析命令行参数,获取 --extend--local 选项。
  2. 确定 CMAKE_PREFIX_PATH

    • 如果没有指定 --local 选项,则 CMAKE_PREFIX_PATH 包含当前工作空间的 installdevel 目录,以及系统安装的 ROS 版本路径(如 /opt/ros/noetic)。
    • 如果指定了 --local 选项,则 CMAKE_PREFIX_PATH 只包含当前工作空间的路径,忽略其他工作空间。
  3. 处理当前工作空间路径

    • 获取当前脚本所在的目录(即当前工作空间的路径),并将其转换为使用正斜杠(/),以便与 CMAKE_PREFIX_PATH 中的路径格式一致。
    • 如果当前工作空间路径不在 CMAKE_PREFIX_PATH 中,则将其插入到 CMAKE_PREFIX_PATH 的首位。
  4. 生成环境变量设置代码

    • 撤销之前的环境变量修改 :如果未指定 --extend 选项,则调用 rollback_env_variables 函数,撤销之前可能已经设置的环境变量修改。
    • 前置当前工作空间的路径 :调用 prepend_env_variables 函数,将当前工作空间的路径前置到各个环境变量中(如 CMAKE_PREFIX_PATHLD_LIBRARY_PATHPATH 等)。
    • 查找并加载环境钩子 :调用 find_env_hooks 函数,查找并加载所有找到的环境钩子(environment hooks),这些钩子可以进一步修改环境变量或其他配置。
  5. 输出生成的 shell 代码

    • 将生成的所有 shell 代码行连接成一个字符串,并打印到标准输出。
    • 显式刷新标准输出,确保所有内容都被写入。
  6. 异常处理

    • 捕获潜在的 I/O 错误,特别是当标准输出不可写时(例如磁盘已满的情况),并返回相应的错误码。
  7. 退出程序

    • 正常情况下,程序以状态码 0 退出,表示成功执行。

关键函数解析

1. rollback_env_variables
python 复制代码
def rollback_env_variables(environ, env_var_subfolders):
    """
    Generate shell code to reset environment variables.
    by unrolling modifications based on all workspaces in CMAKE_PREFIX_PATH.
    This does not cover modifications performed by environment hooks.
    """
    lines = []
    unmodified_environ = copy.copy(environ)
    for key in sorted(env_var_subfolders.keys()):
        subfolders = env_var_subfolders[key]
        if not isinstance(subfolders, list):
            subfolders = [subfolders]
        value = _rollback_env_variable(unmodified_environ, key, subfolders)
        if value is not None:
            environ[key] = value
            lines.append(assignment(key, value))
    if lines:
        lines.insert(0, comment('reset environment variables by unrolling modifications based on all workspaces in CMAKE_PREFIX_PATH'))
    return lines
  • 功能 :生成用于重置环境变量的 shell 代码,撤销基于 CMAKE_PREFIX_PATH 中所有工作空间的修改。
  • 逻辑 :遍历 env_var_subfolders 中定义的环境变量,调用 _rollback_env_variable 函数移除与每个工作空间相关的路径,然后生成相应的 shell 代码。
2. _rollback_env_variable
python 复制代码
def _rollback_env_variable(environ, name, subfolders):
    """
    For each catkin workspace in CMAKE_PREFIX_PATH remove the first entry from env[NAME] matching workspace + subfolder.
    :param subfolders: list of str '' or subfoldername that may start with '/'
    :returns: the updated value of the environment variable.
    """
    value = environ[name] if name in environ else ''
    env_paths = [path for path in value.split(os.pathsep) if path]
    value_modified = False
    for subfolder in subfolders:
        if subfolder:
            if subfolder.startswith(os.path.sep) or (os.path.altsep and subfolder.startswith(os.path.altsep)):
                subfolder = subfolder[1:]
            if subfolder.endswith(os.path.sep) or (os.path.altsep and subfolder.endswith(os.path.altsep)):
                subfolder = subfolder[:-1]
        for ws_path in _get_workspaces(environ, include_fuerte=True, include_non_existing=True):
            path_to_find = os.path.join(ws_path, subfolder) if subfolder else ws_path
            path_to_remove = None
            for env_path in env_paths:
                env_path_clean = env_path[:-1] if env_path and env_path[-1] in [os.path.sep, os.path.altsep] else env_path
                if env_path_clean == path_to_find:
                    path_to_remove = env_path
                    break
            if path_to_remove:
                env_paths.remove(path_to_remove)
                value_modified = True
    new_value = os.pathsep.join(env_paths)
    return new_value if value_modified else None
  • 功能 :从环境变量中移除与每个 catkin 工作空间相关的路径。
  • 逻辑 :遍历 CMAKE_PREFIX_PATH 中的工作空间路径,检查环境变量中是否有与工作空间子目录匹配的路径,如果有则移除该路径,并返回更新后的环境变量值。
3. _get_workspaces
python 复制代码
def _get_workspaces(environ, include_fuerte=False, include_non_existing=False):
    """
    Based on CMAKE_PREFIX_PATH return all catkin workspaces.
    :param include_fuerte: The flag if paths starting with '/opt/ros/fuerte' should be considered workspaces, ``bool``
    """
    # get all cmake prefix paths
    env_name = 'CMAKE_PREFIX_PATH'
    value = environ[env_name] if env_name in environ else ''
    paths = [path for path in value.split(os.pathsep) if path]
    # remove non-workspace paths
    workspaces = [path for path in paths if os.path.isfile(os.path.join(path, CATKIN_MARKER_FILE)) or (include_fuerte and path.startswith('/opt/ros/fuerte')) or (include_non_existing and not os.path.exists(path))]
    return workspaces
  • 功能 :根据 CMAKE_PREFIX_PATH 返回所有 catkin 工作空间的路径。
  • 逻辑 :遍历 CMAKE_PREFIX_PATH 中的路径,检查每个路径是否包含 .catkin 标记文件,或者是否符合特定条件(如 /opt/ros/fuerte),并返回符合条件的工作空间路径。
4. prepend_env_variables
python 复制代码
def prepend_env_variables(environ, env_var_subfolders, workspaces):
    """Generate shell code to prepend environment variables for the all workspaces."""
    lines = []
    lines.append(comment('prepend folders of workspaces to environment variables'))

    paths = [path for path in workspaces.split(os.pathsep) if path]

    prefix = _prefix_env_variable(environ, 'CMAKE_PREFIX_PATH', paths, '')
    lines.append(prepend(environ, 'CMAKE_PREFIX_PATH', prefix))

    for key in sorted(key for key in env_var_subfolders.keys() if key != 'CMAKE_PREFIX_PATH'):
        subfolder = env_var_subfolders[key]
        prefix = _prefix_env_variable(environ, key, paths, subfolder)
        lines.append(prepend(environ, key, prefix))
    return lines
  • 功能:生成用于前置环境变量的 shell 代码,将当前工作空间的路径添加到各个环境变量中。
  • 逻辑 :遍历 env_var_subfolders 中定义的环境变量,调用 _prefix_env_variable 函数生成前置路径,然后生成相应的 shell 代码。
5. _prefix_env_variable
python 复制代码
def _prefix_env_variable(environ, name, paths, subfolders):
    """
    Return the prefix to prepend to the environment variable NAME.

    Adding any path in NEW_PATHS_STR without creating duplicate or empty items.
    """
    value = environ[name] if name in environ else ''
    environ_paths = [path for path in value.split(os.pathsep) if path]
    checked_paths = []
    for path in paths:
        if not isinstance(subfolders, list):
            subfolders = [subfolders]
        for subfolder in subfolders:
            path_tmp = path
            if subfolder:
                path_tmp = os.path.join(path_tmp, subfolder)
            # skip nonexistent paths
            if not os.path.exists(path_tmp):
                continue
            # exclude any path already in env and any path we already added
            if path_tmp not in environ_paths and path_tmp not in checked_paths:
                checked_paths.append(path_tmp)
    prefix_str = os.pathsep.join(checked_paths)
    if prefix_str != '' and environ_paths:
        prefix_str += os.pathsep
    return prefix_str
  • 功能:生成要前置到环境变量的路径字符串,确保不会添加重复或无效的路径。
  • 逻辑 :遍历 paths 中的路径,检查每个路径是否存在,并确保不会添加已经存在于环境变量中的路径,最后返回前置路径字符串。
6. find_env_hooks
python 复制代码
def find_env_hooks(environ, cmake_prefix_path):
    """Generate shell code with found environment hooks for the all workspaces."""
    lines = []
    lines.append(comment('found environment hooks in workspaces'))

    generic_env_hooks = []
    generic_env_hooks_workspace = []
    specific_env_hooks = []
    specific_env_hooks_workspace = []
    generic_env_hooks_by_filename = {}
    specific_env_hooks_by_filename = {}
    generic_env_hook_ext = 'bat' if IS_WINDOWS else 'sh'
    specific_env_hook_ext = environ['CATKIN_SHELL'] if not IS_WINDOWS and 'CATKIN_SHELL' in environ and environ['CATKIN_SHELL'] else None

    workspaces = [path for path in cmake_prefix_path.split(os.pathsep) if path and os.path.isfile(os.path.join(path, CATKIN_MARKER_FILE))]
    for workspace in reversed(workspaces):
        env_hook_dir = os.path.join(workspace, 'etc', 'catkin', 'profile.d')
        if os.path.isdir(env_hook_dir):
            for filename in sorted(os.listdir(env_hook_dir)):
                if filename.endswith('.%s' % generic_env_hook_ext):
                    # remove previous env hook with same name if present
                    if filename in generic_env_hooks_by_filename:
                        i = generic_env_hooks.index(generic_env_hooks_by_filename[filename])
                        generic_env_hooks.pop(i)
                        generic_env_hooks_workspace.pop(i)
                    # append env hook
                    generic_env_hooks.append(os.path.join(env_hook_dir, filename))
                    generic_env_hooks_workspace.append(workspace)
                    generic_env_hooks_by_filename[filename] = generic_env_hooks[-1]
                elif specific_env_hook_ext is not None and filename.endswith('.%s' % specific_env_hook_ext):
                    # remove previous env hook with same name if present
                    if filename in specific_env_hooks_by_filename:
                        i = specific_env_hooks.index(specific_env_hooks_by_filename[filename])
                        specific_env_hooks.pop(i)
                        specific_env_hooks_workspace.pop(i)
                    # append env hook
                    specific_env_hooks.append(os.path.join(env_hook_dir, filename))
                    specific_env_hooks_workspace.append(workspace)
                    specific_env_hooks_by_filename[filename] = specific_env_hooks[-1]

    env_hooks = generic_env_hooks + specific_env_hooks
    env_hooks_workspace = generic_env_hooks_workspace + specific_env_hooks_workspace
    count = len(env_hooks)
    lines.append(assignment('_CATKIN_ENVIRONMENT_HOOKS_COUNT', count))
    for i in range(count):
        lines.append(assignment('_CATKIN_ENVIRONMENT_HOOKS_%d' % i, env_hooks[i]))
        lines.append(assignment('_CATKIN_ENVIRONMENT_HOOKS_%d_WORKSPACE' % i, env_hooks_workspace[i]))
    return lines
  • 功能:查找并加载所有找到的环境钩子(environment hooks),生成相应的 shell 代码。
  • 逻辑 :遍历 CMAKE_PREFIX_PATH 中的工作空间,查找 etc/catkin/profile.d 目录下的环境钩子文件(如 .sh.bat),并生成用于加载这些钩子的 shell 代码。确保不会加载重复的钩子文件,并且优先加载当前工作空间的钩子。

总结

_setup_util.py 的主要作用是生成用于设置环境变量的 shell 代码,确保当前终端会话中的环境变量正确配置,使得用户可以在 catkin 工作空间中使用 ROS 包和工具。它通过以下步骤实现这一目标:

  1. 解析命令行参数:决定是否扩展或覆盖已有的环境变量。
  2. 确定 CMAKE_PREFIX_PATH :根据 --local 选项决定是否只考虑当前工作空间。
  3. 处理当前工作空间路径 :确保当前工作空间的路径被正确添加到 CMAKE_PREFIX_PATH 中。
  4. 生成环境变量设置代码:撤销之前的环境变量修改,前置当前工作空间的路径,并查找并加载环境钩子。
  5. 输出生成的 shell 代码 :将生成的 shell 代码打印到标准输出,供 setup.sh 等脚本使用。

通过这种方式,_setup_util.py 确保了 ROS 环境的正确配置,使得开发和运行 ROS 节点变得更加方便。

相关推荐
诸神缄默不语19 分钟前
如何用Python 3自动打开exe程序
python·os·subprocess·python 3
橘子师兄40 分钟前
分页功能组件开发
数据库·python·django
Logintern091 小时前
使用VS Code进行Python编程的一些快捷方式
开发语言·python
Multiple-ji1 小时前
想学python进来看看把
开发语言·python
liuyuzhongcc2 小时前
List 接口中的 sort 和 forEach 方法
java·数据结构·python·list
鸟哥大大2 小时前
【Python】pypinyin-汉字拼音转换工具
python·自然语言处理
jiugie2 小时前
MongoDB学习
数据库·python·mongodb
阿尔法波2 小时前
python与pycharm如何设置文件夹为源代码根目录
开发语言·python·pycharm
xing25163 小时前
pytest下allure
开发语言·python·pytest
眸笑丶3 小时前
使用 Python 调用 Ollama API 并调用 deepseek-r1:8b 模型
开发语言·python