2013-07-23 2 views
4

J'ai une bibliothèque qui dépend de jsoncpp, qui est un analyseur json écrit en C++. Pour l'instant, jsoncpp est stable et n'est pas mis à jour très souvent. Il a également été publié dans le domaine public. Maintenant, afin de construire la bibliothèque, il y a une dépendance sur SCons et Python, qui fonctionne, mais qui dérange certains de mes utilisateurs. Plutôt que de les télécharger jsoncpp, SCons, Python, puis de construire la bibliothèque eux-mêmes, je pourrais directement inclure le code dans mon projet et tout construire ensemble. Cependant, cela provoque des problèmes.Méthodes pour combiner un projet dans un autre

Principalement, si j'inclue le code jsoncpp dans ma bibliothèque, alors ma bibliothèque contient les symboles jsoncpp. Si un utilisateur essaie d'intégrer ma bibliothèque dans celle qui dépend déjà de jsoncpp, il y a un conflit de symbole. Quelle est la bonne façon de traiter ce problème? Par exemple, je pourrais compiler ma bibliothèque et jsoncpp séparément et distribuer les deux bibliothèques. Si l'utilisateur a déjà jsoncpp, ils peuvent lier leur propre version. Alternativement, je pourrais modifier le code jsoncpp et pousser tout dans un nouvel espace de nom, mais cela semble lourd.

Si ça aide, je construis tout dans CMake, donc s'il y a une astuce CMake pour gérer ça, c'est mieux.


EDIT

Basé sur la suggestion de Fraser, je donne les résultats suivants.

$ find . 
. 
./build 
./build/jsoncpp-src-0.6.0-rc2.tar.gz 
./src 
./src/cpp 
./src/cpp/hello.cpp 
./src/cpp/CMakeLists.txt 
./src/thirdparty 
./src/thirdparty/jsoncpp 
./src/thirdparty/jsoncpp/CMakeLists.txt 
./src/thirdparty/CMakeLists.txt 
./src/CMakeLists.txt 

$ cat ./src/cpp/hello.cpp 
#include <cstdlib> 
#include <iostream> 
#include <fstream> 
#include <iomanip> 
#include "json/json.h" 

// Parses a JSON file and returns the root 
void parse(const std::string& fname,Json::Value& root) { 
    // Read in the input file 
    Json::Reader reader; 
    std::ifstream file(fname.c_str(),std::ifstream::in); 
    bool parsingSuccessful = reader.parse(file, root, true); 
    if (!parsingSuccessful) { 
     std::cerr << "Failed to parse the optimization parameter " 
      "file: " << reader.getFormattedErrorMessages() << std::endl; 
     exit(EXIT_FAILURE); 
    } 

    // Close everything out 
    file.close(); 
} 


int main(int argc,char* argv[]) { 
    // Make sure we have the correct number of arguments 
    if(argc!=2) { 
     std::cout << "hello <json>" << std::endl; 
     return EXIT_FAILURE; 
    } 

    // Parse the JSON files 
    Json::Value root; 
    parse(argv[1],root); 

    // Get the hello string 
    std::string hello = root["Hello"].get("Message","Hello World!").asString(); 

    // Tell everyone 
    std::cout << hello << std::endl; 

    return EXIT_SUCCESS; 
} 

$ cat ./src/cpp/CMakeLists.txt 
project(hello) 
cmake_minimum_required(VERSION 2.8.9) 
enable_language(CXX) 

# Set the location of jsoncpp 
find_library(JSONCPP_LIBRARY 
    NAMES json libjson 
    PATHS ${CMAKE_BINARY_DIR}/installed/lib) 
set(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY}) 
set(JSONCPP_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/installed/include) 

# Locate the headers 
include_directories(${JSONCPP_INCLUDE_DIRS}) 

# Build the executable 
add_executable(hello hello.cpp) 

# Link jsoncpp 
target_link_libraries(hello ${JSONCPP_LIBRARIES}) 

$ cat ./src/thirdparty/jsoncpp/CMakeLists.txt 
project(jsoncpp) 
cmake_minimum_required(VERSION 2.8.9) 
enable_language(CXX) 

# Set the source file prefix 
set(source_prefix ${CMAKE_CURRENT_SOURCE_DIR}/src/lib_json/) 

# Save the include directory 
set(JSONCPP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIRI}/include) 

# Grab all the sources for the library 
set(jsoncpp_srcs 
    "${source_prefix}/json_reader.cpp" 
    "${source_prefix}/json_value.cpp" 
    "${source_prefix}/json_writer.cpp" 
) 

# Locate the headers 
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 

# Compile everything 
set(CMAKE_POSITION_INDEPENDENT_CODE ON) 
add_library(jsoncpp_object OBJECT ${jsoncpp_srcs}) 
add_library(jsoncpp_static STATIC $<TARGET_OBJECTS:jsoncpp_object>) 
add_library(jsoncpp_shared SHARED $<TARGET_OBJECTS:jsoncpp_object>) 
set_target_properties(jsoncpp_shared jsoncpp_static 
    PROPERTIES OUTPUT_NAME json) 

# Install the libraries and headers 
install(TARGETS jsoncpp_static jsoncpp_shared DESTINATION lib) 
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/json DESTINATION include) 

$ cat ./src/thirdparty/CMakeLists.txt 
project(third_party_libraries) 
cmake_minimum_required(VERSION 2.8.9) 

# Build jsoncpp 
include(ExternalProject) 
ExternalProject_Add(
    JsonCpp 
    URL ${CMAKE_BINARY_DIR}/jsoncpp-src-0.6.0-rc2.tar.gz 
    URL_MD5 363e2f4cbd3aeb63bf4e571f377400fb 
    PATCH_COMMAND ${CMAKE_COMMAND} -E copy 
     "${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/CMakeLists.txt" 
     CMakeLists.txt 
    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}/installed 
) 

$ cat ./src/CMakeLists.txt 
project(hello_example) 
cmake_minimum_required(VERSION 2.8.9) 

# First, build our TPLs 
add_subdirectory(thirdparty) 

# Then, build our target 
add_subdirectory(cpp) 

Fondamentalement, nous utilisons le script ExternalProject de CMake pour compiler et installer jsoncpp localement dans le répertoire de construction. Une fois la bibliothèque installée, nous pouvons lier nos exécutables. Puisque jsoncpp ne contient pas de CMakeLists.txt, nous utilisons la procédure de correction pour insérer un script CMake approprié dans la structure source jsoncpp. Dans un script de construction plus sophistiqué, nous pouvons choisir d'utiliser ou non cette procédure de construction ou de spécifier directement la bibliothèque à l'utilisateur.

Dans tous les cas, peut-être que quelqu'un d'autre le trouvera utile. Il simplifie la configuration de la construction pour certains utilisateurs, mais ne regroupe pas tous les symboles dans une méga bibliothèque.

Répondre

2

Si vous voulez essayer et garder tous les utilisateurs heureux (je sais - pas possible!), Vous pouvez ajouter un option pour chaque dépendance, par ex. option(BuildJsonCpp).

Si le option est ON, vous construisez la dépendance, sinon vous incluez à l'aide find_library et find_path ou find_package.

Pour construire les dépendances, plutôt que d'inclure les sources, vous pouvez envisager le module CMake ExternalProject.Ce module est utilisé pour télécharger, configurer, construire et installer des projets externes, et permet au projet externe d'être contenu entièrement dans votre arbre de construction - pas votre arborescence source. Cela vous permettra de conserver uniquement vos propres fichiers source dans votre projet, ce qui le rendra plus petit et plus pertinent - en particulier pour les utilisateurs ne souhaitant pas créer les dépendances.

Cela rendra vos fichiers CMake un peu plus complexes et plus difficiles à maintenir, mais je suppose que c'est le prix à payer si vous voulez rendre le système de construction plus flexible.

Questions connexes