在CMake中加入路径

4

我正在尝试将两条路径连接起来:

SET(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/inkscape")

但是当CMAKE_INSTALL_LIBDIR包含绝对路径时,字符串连接并不能真正解决这个问题。

是否有一个CMake函数,可以将多个路径参数合并,将右侧相对路径添加到最右侧的绝对路径中,就像Python的os.path.join函数一样?

以下是Python解释器展示所需行为的示例:

>>> from os.path import join
>>> join("/foo/bar", "/baz/qux")
'/baz/qux'
>>> join("foo/bar", "/baz/qux")
'/baz/qux'
>>> join("/foo/bar", "./baz/qux")
'/foo/bar/./baz/qux'
>>> join("/foo/bar", "../baz/qux")
'/foo/bar/../baz/qux'
>>> join("./foo/bar", "baz/qux")
'./foo/bar/baz/qux'

我需要处理两种情况,一种是前缀为绝对路径的情况(例如CMAKE_INSTALL_PREFIX),另一种是前缀为相对路径的情况(例如$ORIGIN/..${prefix},这通常在pkg-config文件中需要)。同时,我还需要处理使用相对路径CMAKE_INSTALL_LIBDIR和使用绝对路径的Linux发行版。


2
你想要做什么?CMAKE_INSTALL_PREFIX是绝对路径,CMAKE_INSTALL_LIBDIR也是(大多数情况下)。为什么不直接使用${CMAKE_INSTALL_LIBDIR}/foo呢?请参考https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html。 - Justin
请提供您尝试过的示例(即结果目录路径)和所需目录路径的示例。 - Kevin
@Justin,我认为CMAKE_INSTALL_LIBDIR大多数情况下不是绝对的。你提供的文档举例说明:“在Debian上,liblib64lib/<multiarch-tuple>”。作为一个发行包维护者,我遇到的很多软件项目都假定未加后缀_FULLCMAKE_INSTALL_xxx是相对路径。 - Jan Tojnar
我很难相信CMake没有一个简单的函数来连接两个路径,而Meson从一开始就有它,并且甚至引入了一个“/”运算符来使操作更加方便。 - Jan Tojnar
1
@Justin 那对于特定情况可能有效,但对于其他组合则无效。我现在已将示例更改为Inkscape中的实际代码,以使问题更清晰明了。 - Jan Tojnar
显示剩余5条评论
2个回答

3
2020年9月:cmake_path命令刚刚被合并: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/5158

我已经向上游问题发送了一个示例实现。它支持多个参数,就像Python的os.path.join一样,并且至少在Linux上可以工作:

# Modelled after Python’s os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
    set(temp_path "${first_path_segment}")
    foreach(current_segment IN LISTS ARGN)
        if(NOT ("${current_segment}" STREQUAL ""))
            if(IS_ABSOLUTE "${current_segment}")
                set(temp_path "${current_segment}")
            else()
                set(temp_path "${temp_path}/${current_segment}")
            endif()
        endif()
    endforeach()
    set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()

如果CMake能够直接支持这样的重要功能,那将会非常好。


2

编辑:根据使用者的反馈更新

CMake并没有原生支持你想要的功能。不过,你可以很容易地通过创建自己的if语句(使用IS_ABSOLUTE)来实现你描述的四种情况:

if 语句是一种条件语句,可用于控制在何种情况下执行CMake脚本中的一段代码。IS_ABSOLUTE则是一个用于检查路径是否是绝对路径的参数。

if(IS_ABSOLUTE ${PREFIX_DIR})
    if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
        # Both absolute.
        set(CMAKE_INSTALL_PKGLIBDIR ....)
    else()
        # Prefix is absolute, but LIBDIR is relative.
        set(CMAKE_INSTALL_PKGLIBDIR ....)
    endif()
else()
    if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
        # Prefix is relative, but LIBDIR is absolute.
        set(CMAKE_INSTALL_PKGLIBDIR ....)
    else()
        # Both are relative.
        set(CMAKE_INSTALL_PKGLIBDIR ....)
    endif()
endif()

这可以被概括并制成一个名为directory_join()函数,在您需要的任何地方都可以在CMake文件中使用它。假设我正确解释了您的问题,您可以使用CMake实现join机制。如果您有一些绝对路径和一些相对路径想要连接,get_filename_component()命令可以帮助您。这是一个通用示例:
set(ABS_PATH "C:/the/absolute/path")
set(REL_PATH "../../some/other/relative/path")

# Concatenate your absolute and relative path(s) here.
get_filename_component(COMBINED_PATH ${ABS_PATH}/${REL_PATH} ABSOLUTE)

# Print our merged path to verify.
message(STATUS "COMBINED_PATH: ${COMBINED_PATH}")

我们可以从打印输出中看到,新变量COMBINED_PATH将这两个路径连接起来,解决了任何相对位置的问题。最初的回答。
COMBINED_PATH: C:/the/some/other/relative/path

很遗憾,这不是我需要的,因为它无法处理两个绝对路径。我需要处理两种情况,即前缀是绝对的(例如 CMAKE_INSTALL_PREFIX),以及前缀是相对的(例如 $ORIGIN/..${prefix},通常用于 pkg-config 文件)。而且,我还需要处理使用相对 CMAKE_INSTALL_LIBDIR 的 Linux 发行版和使用绝对 CMAKE_INSTALL_LIBDIR 的发行版。 - Jan Tojnar
@JanTojnar 我更新了我的回复,针对你描述的四种情况提供了额外的解决方案。 - Kevin
我向CMake发起了问题(https://gitlab.kitware.com/cmake/cmake/issues/19568),因为我觉得省略如此重要的功能是一个严重的错误。 - Jan Tojnar
@JanTojnar 我同意 if 语句的解决方案很繁琐。但是你可以将其泛化为适合于实用程序function,并在任何需要它的地方使用,就像你会使用os.path.join()一样。 - Kevin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接