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.