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 节点变得更加方便。

相关推荐
笨鸟先飞,勤能补拙2 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能3 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂4 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study4 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境5 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境5 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1235 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102166 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb006 小时前
一个项目带你入门AI应用开发01
python
0566466 小时前
Python康复训练——常用标准库
开发语言·python·学习