CMake link shared library on Windows - windows

There are three files, (m.c,m.h, and **main.c*).
File m.h
// m.h
int m();
File m.c
// m.c
#include <stdio.h>
#include "m.h"
int m(){
printf("Hello,m!\n");
return 0;
}
File main.c
// main.c
#include "m.h"
int main(){
return m();
}
While I prefer a shared library (m.dll), I've made the CMakeLists.txt file:
PROJECT("app1")
ADD_LIBRARY(m SHARED m.c)
ADD_EXECUTABLE(myexe main.c)
TARGET_LINK_LIBRARIES(myexe m)
The CMake configuration is done and generated done. Opening app1.sln and building with Visual Studio, it crashes as
LNK1104:Can't open file "Debug\m.lib"
It only works as STATIC at ADD_LIBRARY(). Why doesn't it work on Windows?
If I got another shared library (mylib.dll), how could I invoke its functions in my main.c and CMakeLists.txt files?

There are differences between dynamic library linking on different platforms which also needs some additional code. The good news is, that CMake can help you with this. I found the following blog post by Gernot Klingler very useful:
Creating and using shared libraries with different compilers on different operating systems
In short you need some "export prefix" defined for whatever is declared in m.h. Otherwise the build process will not generate an "import library" for statically linking named m.lib (see also CMAKE_IMPORT_LIBRARY_SUFFIX).
Here is your code with the modifications needed:
m.h
#include "m_exports.h"
int M_EXPORTS m();
m.c
#include "m.h"
#include <stdio.h>
int m(){
printf("Hello,m!\n");
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
include(GenerateExportHeader)
PROJECT("app1")
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}")
ADD_LIBRARY(m SHARED m.c m.h m_exports.h)
GENERATE_EXPORT_HEADER(m
BASE_NAME m
EXPORT_MACRO_NAME M_EXPORTS
EXPORT_FILE_NAME m_exports.h
STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC)
ADD_EXECUTABLE(myexe main.c)
TARGET_LINK_LIBRARIES(myexe m)
Additional References
GenerateExportHeader macro
cmake and GenerateExportHeader
How do I get CMake to create a dll and its matching lib file?
MSDN: Walkthrough: Creating and Using a Dynamic Link Library (C++)

Using WINDOWS_EXPORT_ALL_SYMBOLS might help. See an introductory article for details. In short, invoke CMake like this:
cmake -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE -DBUILD_SHARED_LIBS=TRUE

Add this in CMakeLists.txt.
if(MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
set(BUILD_SHARED_LIBS TRUE)
endif()

Related

CMake on Windows generates both a static and dynamic version of a library set to be built as SHARED

I am very inexperienced when it comes to C++ on Windows. For some odd reason add_library(<some-target-name> SHARED) generates both a static library (.lib) and a dynamic one (.dll). This wouldn't be an issue per say if it didn't break the following project during the linking stage.
CMakeLists.txt (library):
cmake_minimum_required (VERSION 3.8)
project(test
LANGUAGES CXX
)
#set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(SRC
${CMAKE_CURRENT_SOURCE_DIR}/src/test.cpp
)
set(INCLUDE_DIR
include
#${PROJECT_BINARY_DIR}
)
include_directories(test ${INCLUDE_DIR})
add_library(test SHARED)
target_sources(test PUBLIC ${SRC})
include(GenerateExportHeader)
generate_export_header(test
BASE_NAME test
EXPORT_MACRO_NAME test_EXPORT
STATIC_DEFINE test_BUILT_AS_STATIC
#EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/deploy/test_export.h
EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/test_export.h
)
CMakeLists.txt (executable that uses library):
cmake_minimum_required (VERSION 3.8)
project(playground
LANGUAGES CXX CUDA
)
set(SRC
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
set(INCLUDE_DIR
${CMAKE_SOURCE_DIR}/test/include
)
link_directories(${CMAKE_BINARY_DIR}/deploy)
add_executable(playground ${SRC})
target_include_directories(playground PUBLIC ${INCLUDE_DIR})
target_link_libraries(playground PRIVATE test)
CMakeLists.txt (top-level project):
cmake_minimum_required (VERSION 3.8)
project ("mainproject")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/deploy)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/deploy)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/deploy)
add_subdirectory ("test")
add_subdirectory ("playground")
You can probably put anything in the source files above to reproduce the error. In my case I have the following simple content:
test.h (library)
#include "test_export.h"
#include <string>
namespace mainproject
{
namespace library
{
class Test {
private:
std::string txt;
public:
test_EXPORT Test(std::string);
virtual test_EXPORT void msg();
};
} // library
} // mainproject
test.cpp (library)
#include "test.h"
#include <iostream>
mainproject::library::Test::Test(std::string txt)
: txt(txt)
{
}
void mainproject::library::Test::msg() {
std::cout << "Message is " << this->txt << std::endl;
}
main.cpp (executable)
#include "test.h"
#include <iostream>
int main(int argc, char* argv[])
{
std::cout << "PLAYGROUND" << std::endl;
mainproject::library::Test t("hello");
t.msg();
return 0;
}
I use Visual Studio 2017 and CMake 3.21 on Windows 10.
The first thing I did after creating my top-level project was to create a sub-project for the library. Everything built without any issues. I didn't even look at the output directory of my build (normally I do that only when something breaks - more on that below :D).
But after linking against it all of a sudden I got:
fatal error LNK1169: one or more multiply defined symbols found
I spent a lot of time until I discovered the issue: my library was being built in two versions - static and dynamic. So simply using
target_link_libraries(playground PUBLIC test)
basically was throwing test.lib and test.dll at the linker hence the error. I fixed the issue by specifying that I want the DLL:
target_link_libraries(playground PUBLIC test.dll)
I would like to know why this is happening. On Linux I have never experienced such a behaviour. When I set it to SHARED I get a shared object file (.so; equivalent to the dynamic library on Windows). When I set it to STATIC I get an archive file (.a; equivalent to the static library on Windows). If the property is not set explicitly, the default is taken. The behaviour exhibited in the example above points at implicit overriding explicit.
It goes either weirder - even my executable has its own LIB file. If I remove all the LIB files, I can still run the executable without any issues.

Simple CGO example fails compilation with "duplicate symbol" error

I want to make a simple example of calling C code from Go with CGO. But for some reason I can't achieve desired. Compilation fails with the following error:
go build main.go
# awesomeProject1/print
duplicate symbol '_do_print' in:
$WORK/b002/_x002.o
$WORK/b002/_x003.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The code:
// print/print.c
#include <stdio.h>
void do_print(char * x){
printf("%s", x);
}
// print/print.go
package print
// #include <print.c>
import "C"
func DoPrint() {
C.do_print(C.CString("Hello!"))
}
// main.go
package main
import "awesomeProject1/print"
func main() {
print.DoPrint()
}
If I make do_print function static it compiles but I wouldn't be able to do that for 3rd party code I want to integrate with later.
Am I missing some essential piece from documentation? Tutorials are all alike and claim to work where my example fails. Please help!
Go version 1.16.4
There are two things going on here:
go build compiles *.c in addition to *.go1
#include <print.c> is exactly equivalent to overwriting the include statement with the contents of print.c
As a result, you are compiling the contents of print.c twice: once when print.c is compiled by CC, and once when print.go is compiled by CGo. Thus, the object files for print.c and print.go each contain all of the exported symbols defined in print.c. So you get two copies of do_print. Making do_print static works because a function declared as static will not be exported by CC.
Including a .c file (e.g. #include <file.c>) is pretty much always a bad idea. If you have a legitimate reason to #include function bodies, the convention is to use a header (.h) file. This is what C++ templating libraries do (like Boost, IIRC). Because C files normally are not included, and H files normally are included, including a C file subverts expectations and thus is likely to cause confusion and problems.
1 IIRC, Go will compile C files in a package if any of the Go files in the package import "C". That is, if no Go files in a package use CGo, Go will ignore C files in that package.

How To Add <math.h> header in CLion

I'm New to CLion. I have created a project which name is test12 and which file name is avs.c.
I'm Getting an Error of type Undefined reference 'sqrt'
CMakeListsta.txt is :
cmake_minimum_required(VERSION 3.13)
project (test12 C)
set(CMAKE_C_STANDARD 99)
add_executable(test12 avs.c)
What changes should I do to make CMAKELISTS use math.h header in CLion?
It looks like math.his not enabled in CLion, so you need to enable it! Source here.
add #include <math.h>
add target_link_libraries( m) in CMakeLists.txt
The second command allows you to link with libm for the math functions.
Adding this to your makefile! (Check the source I gave you!!)
target_link_libraries(log m)

How to setup wxWidgets for hello world project with Code::Blocks?

I would like to use wxWidgets 3.0.2 library in my project. However I am unable to run even the hello world program. I have downloaded the headers and the appropriate binaries (TDM GCC x64 4.8.1). I've extracted them without any changes. So there are include and libs folders present in my wxWidgets folder. I am using TDM-GCC 5.1 which is setup properly.
When I create a simple console application and only include the main file
#include "C:\wxWidgets\include\wx\wx.h"
I get an error
C:\wxWidgets\include\wx\wx.h|14|fatal error: wx/defs.h: No such file or directory|
Which is quite reasonable, as defs.h is in the same folder as wx.h and there is no wx folder inside. Do I need to rearrange the file structure?
Is the compiler problem here (5.1 used instead of 4.8.1)?
For three days I am running trough different tutorials, and all the time I get this or similar errors. How to set it up properly?
The whole code is just:
#include <iostream>
using namespace std;
#include "C:\wxWidgets\include\wx\wx.h"
int main()
{
cout << "hello" << endl;
return 0;
}
You should never include wxWidgets (or any other library) header files using full paths. Instead you should have just
#include <wx/wx.h>
in your code and set up your compiler headers search path to include c:\wxWidgets\include directory. Notice that you will also need to add c:\wxWidgets\lib\gcc481_lib\mswu or similar to the includes path, depending on the exactl configuration you're using (e.g. it could be gcc481_dll if you're using the DLL build).

Netbeans Ubuntu C++ cannot find include files in release build but can in debug

I am creating a small command line C/C++ app to convert a binary file to a tab delimited text file after not working with C/C++ for several years. Netbeans is new to me as well. The include files below work fine when I build using 'Debug' but when using 'Release' I get the IDE error "Cannot find include file'; The files are flagged as well as lines of code. The compiler error is 'No such file or directory'.
I have other apps in the work space that do compile with the release tab set.
// these are the first code lines in the file
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
// No problem with these
#include <math.h>
#include <stdbool.h>
I don't notice any differences in properties between the apps that build correctly and this one.
Edit:
I've added the path to the includes, /usr/include for C and /usr/include/c++/4.9.2/ for C++, (I have not done this for other apps and they compile) and the error colors for the release includes went from red to yellow and only two lines in the code had red underlines. size_type and npos with the error "unable to find identifiers:
void replaceExt(std::string& s, const std::string& newExt) {
std::string::size_type i = s.rfind('.', s.length()); // size_type
if (i != std::string::npos) { //npos
s.replace(i + 1, newExt.length(), newExt);
}
}
The compiler produced this error message:
gcc -m64 -c -g -I/usr/include/c++/4.9.2 -I/usr/include -MMD -MP -MF "build/Release/GNU-Linux-x86/main.o.d" -o build/Release/GNU-Linux-x86/main.o main.c
In file included from main.c:9:0:
/usr/include/c++/4.9.2/cstdlib:41:28: fatal error: bits/c++config.h: No such file or directory
#include <bits/c++config.h>
Final Edit
It didn't appear right that NetBeans would not have the path to the standard includes so I created a new C++ project, accepted all the default preferences, and copied the all the code except main from the failed project to the new one checking to see if it would compile. Lastly I copied main and found that it builds in both release and debug with no errors.
I don't have any idea what went wrong, but I've found the fix.
It didn't appear right that NetBeans would not have the path to the standard includes so I created a new C++ project, accepted all the default preferences, and copied the all the code except main from the failed project to the new one checking to see if it would compile. Lastly I copied main and found that it builds in both release and debug with no errors.
I don't have any idea what went wrong, but I've found the fix.

Resources