I am using Windows 10 and CMake to build my project (using MinGW Makefiles as the generator), while choosing Clang/Clang++ (LLVM) as my compiler due to my Language Server Protocol also being for Clang.
However, when trying to link a static library I had created to my main executable, none of the definitions in the translation units were able to be resolved.
For a simple recreation:
File system
+ root/
+ main.cpp
+ foobar.cpp
+ foobar.hpp
+ CMakeLists.txt
// main.cpp
#include <iostream>
#include "foobar.hpp"
int main( int argc, char** argv ) {
foo();
std::cin.get();
return 0;
}
// foobar.cpp
#include "foobar.hpp"
void foo() {
std::cout << "bar" << std::endl;
}
// foobar.hpp
#pragma once
#ifndef FOOBAR_HEADER_H
#define FOOBAR_HEADER_H
#include <iostream>
void foo();
#endif
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(linker_fail
VERSION 1.0.0
LANGUAGES C CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_library(foobar
foobar.cpp
foobar.hpp)
add_executable(${PROJECT_NAME}
main.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC
foobar)
Then for building:
root> cmake -S . -B bin -G "MinGW Makefiles"
root> cmake --build bin
lld-link: error: undefined symbol: void __cdecl foo(void)
Does anyone know how to fix this? Should I have passed some arguments to the compiler/linker? Or does Clang just not allow static linking?
Related
I want to build a simple application based on a .cpp and a .h file.
I couldn't make my project work so i started from a basic example but didn't succeed as i'm just starting creating project in Linux. From what i've seen, my CMakeLists should be like this :
My CMakeLists.txt :
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 11)
project(test C CXX)
add_executable(${PROJECT_NAME} main_new.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR})
My main_new.cpp :
#include <read.h>
int main(int argc, char* argv[])
{
int a, b, c, d, e = 0;
std::cout << "hello im example " << std::endl;
read_int(&a, &b, &c, &d, &e);*
return 0;
}
My read.h :
#ifdef __cplusplus
extern "C" {
#endif
int read_int(int *vectorA, int *vectorB, int *matrixA, int *matrixB, int *flags);
#ifdef __cplusplus
}
#endif
My .cpp and .h file are in the same folder. cmake . is giving me no error, but after using make i get
main_new.cpp:(.text+0x68) : undefined reference to « read_int »
I'm using the makefile created by the cmake command. Should i create a custom makefile ?
Edit : Added question :
I also have to implement .so files, but doing target_link_libraries(test ${CMAKE_SOURCE_DIR}/libA.so ${CMAKE_SOURCE_DIR}/libB.so) in my CMakeLists.txt file doesn't work. How can i link these libraries to my executable ?
My problem right now is that when using CLion and using the following cmake file:
cmake_minimum_required(VERSION 3.0.0)
project(my_library)
# C++ version
set(CMAKE_CXX_STANDARD 17)
# Create Library
add_library(
my_library
public/testlib/config.h
public/testlib/config.cpp
)
target_include_directories(
my_library
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/public
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/private
)
# Make Test File
add_executable(lib_test testing/main.cpp)
target_include_directories(lib_test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/public)
target_link_libraries(lib_test PUBLIC my_library)
It compiles perfectly fine using visual studio with clang++ and cmake, and the lib_test.exe works! Unfortunately when I do this in CLion (clang++ and cmake) I get multiple 'lld-link: error: undefined symbol: public: __cdecl ' errors for every time I use the library in the main.cpp file. I was able to fix this by specifying the library to be an OBJECT library like this:
add_library(
my_library
OBJECT
public/testlib/config.h
public/testlib/config.cpp
)
And then it works perfectly fine in CLion.
My question is why? Does visual studio automatically do this? Or is it something else?
Thank you very much in advance :)
EDIT:
I'd like to export a statically linked library.
config.h File:
#pragma once
namespace TestLib
{
struct Config
{
const char* name;
int width;
int height;
Config();
};
}
config.cpp File:
#include <testlib/config.h>
using namespace TestLib;
Config::Config()
{
this->name = nullptr;
this->width = 0;
this->height = 0;
}
main.cpp File:
#include <testlib/config.h>
using namespace TestLib;
int main(int argc, char** argv)
{
Config config;
config.name = "haha yes";
config.width = 1280;
config.height = 720;
return 0;
}
Error Message:
lld-link: error: undefined symbol: public: __cdecl TestLib::Config::Config(void)
>>> referenced by D:\testlib\testing\main.cpp:7
>>> CMakeFiles/lib_test.dir/testing/main.cpp.obj:(main)
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
I'm trying to integrate a normal compilation and a unit-test with gtest into one cmake file, but I don't know how to achieve this. Here is my project:
|---include
| |---Student.h
|
|---src
| |---Student.cpp
| |---main.cpp
|
|---unittest
| |---TestStudent.cpp
|
|---CMakeLists.txt # how to write this file?
So, Student.h, Student.cpp and main.cpp is the source code, TestStudent.cpp is the test code, which includes gtest/gtest.h and a main function, here it is:
#include "gtest/gtest.h"
#include "Student.h"
class TestStudent : public ::testing::Test
{
protected:
Student *ps;
void SetUp() override
{
ps = new Student(2, "toto");
}
void TearDown() override
{
delete ps;
}
};
TEST_F(TestStudent, ID)
{
EXPECT_TRUE(ps->GetID() == 2);
EXPECT_TRUE(ps->GetName() == "toto");
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Now, if I want to compile the source code, I need to run g++ -std=c++11 Student.cpp main.cpp -o a.out, whereas if I want to compile the test code, I need to run g++ -std=c++11 TestStudent.cpp Student.cpp -lgtest -lpthread -o test.out.
So, how could I write the CMakeLists.txt to allow me to compile the different targets, such as cmake NORMAL and cmake TEST?
As already pointed out in the comments use multiple targets via add_executable. The following CMakeLists.txt will produce two targets generating the executables student and student-test.
The last three lines can be omitted if you don't care about CTest.
cmake_minimum_required(VERSION 3.9)
project(Student CXX)
set(CMAKE_CXX_STANDARD 11)
set(SOURCES src/Student.cpp)
set(INCLUDES include)
find_package(GTest REQUIRED)
add_executable(student src/main.cpp ${SOURCES})
target_include_directories(student PRIVATE ${INCLUDES})
add_executable(student-test unittest/TestStudent.cpp ${SOURCES})
target_include_directories(student-test PRIVATE ${INCLUDES})
target_link_libraries(student-test GTest::GTest GTest::Main)
enable_testing()
include(GoogleTest)
gtest_add_tests(TARGET student-test AUTO)
Here is the cmake file that i am using
cmake_minimum_required (VERSION 3.0)
project (midasd)
set (midas VERSION_MAJOR 0)
set (midas VERSION_MINOR 0)
set (midas VERSION_REVISION 1)
find_library(libconfig libconfig)
add_executable(midasd src/main.cpp)
target_link_libraries(midasd "${libconfig_LIBS}")
The problem i am facing is undefined reference for config_init. The main function is as follows
#include <libconfig.h>
int main(int argc, char *argv[])
{
midas::midasCtx *container = new midas::midasCtx(argc,argv);
config_t cfg;
config_init(&cfg);
return 0;
}
Where am i going wrong with CMAKE ?
Actually the libconfig is recognized as simply -lconfig not -llibconfigin linking argument. The CMakeLists.txt should contain
target_link_libraries(my_project config)
Source
This manual(https://hyperrealm.github.io/libconfig/libconfig_manual.html) says " To link with the library, specify ‘-lconfig++’ as an argument to the linker. "
So I fixed like following code and build was completed.
target_link_libraries(my_project config++)
This my project structure:
main
|
--src
|
--feed.h
--feed.cc
--other files
--CMakeLists2.txt
--test
|
--test.cpp
--CMakeLists3.txt
CMakeLists1.txt
CMakeLists1.txt
cmake_minimum_required (VERSION 2.8.11)
project (Feedparser)
set(CMAKE_MODULE_PATH cmake)
find_package(PTHREAD REQUIRED)
find_package(CURL REQUIRED)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 ")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x ")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
add_subdirectory (src)
add_subdirectory (test)
CMakeLists2.txt
cmake_minimum_required (VERSION 2.8.11)
add_library (Feedparser news.h xml2json.hpp jsonxx.cc curler.cc feed.cc )
target_link_libraries(Feedparser pthread)
target_link_libraries(Feedparser curl)
install(TARGETS Feedparser
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION /usr/lib)
install(FILES "feed.h" DESTINATION /usr/include )
target_include_directories (Feedparser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
CMakeLists3.txt
cmake_minimum_required (VERSION 2.8.11)
add_executable(Feedtest test.cc)
target_link_libraries (Feedtest LINK_PUBLIC Feedparser)
Here is my Header file.
feed.h
#include "news.h"
#include "curler.cc"
#include "jsonxx.h"
#include "jsonxx.cc"
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <thread>
#include <stdio.h>
using namespace std;
using namespace jsonxx;
class feed{
map <string,string> info;
std::map<int, Object> items;
string url;
news News;
Object *item;
void strip_items();
public:
Object json;
feed(string url);
void create(string url){
this->url = url;
}
feed(){}
string get_topic(){
return info["title"];
}
bool fetch();
bool fetch_data();
bool parse();
string get_url(){
return url;
}
string get_item_title(int index){
return News.title[index];
}
string get_item_img(int index){
return News.image[index];
}
string get_item_link(int index){
return News.link[index];
}
int get_total(){
return News.num_item;
}
struct news get_news(){
return News;
}
};
Should I include feed.h in feed.cc and compile and how does the compiler directly link .h files with the .cxx files in archives?
How do i write a cmake script for installing this library?
Where is my mistake?
C++'s building process consists of 2 stages:
Compilation: The compiler runs on every source (.cc) file, generating an intermidiate binary.
Linking: The linker runs on every intermidiate binary, matching declarations with definitions.
Finally, it combines all of them to make the final binary.
With this in mind, you need to make sure the compiler doesn't come across something that it doesn't know about. That's why you include headers (.h).
The linker should be fed with everything the compiler creates.