7 votes

Problème d'ajout de std::filesystem au projet CMake

Je suis nouveau dans les projets CMake et je veux utiliser la bibliothèque du système de fichiers dans mon projet. Je travaille sous Ubuntu 18.04 avec GCC 8.2 et CMake 3.13. Pour y parvenir, j'ai essayé deux options :

Option 1

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

Cela ne sert à rien car le compilateur ne trouve toujours pas le système de fichiers. lors de la compilation.

Option 2 (copié de : 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()

Cela n'aide pas non plus car j'obtiens une erreur CMake que je n'ai pas. comprends pas.

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

Je ne me sens pas à la hauteur sur ce sujet et c'est pourquoi je suis venu ici. Je veux bien faire des efforts supplémentaires pour en savoir plus, mais je ne sais plus où chercher. Toute aide serait appréciée !

EDIT 1

Merci pour les réponses reçues jusqu'à présent ! J'ai fait Option 3 sur la base de vos commentaires :

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)

Malheureusement, cela ne résout pas mon problème. Il émet toujours une erreur lors de la compilation en indiquant qu'il ne trouve pas l'en-tête de compilation.

EDIT 2

Merci pour toutes les réponses apportées jusqu'à présent. Elles sont toutes utiles. J'ai essayé la réponse d'Ashkan en dernier (parce qu'elle semblait intimidante). Celle-ci renvoie

Le compilateur ne dispose pas des capacités du système de fichiers.

Je suppose donc qu'il y a un problème de ce côté-là. C'est utile dans le sens où je sais maintenant que ce n'est probablement pas dû à mon fichier CMake. Je dois maintenant trouver pourquoi le compilateur supporte l'en-tête du système de fichiers...

EDIT 3

Strictement parlant, la réponse à cette question est donnée car ma question concerne le fichier CMake. Je vais marquer la réponse d'Ashkan comme étant la solution simplement parce qu'elle a produit l'étape suivante dans ma recherche de dépannage. Si je pouvais, je marquerais aussi la réponse de lubgr parce que je pense que c'est aussi une très bonne réponse. Merci à tous !

9voto

lubgr Points 29224

Gcc 8.2. est livré avec <filesystem> Il n'est donc pas nécessaire d'enquêter sur la disponibilité. Ensuite, l'option 1 est suffisante, mais doit être corrigée :

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

add_executable(yourExecutable yourSourceFile.cpp)

target_link_libraries(yourExecutable stdc++fs)

Cela devrait permettre de compiler les sources avec -std=c++17 o -std=gnu++17 et en ajoutant -lstdc++fs lors de l'établissement des liens.

Edit : Notez que, comme @Ashkan l'a fait remarquer dans les commentaires, le fait de paramétrer CMAKE_CXX_STANDARD_REQUIRED a true entraîne une erreur immédiate au moment de la configuration si C++17 n'est pas pris en charge par le compilateur, au lieu d'une erreur de compilation (due à l'absence de l'élément <filesystem> ) ou au moment de la liaison (à cause de la bibliothèque partagée manquante). Cela peut être souhaitable.

7voto

Ashkan Points 485

En plus de la réponse de @lubgr. Je pense qu'une façon plus complète est de faire également try_compile pour voir si vous pouvez effectivement utiliser l'en-tête du système de fichiers. A mon avis, c'est mieux car certains compilateurs ne supportent pas encore std::filesystem. De plus, dans gcc 7.x, vous avez le système de fichiers sous experimental l'espace de noms. De cette façon, vous pouvez avoir un try_compile séparé dans l'espace de noms else et la détecter.

Voici le cmake correspondant

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

Le fichier tests/has_filesystem.cc est très simple

#include <filesystem>

namespace fs = std::filesystem;

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

    return 0;
}

Vous pourriez dans votre clause else try_compile pour boost::filesystem et passer une directive qui peut être utilisée dans votre fichier source où vous décidez si vous voulez utiliser c++17 filesystem ou boost.

2voto

Matthieu Brucher Points 18535

CHECK_CXX_SYMBOL_EXISTS prend trois arguments, et non deux :

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

Vous avez oublié d'indiquer à CMake où chercher les symboles (l'en-tête qui les déclare).

0voto

user465139 Points 727

J'ai trouvé un cas où try_compile n'était pas suffisant : en utilisant le compilateur Intel C++ ( icpc (ICC) 19.1.1.216 20200306 ) en mode C++17 sous MacOS "Mojave" 10.14.6. Le programme de test recommandé par @Ashkan s'est compilé sans erreur, et il s'est même exécuté. Cependant, mon code utilise fs::path::filename() à un moment donné, ce qui a donné lieu à une durée d'exécution erreur d'éditeur de liens ( dyld: lazy symbol binding failed: Symbol not found: ). En d'autres termes : l'en-tête est là, l'implémentation ne l'est apparemment pas ( ?). Je n'ai pas approfondi la question.

La solution consiste à utiliser try_run au lieu de try_compile et (dans mon cas) se rabattre sur boost::filesystem si std::filesystem n'est pas encore pris en charge.

Voici la section du code CMake correspondante :

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

Notez que la variable RUNS_WITH_STDFS n'est pas réglé sur NO en cas d'échec, mais à "FAILED_TO_RUN" qui n'est pas interprété comme un FALSE Booléen (voir CMake if() documents :

if() Vrai si la constante est 1, ON, YES, TRUE, Y, ou a nombre non nul. Faux si la constante est 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, la chaîne vide ou se termine par le suffixe -NOTFOUND.

J'ai donc dû comparer sa valeur avec une chaîne de caractères.

Le petit programme de test a également changé un peu par rapport à la solution de @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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X