CMake:将 add_library 与许多源一起使用并在 target_link_libraries 中使用时出现链接错误
CMake: Linking error when using add_library with many sources and using it in target_link_libraries
完整列表在最后,这里是具体部分:
add_library(common common.h utils.h utils.cc)
和
add_executable(type_test type_test.cc)
target_link_libraries(type_test
${GTEST_BOTH_LIBRARIES}
ast
common
compilation_context
easyloggingpp
functions
lexer
parser
${CMAKE_THREAD_LIBS_INIT})
add_test(type_test COMMAND out/type_test)
现在当我 运行 cmake . --GNinja
和以后 ninja type_test
时,我得到:
[0/1] Re-running CMake...
-- Found LLVM 6.0.1
-- Using LLVMConfig.cmake in: /usr/lib/llvm-6.0/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /home/gru/Code/schwifty
[1/1] Linking CXX executable out/type_test
FAILED: out/type_test
: && /usr/bin/clang++ -DELPP_FEATURE_CRASH_LOG CMakeFiles/type_test.dir/type_test.cc.o -o out/type_test /usr/local/lib/libgtest.a /usr/local/lib/libgtest_main.a out/libast.a out/libcommon.a out/libcompilation_context.a out/libeasyloggingpp.a out/libfunctions.a out/liblexer.a out/libparser.a -lpthread && :
/usr/bin/ld: out/libcompilation_context.a(type.cc.o): in function `schwifty::Types::parse_type_string_internal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
type.cc:(.text+0x18cb): undefined reference to `schwifty::utils::startswith(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
/usr/bin/ld: type.cc:(.text+0x1c04): undefined reference to `schwifty::utils::startswith(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.
schwifty::utils::startswith
肯定是在utils.h
中声明的,在utils.cc
中实现的。
此处提供完整清单:
cmake_minimum_required(VERSION 3.10)
project(schwifty)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DELPP_FEATURE_CRASH_LOG")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out)
find_package(PythonInterp 3.6 REQUIRED)
file(MAKE_DIRECTORY downloads external)
#
# Easylogging++
#
if (EXISTS "external/easyloggingpp")
else ()
file(MAKE_DIRECTORY external/easyloggingpp)
file(DOWNLOAD https://github.com/muflihun/easyloggingpp/archive/v9.96.4.zip
downloads/easyloggingpp.zip)
execute_process(COMMAND unzip downloads/easyloggingpp.zip -d downloads)
file(GLOB easyloggingpp_files
downloads/easyloggingpp-9.96.4/src/easylogging++.*)
file(COPY ${easyloggingpp_files} DESTINATION external/easyloggingpp)
endif ()
include_directories(external/easyloggingpp)
add_library(easyloggingpp external/easyloggingpp/easylogging++.cc)
#
# Local lib targets
#
add_library(ast ast.h ast.cc)
add_library(ast_compare_visitor ast_compare_visitor.h ast_compare_visitor.cc)
add_library(classes classes.h classes.cc)
add_library(codegen
codegen.h
codegen.cc
codegen_common.h
codegen_common.cc
expression_type_visitor.cc
expression_type_visitor.h)
add_library(common common.h utils.h utils.cc)
add_library(compilation_context
compilation_context.h
compilation_context.cc
enum.h
enum.cc
errors.h
errors.cc
operators.h
operators.cc
type.h
type.cc)
add_library(functions functions.h functions.cc)
add_library(jit jit.cc jit.h)
add_library(lexer lexer.cc lexer.h lexer_common.cc lexer_common.h)
add_library(parser parser.h parser.cc)
add_library(runtime runtime.cc runtime.h)
add_library(type_inference
type_inference.h
type_inference.cc
symbol_visitor.cc
symbol_visitor.h
type_inference_visitor.cc
type_inference_visitor.h)
#
# External lib targets
#
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
llvm_map_components_to_libnames(llvm_libs all)
find_package(FMT REQUIRED CONFIG)
#
# Schwifty main executable
#
add_executable(schwifty schwifty.cc)
target_link_libraries(schwifty
${llvm_libs}
ast
classes
codegen
common
compilation_context
easyloggingpp
fmt::fmt
functions
lexer
parser
runtime
type_inference)
#
# Testing
#
enable_testing()
find_package(GTest REQUIRED)
find_package(Threads)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(codegen_test codegen_test.cc)
target_link_libraries(codegen_test
${GTEST_BOTH_LIBRARIES}
${llvm_libs}
easyloggingpp
ast
classes
codegen
common
compilation_context
fmt::fmt
functions
jit
lexer
parser
runtime
type_inference
${CMAKE_THREAD_LIBS_INIT})
add_test(codegen_test COMMAND out/codegen_test)
add_executable(lexer_test lexer_test.cc)
target_link_libraries(lexer_test
${GTEST_BOTH_LIBRARIES}
ast
common
compilation_context
easyloggingpp
functions
lexer
parser
fmt::fmt
${CMAKE_THREAD_LIBS_INIT})
add_test(lexer_test COMMAND out/lexer_test)
add_executable(parser_test parser_test.cc)
target_link_libraries(parser_test
${GTEST_BOTH_LIBRARIES}
ast
ast_compare_visitor
compilation_context
common
easyloggingpp
functions
lexer
parser
fmt::fmt
${CMAKE_THREAD_LIBS_INIT})
add_test(parser_test COMMAND out/parser_test)
add_executable(type_test type_test.cc)
target_link_libraries(type_test
${GTEST_BOTH_LIBRARIES}
ast
common
compilation_context
easyloggingpp
functions
lexer
parser
${CMAKE_THREAD_LIBS_INIT})
add_test(type_test COMMAND out/type_test)
add_executable(type_inference_test type_inference_test.cc)
target_link_libraries(type_inference_test
${GTEST_BOTH_LIBRARIES}
easyloggingpp
ast
classes
common
compilation_context
functions
fmt::fmt
lexer
parser
runtime
type_inference
${CMAKE_THREAD_LIBS_INIT})
add_test(type_inference_test COMMAND ./out/type_inference_test)
add_test(NAME end_to_end_tests
WORKING_DIRECTORY ${CTEST_SOURCE_DIRECTORY}
COMMAND ${PYTHON_EXECUTABLE} end_to_end_tests.py)
此处提供所有代码:https://bitbucket.org/gruszczy/schwifty/src/default/
使用 bfd ld 的静态库的链接顺序很重要。 (不确定黄金)。 libcommon.a 在 libcompilation_context.a
出现之前被读取、处理和删除,这需要在 libcommon.a
中声明的符号。
解决这个问题的老办法是 ld c.o -lcommon -lcompilation_context -lcommon
。 cmake
的 LINK_INTERFACE_MULTIPLICITY
应该(我认为)可以解决您的问题。
您似乎没有遵循现代 CMake 最佳实践。您的库不会 link 任何东西,只有您的可执行文件可以。手动编译时,这就是它的做法,因为静态库不会 link 到其他静态库,只有可执行文件才可以。然而,在 CMake 中,世界有点不同。
你应该总是link你使用的库,只你直接使用的,不是你间接使用。对于可执行文件和库目标。 CMake 中的目标形成库和可执行文件的层次结构。 CMake 会相应地跟踪需要什么和 links。例如,如果您的可执行文件 exe
需要库 liba
,而库 liba
本身需要库 libb
,您不应该 link 您的可执行文件同时用于 liba
和 libb
,而是 link liba
到 libb
,并且只有 link 你的可执行文件到 liba
。 CMake 将完成剩下的工作并解析层次结构以形成正确的 linking 命令。
完整列表在最后,这里是具体部分:
add_library(common common.h utils.h utils.cc)
和
add_executable(type_test type_test.cc)
target_link_libraries(type_test
${GTEST_BOTH_LIBRARIES}
ast
common
compilation_context
easyloggingpp
functions
lexer
parser
${CMAKE_THREAD_LIBS_INIT})
add_test(type_test COMMAND out/type_test)
现在当我 运行 cmake . --GNinja
和以后 ninja type_test
时,我得到:
[0/1] Re-running CMake...
-- Found LLVM 6.0.1
-- Using LLVMConfig.cmake in: /usr/lib/llvm-6.0/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /home/gru/Code/schwifty
[1/1] Linking CXX executable out/type_test
FAILED: out/type_test
: && /usr/bin/clang++ -DELPP_FEATURE_CRASH_LOG CMakeFiles/type_test.dir/type_test.cc.o -o out/type_test /usr/local/lib/libgtest.a /usr/local/lib/libgtest_main.a out/libast.a out/libcommon.a out/libcompilation_context.a out/libeasyloggingpp.a out/libfunctions.a out/liblexer.a out/libparser.a -lpthread && :
/usr/bin/ld: out/libcompilation_context.a(type.cc.o): in function `schwifty::Types::parse_type_string_internal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
type.cc:(.text+0x18cb): undefined reference to `schwifty::utils::startswith(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
/usr/bin/ld: type.cc:(.text+0x1c04): undefined reference to `schwifty::utils::startswith(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.
schwifty::utils::startswith
肯定是在utils.h
中声明的,在utils.cc
中实现的。
此处提供完整清单:
cmake_minimum_required(VERSION 3.10)
project(schwifty)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DELPP_FEATURE_CRASH_LOG")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out)
find_package(PythonInterp 3.6 REQUIRED)
file(MAKE_DIRECTORY downloads external)
#
# Easylogging++
#
if (EXISTS "external/easyloggingpp")
else ()
file(MAKE_DIRECTORY external/easyloggingpp)
file(DOWNLOAD https://github.com/muflihun/easyloggingpp/archive/v9.96.4.zip
downloads/easyloggingpp.zip)
execute_process(COMMAND unzip downloads/easyloggingpp.zip -d downloads)
file(GLOB easyloggingpp_files
downloads/easyloggingpp-9.96.4/src/easylogging++.*)
file(COPY ${easyloggingpp_files} DESTINATION external/easyloggingpp)
endif ()
include_directories(external/easyloggingpp)
add_library(easyloggingpp external/easyloggingpp/easylogging++.cc)
#
# Local lib targets
#
add_library(ast ast.h ast.cc)
add_library(ast_compare_visitor ast_compare_visitor.h ast_compare_visitor.cc)
add_library(classes classes.h classes.cc)
add_library(codegen
codegen.h
codegen.cc
codegen_common.h
codegen_common.cc
expression_type_visitor.cc
expression_type_visitor.h)
add_library(common common.h utils.h utils.cc)
add_library(compilation_context
compilation_context.h
compilation_context.cc
enum.h
enum.cc
errors.h
errors.cc
operators.h
operators.cc
type.h
type.cc)
add_library(functions functions.h functions.cc)
add_library(jit jit.cc jit.h)
add_library(lexer lexer.cc lexer.h lexer_common.cc lexer_common.h)
add_library(parser parser.h parser.cc)
add_library(runtime runtime.cc runtime.h)
add_library(type_inference
type_inference.h
type_inference.cc
symbol_visitor.cc
symbol_visitor.h
type_inference_visitor.cc
type_inference_visitor.h)
#
# External lib targets
#
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
llvm_map_components_to_libnames(llvm_libs all)
find_package(FMT REQUIRED CONFIG)
#
# Schwifty main executable
#
add_executable(schwifty schwifty.cc)
target_link_libraries(schwifty
${llvm_libs}
ast
classes
codegen
common
compilation_context
easyloggingpp
fmt::fmt
functions
lexer
parser
runtime
type_inference)
#
# Testing
#
enable_testing()
find_package(GTest REQUIRED)
find_package(Threads)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(codegen_test codegen_test.cc)
target_link_libraries(codegen_test
${GTEST_BOTH_LIBRARIES}
${llvm_libs}
easyloggingpp
ast
classes
codegen
common
compilation_context
fmt::fmt
functions
jit
lexer
parser
runtime
type_inference
${CMAKE_THREAD_LIBS_INIT})
add_test(codegen_test COMMAND out/codegen_test)
add_executable(lexer_test lexer_test.cc)
target_link_libraries(lexer_test
${GTEST_BOTH_LIBRARIES}
ast
common
compilation_context
easyloggingpp
functions
lexer
parser
fmt::fmt
${CMAKE_THREAD_LIBS_INIT})
add_test(lexer_test COMMAND out/lexer_test)
add_executable(parser_test parser_test.cc)
target_link_libraries(parser_test
${GTEST_BOTH_LIBRARIES}
ast
ast_compare_visitor
compilation_context
common
easyloggingpp
functions
lexer
parser
fmt::fmt
${CMAKE_THREAD_LIBS_INIT})
add_test(parser_test COMMAND out/parser_test)
add_executable(type_test type_test.cc)
target_link_libraries(type_test
${GTEST_BOTH_LIBRARIES}
ast
common
compilation_context
easyloggingpp
functions
lexer
parser
${CMAKE_THREAD_LIBS_INIT})
add_test(type_test COMMAND out/type_test)
add_executable(type_inference_test type_inference_test.cc)
target_link_libraries(type_inference_test
${GTEST_BOTH_LIBRARIES}
easyloggingpp
ast
classes
common
compilation_context
functions
fmt::fmt
lexer
parser
runtime
type_inference
${CMAKE_THREAD_LIBS_INIT})
add_test(type_inference_test COMMAND ./out/type_inference_test)
add_test(NAME end_to_end_tests
WORKING_DIRECTORY ${CTEST_SOURCE_DIRECTORY}
COMMAND ${PYTHON_EXECUTABLE} end_to_end_tests.py)
此处提供所有代码:https://bitbucket.org/gruszczy/schwifty/src/default/
使用 bfd ld 的静态库的链接顺序很重要。 (不确定黄金)。 libcommon.a 在 libcompilation_context.a
出现之前被读取、处理和删除,这需要在 libcommon.a
中声明的符号。
解决这个问题的老办法是 ld c.o -lcommon -lcompilation_context -lcommon
。 cmake
的 LINK_INTERFACE_MULTIPLICITY
应该(我认为)可以解决您的问题。
您似乎没有遵循现代 CMake 最佳实践。您的库不会 link 任何东西,只有您的可执行文件可以。手动编译时,这就是它的做法,因为静态库不会 link 到其他静态库,只有可执行文件才可以。然而,在 CMake 中,世界有点不同。
你应该总是link你使用的库,只你直接使用的,不是你间接使用。对于可执行文件和库目标。 CMake 中的目标形成库和可执行文件的层次结构。 CMake 会相应地跟踪需要什么和 links。例如,如果您的可执行文件 exe
需要库 liba
,而库 liba
本身需要库 libb
,您不应该 link 您的可执行文件同时用于 liba
和 libb
,而是 link liba
到 libb
,并且只有 link 你的可执行文件到 liba
。 CMake 将完成剩下的工作并解析层次结构以形成正确的 linking 命令。