I'm not a programmer, just writing some code for personal use from time to time, so I don't have much experience and knowledge. I'm using CMake and gcc/g++ on Linux or clang on Android (in Termux). Since, I only used those compilers (which have much the same flags), I wrote CMake code to accept only them (I noticed that MSVC has different flags, so, I just didn't spend my time on it).
Recently, I wrote a dynamic C library which uses the GMP library (GMP is linked statically, so, the library can be used on another desktop, without need of installing GMP). But there was a need to compile it on Windows, so, my mate could use the it too. I spent 2 days reading all information I've found and trying to compile it, but without luck. I tried using Visual Studio 2022 and Visual Studio Code with the MSVC compiler but it looked like a completely different workflow to me. I tried using vcpkg but still wasn't able to compile the library (I just aborted the compilation process after waiting more than 1 hour… On Linux it compiled for less than 10 seconds). Switching to another library, like MPIR, didn't look good to me (don't want to rewrite the existing code).
Finally, I managed to compile the DLL library for Windows. Below, I'll provide some initial (Linux) dummy code (for the sake of simplicity), the steps that I took to compile the library, and describe what I changed for Windows.
I'm posting this for the following reasons:
Update
Moved the solution I came up with (not the best, I think) to the answers.
Example code and steps of my solution are below.
Project structure:
dll
├src
│├test
││├test.h
││└test.c
│├main.c
│└main.py
├build.sh
└CMakeLists.txt
Initial project files (for Linux)
test.h:
#ifndef TEST_H
#define TEST_H
#include <gmp.h>
void Double(mpz_t a);
void Triple(mpz_t a);
#endif // TEST_H
test.c:
#include "test.h"
#include <gmp.h>
void Double(mpz_t a) { mpz_mul_ui(a, a, 2UL); }
void Triple(mpz_t a) { mpz_mul_ui(a, a, 3UL); }
main.c:
#include <stdbool.h>
#include <stdio.h>
#include "test/test.h"
#include <gmp.h>
bool Mult(const char* const val, const char op) {
mpz_t num;
mpz_init_set_str(num, val, 0);
gmp_printf(" In: %Zi\n", num);
if (op == 'd') { Double(num); }
else if (op == 't') { Triple(num); }
else {
printf("Invalid argument!\n");
mpz_clear(num);
return false;
}
gmp_printf("Out: %Zi\n", num);
mpz_clear(num);
return true;
}
main.py:
import ctypes
import os
import platform
def GetFunction():
osys = platform.system()
lib_path = r'/home/linux/Documents/tests/dll/cmake_build'
lib_path = os.path.join(lib_path, 'libtest.')
if osys == 'Windows': lib_path += 'dll'
else: lib_path += 'so'
lib = ctypes.CDLL(lib_path)
lib.Mult.restype = ctypes.c_bool
lib.Mult.argtypes = [ctypes.c_char_p, ctypes.c_char]
return lib.Mult
if __name__ == '__main__':
mult = GetFunction()
big_num = 12345678901234567890
big_num = f'{big_num}'.encode('ascii')
op = 't'.encode('ascii')
mult(big_num, op)
build.sh:
#!/bin/bash
n_args=$#
if [ $n_args -gt 0 ]; then
echo "Illegal number of parameters!"
exit 1
fi
BLD='cmake_build'
BIN='libtest.so'
set -e # Exit on error.
[ -d "$BLD" ] || mkdir "$BLD" # Create build dir.
cd "$BLD"
[ -f "$BIN" ] && rm "$BIN" # Delete compiled file.
# Build and run.
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
cd ..
CmakeLists.txt:
cmake_minimum_required(VERSION 3.10)
set(pref "lib_") # Prefix.
project(${pref}test)
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
string(LENGTH ${pref} len)
string(SUBSTRING ${PROJECT_NAME} ${len} -1 out_name)
string(REPLACE "_" "" out_name ${out_name})
string(REPLACE "_" "" lib_name ${PROJECT_NAME})
unset(len)
string(TOLOWER "${CMAKE_BUILD_TYPE}" conf_build_type)
string(TOLOWER "Debug" conf_debug)
string(TOLOWER "Release" conf_release)
if(NOT conf_build_type MATCHES "^(${conf_debug}|${conf_release})$")
message(FATAL_ERROR "Unknown build configuration (\"${CMAKE_BUILD_TYPE}\")!")
endif()
set(comp_gnu "GNU")
set(comp_clang "Clang")
if(CMAKE_C_COMPILER_ID STREQUAL comp_gnu)
set(COMPILER_VERSION_REQD "13.0.0")
elseif(CMAKE_C_COMPILER_ID STREQUAL comp_clang)
set(COMPILER_VERSION_REQD "17.0.0")
else()
message(FATAL_ERROR "\tNot supported compiler (${CMAKE_C_COMPILER_ID})!")
endif()
if(CMAKE_C_COMPILER_VERSION VERSION_LESS COMPILER_VERSION_REQD)
message(FATAL_ERROR "Version ${COMPILER_VERSION_REQD} or higher required!")
endif()
message("\t${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} "
"${CMAKE_BUILD_TYPE} ${lib_name}")
string(CONCAT flags "-W;-Wall;-Wextra;-Wpedantic;-Wconversion;"
"-Werror=return-type;-Wsign-conversion")
if(CONF_BUILD_TYPE STREQUAL conf_debug)
string(CONCAT flags "${flags}" ";-O0;-g")
string(CONCAT flags "${flags}" ";-fsanitize=address")
add_compile_options("$<$<CONFIG:DEBUG>:${flags}>")
add_link_options("$<$<CONFIG:DEBUG>:-fsanitize=address>")
elseif(CONF_BUILD_TYPE STREQUAL conf_release)
string(CONCAT flags "${flags}" ";-Ofast")
add_compile_options("$<$<CONFIG:RELEASE>:${flags}>")
endif()
set(proj_fpath "${CMAKE_CURRENT_SOURCE_DIR}/")
set(exec_path "${proj_fpath}src/")
add_library(${lib_name} SHARED
"${exec_path}main.c"
"${exec_path}test/test.c")
target_include_directories(${lib_name} PRIVATE "${exec_path}")
target_link_libraries(${lib_name} PRIVATE gmp)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
set_target_properties(${lib_name} PROPERTIES OUTPUT_NAME "${out_name}")
Steps that helped to solve the problem
MSYS2.MSYS2 MINGW64 shell and installed the following:
CMake: pacman -S mingw-w64-x86_64-cmake;GMP: pacman -S mingw-w64-x86_64-gmp.C code in main.c for DLL compilation.build.sh to include proper compiled library extension (.dll for Windows).CmakeLists.txt to specify paths to GMP header and compiled library:
C:\msys64\mingw64\include for gmp.h;C:\msys64\mingw64\lib: for libgmp.a and libgmp.dll.a.MSYS2 MINGW64 shell, ran ./build.sh to compile the library and closed the shell.main.py to include proper compiled library path.PowerShell or Command Prompt.main.py folder.python3 main.py command.Modified portions of project files (for Windows)
main.c:
...
#include <gmp.h>
#ifdef _WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif // _WIN32
DLLEXPORT bool Mult(const char* const val, const char op) {
...
main.py:
...
osys = platform.system()
lib_path = r'C:\Users\Linux\diff\dll\cmake_build'
lib_path = os.path.join(lib_path, 'libtest.')
...
build.sh:
...
BLD='cmake_build'
BIN='libtest.dll'
set -e # Exit on error.
...
# Build and run.
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
...
CmakeLists.txt:
...
target_include_directories(${lib_name} PRIVATE "${exec_path}")
#find_package(GMP REQUIRED) # This line throws an error, so, I just commented it out.
if(NOT GMP_FOUND)
set(GMP_ROOT_DIR "C:/msys64/mingw64/")
set(GMP_INCLUDE_DIRS "${GMP_ROOT_DIR}/include")
set(GMP_LIBRARIES "${GMP_ROOT_DIR}/lib/libgmp.a")
set(GMP_FOUND TRUE)
endif()
target_link_libraries(${lib_name} ${GMP_LIBRARIES})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
...