将std::filesystem添加到CMake项目中的问题

12

我刚接触CMake项目,并想在我的项目中使用文件系统库。我使用的是Ubuntu 18.04, GCC 8.2 和 CMake 3.13。为了实现这个目标,我尝试了两个选项:

选项1

cmake_minimum_required(VERSION 3.13)  
project(TheFsProject)  
set(CMAKE_CXX_STANDARD 17)  
set(CMAKE_CXX_FLAGS "-std=c++17 -lstdc++fs")  

这并没有帮助,因为编译器在编译时仍然无法找到文件系统库。

选项2(摘自:https://www.scivision.co/cmake-cpp-17-filesystem/

make_minimum_required(VERSION 3.13)
project(TheFsProject)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_REQUIRED_FLAGS -std=c++17)
include(CheckCXXSymbolExists)
CHECK_CXX_SYMBOL_EXISTS(std::filesystem::path::preferred_separator cxx17fs)

if(cxx17fs)
  add_executable(TheFsProject main.cpp)
  set_property(TARGET TheFsProject PROPERTY CXX_STANDARD 17)
endif()

这也没有帮助,因为我遇到了一个我不理解的CMake错误。

(CHECK_CXX_SYMBOL_EXISTS):  
 CHECK_CXX_SYMBOL_EXISTS Macro invoked with incorrect arguments for macro named: CHECK_CXX_SYMBOL_EXISTS

我对这个主题感到很陌生,所以才来这里寻求帮助。我愿意花更多的时间去了解,但我不知道该在哪里寻找。非常感谢任何帮助!

编辑1

感谢迄今为止的回复!基于您的反馈,我做出了选项3

cmake_minimum_required(VERSION 3.13)
project(TheFsProject)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(TheFsProject main.cpp)
target_link_libraries(TheFsProject stdc++fs)

很遗憾,它没有解决我的问题。在编译期间仍会发出错误,表示找不到编译头文件。

第二次编辑

感谢到目前为止的所有回复。所有这些都有所帮助。我最后尝试了Ashkan的答案(因为它看起来比较困难)。这个返回

编译器缺少文件系统功能。

所以我猜测那方面出了问题。从这个意义上讲,这是有用的,因为我现在知道这可能不是由于我的CMake文件。现在我必须找出为什么编译器支持文件系统头文件...

第三次编辑

严格来说,这个问题已经得到了解答,因为我的问题涉及CMake文件。我将标记Ashkan的答案为解决方案,仅仅因为它提供了我下一步的故障排除搜索。如果可以的话,我也会标记lubgr的答案,因为我认为那是一个非常好的答案。谢谢大家!


对于那些感兴趣的人,我犯了一个错误,认为我有GCC和G++ 8.2。我之所以会犯这个错误,是因为CLion报告它有GDB 8.2,我认为这意味着安装了GCC 8.2和G++ 8.2。此外,我期望这些被安装,因为我真的不认为包管理器不会自动更新。但我错了;Ubuntu 18.04附带版本7.3,并且不会自动更新。您需要手动修复此问题。 - MaestroMaus
一些版本的Boost使用标志Boost_INCLUDE_DIR,而其他版本使用标志Boost_INCLUDEDIR没有下划线)。您可以通过阅读path-to-cmake/Modules/FindBoost.cmake下的FindBoost.cmake文件来检查适合您情况的正确标志。 - marcelosalloum
4个回答

13

Gcc 8.2.版本已经包含了<filesystem>,不需要再查看可用性。接下来,选项1是足够的,但需要进行修复:


set(CMAKE_CXX_STANDARD 17) # no need to manually adjust the CXXFLAGS

add_executable(yourExecutable yourSourceFile.cpp)

target_link_libraries(yourExecutable stdc++fs)

这应该导致使用-std=c++17-std=gnu++17编译源代码,并在链接时添加-lstdc++fs

编辑:请注意,正如评论中的@Ashkan所指出的那样,将CMAKE_CXX_STANDARD_REQUIRED设置为true会在配置时立即出错,如果编译器不支持C++17,而不是在链接时(由于缺少共享库)或编译时(由于缺少<filesystem>头文件)发生错误。这可能是可取的。


1
我认为你也需要这个标志 -DCMAKE_CXX_STANDARD_REQUIRED=ON。 - Ashkan
1
如果编译器不支持所需的标准设置,那么这将导致配置步骤立即失败,对吗?我认为这是可选的,因为如果不支持C++17,则编译无论如何都不会成功。 - lubgr
3
没错,但如果你能把错误放在正确的位置,那么解决它会更容易。这样做,CMake 会退出而不是让 make 给一个编译错误。 - Ashkan
我认为在macOS上,Xcode 10发布版本会告诉你它支持C++17,但它并不包括文件系统头文件,甚至不包括实验性的文件系统头文件(与beta版本相比)。 - John

13
除了@lubgr的回答外,我认为更完整的方法是使用try_compile来验证您是否可以实际使用文件系统头文件。在我看来,这样做更好,因为有些编译器还不支持std::filesystem。另外,在gcc 7.x中,您可以在experimental命名空间下找到文件系统。这样,您可以在else子句中使用单独的try_compile并检测它。
以下是相关的cmake代码:
# set everything up for c++ 17 features
set(CMAKE_CXX_STANDARD 17)
# Don't add this line if you will try_compile with boost.
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# test that filesystem header actually is there and works
try_compile(HAS_FS "${CMAKE_BINARY_DIR}/temp" 
"${CMAKE_SOURCE_DIR}/tests/has_filesystem.cc" 
            CMAKE_FLAGS -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON
            LINK_LIBRARIES stdc++fs)
if(HAS_FS)
    message(STATUS "Compiler has filesystem support")
else()
#   .... You could also try searching for boost::filesystem here.
    message(FATAL_ERROR "Compiler is missing filesystem capabilities")
endif(HAS_FS)

文件tests/has_filesystem.cc非常简单。
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    fs::path aPath {"../"};

    return 0;
}

在else语句中,您可以尝试使用boost::filesystem的try_compile,并传递一个指令,该指令可用于源文件中,在那里您可以决定是否要使用c++17文件系统或boost。


4

CHECK_CXX_SYMBOL_EXISTS需要三个参数,而不是两个:

include(CheckCXXSymbolExists)
check_cxx_symbol_exists(std::filesystem::path::preferred_separator filesystem cxx17fs)

你忘记告诉CMake在哪里查找符号(声明它们的头文件)。


2
不幸的是,在GCC-8上,即使需要stdc++fs进行成功链接,这也会在cxx17fs中返回true。 - user35443
有没有其他符号可以检查这个?很乐意更新我的答案,或者很乐意为您的答案点赞,并提供一个适用于gcc-8(及以上版本)的工作测试。 - Matthieu Brucher
我不知道,这是我第一次尝试使用它。我敢猜测std::filesystem::path::preferred_separator可以被找到,因为它从头文件编译到目标可执行文件中,甚至有些符号已经在libstdc++中了。下次我在那个分支时,我会考虑尝试一个不同的stdc++fs符号。 - user35443

1
我发现了一个情况,当使用Intel C++编译器(icpc(ICC)19.1.1.216 20200306)在运行MacOS“Mojave”10.14.6的C++17模式时,try_compile是不够的。由@Ashkan推荐的测试程序编译没有错误,甚至可以运行。然而,我的代码在某个点上使用了fs::path::filename(),结果导致了运行时链接器错误(dyld: lazy symbol binding failed: Symbol not found:)。换句话说:头文件是存在的,但实现似乎并不存在(?)。我没有进一步调查这个问题。
解决方法是使用try_run代替try_compile,并且(在我的情况下)如果std::filesystem尚未支持,则回退到boost::filesystem
以下是相关的CMake代码部分:
try_run(RUNS_WITH_STDFS COMPILES_WITH_STDFS
    "${CMAKE_BINARY_DIR}/try"
    "${CMAKE_SOURCE_DIR}/cmake/has_stdfs.cc"
    CMAKE_FLAGS CMAKE_CXX_STANDARD=17 CMAKE_CXX_STANDARD_REQUIRED=ON
    )
if (RUNS_WITH_STDFS STREQUAL "FAILED_TO_RUN")
    message(STATUS "Using boost::filesystem instead of std::filesystem")
    set(_boost_components ${_boost_components} filesystem system)
    add_definitions(-DUSE_BOOST_FILESYSTEM)
else()
    message(STATUS "std::filesystem supported")
endif()

请注意,变量RUNS_WITH_STDFS在失败的情况下不会设置为NO,而是设置为"FAILED_TO_RUN",这不被解释为FALSE布尔值(参见CMake if() docs

if()如果常量为1、ON、YES、TRUE、Y或非零数字,则为真。如果常量为0、OFF、NO、FALSE、N、IGNORE、NOTFOUND、空字符串或以后缀-NOTFOUND结尾,则为假。

因此,我不得不对其值进行字符串比较。
与@Ashkan的解决方案相比,这个小测试程序也有所改变:
// == PROGRAM has_stdfs.cc ==

// Check if std::filesystem is available
// Source: https://dev59.com/cFQJ5IYBdhLWcg3wPDSP#54290906
// with modifications

#include <filesystem>
namespace fs = std::filesystem;

int main(int argc, char* argv[]) {
    fs::path somepath{ "dir1/dir2/filename.txt" };
    auto fname = somepath.filename();
    return 0;
}

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