将一个项目合并到另一个项目的方法

时间:2013-07-23 21:31:12

标签: c++ cmake

我有一个依赖于jsoncpp的库,这是一个用C ++编写的json解析器。目前,jsoncpp稳定且不经常更新。它也已被释放到公共领域。现在,为了构建库,依赖于SCons和Python,这是有效的,但对我的一些用户来说是一个烦恼。而不是让他们下载jsoncpp,SCons,Python,然后自己构建库,我可以直接将代码包含到我的项目中并将所有内容组合在一起。但是,这会导致一些问题。

首先,如果我将jsoncpp代码包含到我的库中,那么我的库包含jsoncpp符号。如果用户试图将我的库嵌入到已经依赖于jsoncpp的库中,则会出现符号冲突。处理这个问题的正确方法是什么?例如,我可以单独编译我​​的库和jsoncpp并分发这两个库。如果用户已经拥有jsoncpp,他们可以链接自己的版本。或者,我可以修改jsoncpp代码并将所有内容推送到新的命名空间,但这看起来很麻烦。

如果有帮助,我会在CMake中构建所有内容,所以如果有一个CMake技巧来处理这个问题,那就最好了。


修改

根据弗雷泽的建议,我有以下内容。

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

基本上,我们使用CMake的ExternalProject脚本,以便在本地编译和安装jsoncpp到build目录。一旦安装了库,我们就可以将可执行文件链接到它。由于jsoncpp不包含CMakeLists.txt,因此我们使用补丁程序将适当的CMake脚本插入到jsoncpp源结构中。在更高级的构建脚本中,我们可以选择是否使用此构建过程或让用户直接指定库。

无论如何,也许其他人会觉得这很有用。它简化了一些用户的构建设置,但没有将所有符号捆绑到一些大型库中。

1 个答案:

答案 0 :(得分:2)

如果您想尝试让所有用户满意(我知道 - 不可能!),您可以为每个依赖项添加option,例如: option(BuildJsonCpp)

如果optionON,则构建依赖关系,否则您使用find_libraryfind_pathfind_package将其包含在内。

要构建依赖项,而不是包含源,您可以考虑ExternalProject CMake模块。此模块用于下载,配置,构建和安装外部项目,并允许外部项目完全包含在构建树中 - 而不是源树。

这将允许您在项目中保留自己的源文件,使其更小,更相关 - 尤其是对于不希望构建依赖项的用户。

它会使你的CMake文件更复杂,更难维护,但我想如果你想让构建系统更灵活,你必须付出代价。