CMake: Build C++ Project for Windows, Linux and macOS
Last week, I shared how to create a CMake project for Windows. Since CMake is a cross-platform software building tool, I am going to make my C/C++ project support Linux and macOS.
What You Should Know
How to Make C/C++ Code Compatible with Multiple Platforms
I used the predefined macros to detect the operating system and customize relevant C/C++ code. Insert the following definitions to BarcodeReaderConfig.h.in:
#if defined(__linux) || defined(__linux__) || defined(linux)
# define LINUX
#elif defined(__APPLE__)
# define MACOS
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
# define WINDOWS
#endif
Use macros to control code logic in BarcodeReader.cxx.
How to Configure CMake Project
Windows
In my previous article, I didn’t mention how to select a generator. By default, CMake generates an x86 project in which I couldn’t link DynamsoftBarcodeReaderx64.dll. Let’s list the available generators on Windows:
cmake –help
According to the help information, we can specify a generator with Win64 architecture:
cmake -G"Visual Studio 14 2015 Win64" ..
Link the x64 library in CMakeLists.txt:
if(CMAKE_CL_64)
target_link_libraries (BarcodeReader "DBRx64")
else()
target_link_libraries (BarcodeReader "DBRx86")
endif()
if(CMAKE_CL_64)
add_custom_command(TARGET BarcodeReader POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_SOURCE_DIR}/platforms/win/DynamsoftBarcodeReaderx64.dll"
$<TARGET_FILE_DIR:BarcodeReader>)
else()
add_custom_command(TARGET BarcodeReader POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_SOURCE_DIR}/platforms/win/DynamsoftBarcodeReaderx86.dll"
$<TARGET_FILE_DIR:BarcodeReader>)
endif()
if(CMAKE_CL_64)
install (FILES "${PROJECT_SOURCE_DIR}/platforms/win/DynamsoftBarcodeReaderx64.dll" DESTINATION bin)
else()
install (FILES "${PROJECT_SOURCE_DIR}/platforms/win/DynamsoftBarcodeReaderx86.dll" DESTINATION bin)
endif()
Linux & macOS
Set the library search path:
if(LINUX)
link_directories("${PROJECT_SOURCE_DIR}/platforms/linux")
elseif(MACOS)
link_directories("${PROJECT_SOURCE_DIR}/platforms/macos")
endif()
Link shared libraries:
target_link_libraries (BarcodeReader "DynamsoftBarcodeReader")
Set install destination:
if(LINUX)
install (FILES "${PROJECT_SOURCE_DIR}/platforms/linux/libDynamsoftBarcodeReader.so" DESTINATION lib)
elseif(MACOS)
install (FILES "${PROJECT_SOURCE_DIR}/platforms/macos/libDynamsoftBarcodeReader.dylib" DESTINATION lib)
endif()
Build and install the executable file and library:
# Linux
sudo cmake --build . --target install
# macOS
cmake --build . --target install
Note: the default destinations are /usr/local/bin and /usr/local/lib. When you run the executable BarcodeReader, you will see the error message:
libDynamsoftBarcodeReader.so: cannot open shared object file: No such file or directory.
Can we fix the issue by changing the value of CMAKE_INSTALL_PREFIX to `/usr/lib’?
set(CMAKE_INSTALL_PREFIX "/usr")
It is okay for Linux, but not permitted on the latest version of macOS.
A usual solution is to export LD_LIBRARY_PATH:
export LD_LIBRARY_PATH =/usr/local/lib: $LD_LIBRARY_PATH
CMake provides a better way that changes RPATH.
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
Build and install the project again. We can now run barcode reader app with no errors.