将std :: filesystem添加到CMake项目时出现问题

时间:2019-01-21 12:43:16

标签: c++ cmake

我是CMake项目的新手,我想在项目中使用文件系统库。我正在使用GCC 8.2和CMake 3.13运行Ubuntu 18.04。为了实现这一点,我尝试了两种选择:

选项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)

遗憾的是,它不能解决我的问题。仍然在编译过程中发出错误,提示找不到编译头。

编辑2

感谢到目前为止的所有回复。所有这些帮助。我上次尝试了Ashkan的回答(因为它似乎令人生畏)。这个返回

  

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

所以我猜想那是错误的。从我现在知道可能不是由于我的CMake文件的意义上来说,这很有用。我现在必须找出为什么编译器确实支持文件系统头文件...

编辑3

严格来说,回答此问题是因为我的问题与CMake文件有关。我将把Ashkan的答案标记为解决方案,只是因为它产生了我的故障排除搜索的下一步。如果可以的话,我也将他的答案标记为lubgr,因为我认为这也是一个很好的答案。谢谢大家!

4 个答案:

答案 0 :(得分:5)

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在注释中指出的那样,如果编译器不支持C ++ 17,则将CMAKE_CXX_STANDARD_REQUIRED设置为true会在配置时立即导致错误,而不是编译错误(由于缺少<filesystem>标头)或链接时(由于缺少共享库)。这可能是理想的。

答案 1 :(得分:3)

除了@lubgr的答案。我认为更完整的方法是也进行try_compile来查看您是否可以实际使用文件系统标头。我认为这更好,因为某些编译器尚不支持std :: filesystem。同样在gcc 7.x中,文件系统位于experimental命名空间下。这样,您可以在else子句中使用单独的try_compile并进行检测。

这是相关的命令

# 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子句try_compile中使用boost :: filesystem并传递一个可在源文件中使用的指令,在此您可以决定要使用c ++ 17文件系统还是boost。

答案 2 :(得分:1)

profile(creation_date)接受三个参数,而不是两个:

CHECK_CXX_SYMBOL_EXISTS

您忘记告诉CMake在哪里寻找符号(声明它们的标头)。

答案 3 :(得分:0)

我发现try_compile不够用的情况:在MacOS“ Mojave” 10.14.6上以C ++ 17模式使用Intel C ++编译器(icpc (ICC) 19.1.1.216 20200306)时。 @Ashkan推荐的测试程序已编译,没有错误,并且甚至运行了。但是,我的代码在某一时刻使用了fs::path::filename(),这导致了运行时链接器错误(dyld: lazy symbol binding failed: Symbol not found:)。换句话说:标头在那儿,实现显然不是(?)。我没有对此进行进一步的调查。

解决方案是使用try_run代替try_compile,并且(在我的情况下)如果尚未支持boost::filesystem,则退回到std::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布尔值(请参阅{{3} }:

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

所以我不得不对它的值进行字符串比较。

与@Ashkan的解决方案相比,该小测试程序也有所变化:

// == PROGRAM has_stdfs.cc ==

// Check if std::filesystem is available
// Source: https://stackoverflow.com/a/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;
}