我有一个依赖于 jsoncpp 的库,它是一个用 C++ 编写的 json 解析器。目前,jsoncpp 是稳定的并且不经常更新。它也已发布到公共(public)领域。现在,为了构建该库,依赖于 SCons 和 Python,它们可以工作,但对我的一些用户来说是一种烦恼。与其让他们下载 jsoncpp、SCons、Python,然后自己构建库,我可以直接将代码包含到我的项目中,然后一起构建所有内容。但是,这会导致一些问题。
主要是,如果我将 jsoncpp 代码包含到我的库中,那么我的库将包含 jsoncpp 符号。如果用户试图将我的库嵌入到已经依赖 jsoncpp 的库中,就会出现符号冲突。处理这个问题的正确方法是什么?例如,我可以分别编译我的库和 jsoncpp 并分发这两个库。如果用户已经有 jsoncpp,他们可以链接他们自己的版本。或者,我可以修改 jsoncpp 代码并将所有内容推送到新的命名空间中,但这看起来很麻烦。
如果有帮助,我会在 CMake 中构建所有内容,所以如果有 CMake 技巧来处理这个问题,那是最好的。
编辑
根据 Fraser 的建议,我有以下内容。
$ 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 到构建目录。安装库后,我们可以将可执行文件链接到它。由于 jsoncpp 不包含 CMakeLists.txt,我们使用补丁程序将适当的 CMake 脚本插入到 jsoncpp 源结构中。在更高级的构建脚本中,我们可以选择是否使用此构建过程或让用户直接指定库。
无论如何,也许其他人会发现这很有用。它简化了某些用户的build设置,但并未将所有符号捆绑到某个大型库中。
最佳答案
如果你想让所有用户满意(我知道 - 不可能!),你可以添加一个 option对于每个依赖项,例如选项(BuildJsonCpp)。
如果 option 为 ON,则构建依赖项,否则使用 find_library 和 find_path 包含它或 find_package。
为了构建依赖关系,而不是包含源代码,您可以考虑 ExternalProject CMake 模块。该模块用于下载、配置、构建和安装外部项目,并允许外部项目完全包含在您的构建树中,而不是您的源代码树中。
这将允许您在项目中只保留您自己的源文件,使其更小且更相关 - 特别是对于不希望构建依赖项的用户。
它会让您的 CMake 文件变得更复杂,更难维护,但我想如果您想让构建系统更灵活,这就是您必须付出的代价。
关于c++ - 将一个项目合并到另一个项目的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17821443/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer