Issue
TL;DR: how can I make CMake
-generated Makefiles
create shared libraries with .so
extensions in Windows instead of .dll
? Put another way: how can I write a CMakeLists.txt
that creates libfunc.so
on both Linux and Windows, instead of libfunc.dll
on the latter?
This is a simplification of a larger project. The main application uses dlopen()
to dynamically load a shared library. Note that the in-code loaded shared library filename has a .so
extension.
// main.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
int (*fptr)(void) = NULL;
const char* filename = "libfunc.so"; // Nota bene
void* handle = dlopen(filename, RTLD_NOW);
if (! handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
fptr = dlsym(handle, "func");
fprintf(stdout, "fptr returns %d\n", (*fptr)());
dlclose(handle);
return 0;
}
// dlfcn.h
#ifndef DLFCN_H
#define DLFCN_H
#include <windows.h>
#define RTLD_NOW (1<<0)
extern inline void *dlopen(const char *lib, int flags) {
(void) flags;
return LoadLibraryExA(lib, NULL, 0);
}
extern inline void *dlsym(void *handle, const char *name) {
FARPROC fp = GetProcAddress((HINSTANCE) handle, name);
return (void *)(intptr_t)fp;
}
extern inline char *dlerror() {
// Not thread-safe!
static char msg[1024];
msg[0] = '\0';
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
return msg;
}
extern inline int dlclose(void *handle) {
return FreeLibrary(handle) ? 0 : -1;
}
#endif
// func.c
int func(void) {
return 42;
}
This code is compiled for both Linux and Windows.
The original version of this project was built with handwritten makefiles, therefore the filename of the compiled shared library was controlled by a Makefile
rule, e.g.:
.PHONY: all
CFLAGS :=
ifeq ($(shell uname), Windows_NT)
CFLAGS += -isystem .
endif
all: libfunc.so a.out
libfunc.so: func.o
cc -shared -fpic -o $@ $^
a.out: libfunc.so main.c
cc -o $@ main.c ${CFLAGS}
The above Makefile
results in libfoo.so
being created on both Linux and Windows, because make
invokes the following line on both Linux and Windows:
cc -shared -fpic -o libfunc.so func.o
The project is being migrated to a CMake
-based build. This is the CMakeLists.txt
that has been written:
cmake_minimum_required(VERSION 3.5)
project(hello_world)
add_library(func SHARED func.c)
add_executable(a.out main.c)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR})
endif()
The CMake
generator is "Unix Makefiles", and the Makefile
generation and subsequent build are successful, however libfunc.dll
is generated by the build instead of libfunc.so
, therefore the executable fails at runtime, because it is trying to load a file named libfunc.so
. If I rename libfunc.dll
libfunc.so
, the executable is able to run to completion successfully.
I've been told that CMake can be configured to either copy/rename files after their creation, e.g. copying/renaming libfunc.dll
to libfunc.so
, but not how to do this. So my question is: how can I configure my CMakeLists.txt
to create libfunc.so
both when the host platform is Linux and Windows? Either copying/renaming the .dll
to .so
after creation, or just outright creating a .so
in the first place are both fine.
This answer informs the extent of my knowledge on the differences between dynamic link libraries and shared libraries. But I believe they are not relevant to my immediate need since, as stated above, renaming the .dll
to .so
fixes the reported problem.
I'm also aware that there may be other ways to handle this situation: e.g. some form of #ifdef
macro to create a compile-time conditional on the shared library extension. But, per past guidance, I don't want to mix many questions into one post. I would like to use this post to learn about the ability to control shared library output filenames in a CMakeLists.txt
. I will ask separately if there are best practices how to handle this situation.
Solution
You may change extension of the created library by setting SUFFIX property.
For avoid confusion, I would not use Linux-specific .so
extension on Windows. You may choose any other extension, e.g. .mod
.
Also note, that libraries loaded via dlopen
mechanism should be created with MODULE
keyword instead of SHARED
.
Example:
# CMakeLists.txt
add_library(func MODULE func.c)
set_target_properties(func PROPERTIES SUFFIX ".mod")
# Module usage
void* handle = dlopen("func.mod", RTLD_NOW);
Answered By - Tsyvarev Answer Checked By - Pedro (WPSolving Volunteer)