Problem adding std::filesystem to CMake Project

后端 未结 4 597
余生分开走
余生分开走 2020-12-19 09:29

I am new to CMake projects and I want to use the file system library in my project. I am running Ubuntu 18.04 with GCC 8.2 and CMake 3.13. In order to achieve this I tried t

相关标签:
4条回答
  • 2020-12-19 10:01

    I have found a case when try_compile was not enough: when using the Intel C++ compiler (icpc (ICC) 19.1.1.216 20200306) in C++17 mode running on MacOS "Mojave" 10.14.6. The test program recommended by @Ashkan compiled without errors, and it even ran. However, my code uses fs::path::filename() at one point and that resulted in a runtime linker error (dyld: lazy symbol binding failed: Symbol not found:). In other words: the header is there, the implementation apparently isn't (?). I didn't investigate this any further.

    The solution is to use try_run instead of try_compile and (in my case) fall back to boost::filesystem if std::filesystem is not yet supported.

    Here is the relevant CMake code section:

    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()
    

    Note that the variable RUNS_WITH_STDFS is not set to NO in case of failure but to "FAILED_TO_RUN" which is not interpreted as a FALSE Boolean (see CMake if() docs:

    if() True if the constant is 1, ON, YES, TRUE, Y, or a non-zero number. False if the constant is 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, the empty string, or ends in the suffix -NOTFOUND.

    so I had to string-compare its value.

    The little test program also changed a bit compared to @Ashkan's solution:

    // == 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;
    }
    
    0 讨论(0)
  • 2020-12-19 10:17

    Gcc 8.2. comes with <filesystem>, so there is no need to investigate with regard to the availability. Next, option 1 is sufficient, but needs a fix:

    set(CMAKE_CXX_STANDARD 17) # no need to manually adjust the CXXFLAGS
    
    add_executable(yourExecutable yourSourceFile.cpp)
    
    target_link_libraries(yourExecutable stdc++fs)
    

    This should result in compiling the sources with -std=c++17 or -std=gnu++17 and adding -lstdc++fs when linking.

    Edit: Note that as @Ashkan has pointed out in the comments, setting CMAKE_CXX_STANDARD_REQUIRED to true results in an immediate error at configure time if C++17 isn't supported by the compiler, instead of a compilation error (due to the missing <filesystem> header) or at link time (due to the missing shared library). This might be desirable.

    0 讨论(0)
  • 2020-12-19 10:19

    Besides from @lubgr's answer. I think a more complete way is to also do try_compile to see that you can actually use the filesystem header. This in my opinion is better because some compilers are not supporting std::filesystem yet. Also in gcc 7.x you have the filesystem under experimental namespace. This way you can have a separate try_compile in the else clause and detect that.

    Here is the related cmake for it

    # 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)
    

    The file tests/has_filesystem.cc is very simple

    #include <filesystem>
    
    namespace fs = std::filesystem;
    
    int main()
    {
        fs::path aPath {"../"};
    
        return 0;
    }
    

    You could in your else clause try_compile for boost::filesystem and pass a directive that can be used in your source file where you decide if you want to use c++17 filesystem or boost.

    0 讨论(0)
  • 2020-12-19 10:21

    CHECK_CXX_SYMBOL_EXISTS takes three arguments, not two:

    CHECK_CXX_SYMBOL_EXISTS(std::filesystem::path::preferred_separator filesystem cxx17fs)
    

    You forgot to tell CMake where to look for the symbols (the header that declares them).

    0 讨论(0)
提交回复
热议问题