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.
This article is Part 2 in a 16-Part Series.
- Part 1 - Building a C/C++ Barcode & QR Code Reader for Raspberry Pi with Dynamsoft SDK
- Part 2 - CMake: Build C++ Project for Windows, Linux and macOS
- Part 3 - How to Port Visual Studio C++ Project to Linux with CMake
- Part 4 - Insight Into Dynamsoft Barcode SDK Decoding Performance
- Part 5 - Building ARM64 Barcode and QR Code Scanner on Nvidia Jetson Nano
- Part 6 - How to Decode QR Code on Mac with Apple Silicon
- Part 7 - How to Develop a Desktop GUI Barcode Reader with Qt and C/C++
- Part 8 - How to Build a Desktop Barcode Scanner with Webcam Support Using Qt QCamera
- Part 9 - Building Command-line Barcode and QR Code Reader in C++
- Part 10 - How to Build Linux ARM32 and Aarch64 Barcode QR Scanner in Docker Container
- Part 11 - How to Link MSVC DLLs with MinGW GCC in Windows
- Part 12 - Transforming Raspberry Pi 4 into a Barcode Scanner with a C++ App, USB Camera, and OLED Display
- Part 13 - Building Windows Desktop Barcode Reader with Win32 API and Dynamsoft C++ Barcode SDK
- Part 14 - How to Build a Command-Line Barcode Reader with Rust and C++ Barcode SDK
- Part 15 - How to Decode Barcode and QR Code from WebP Images in C++ and Python
- Part 16 - Building a Desktop C++ Barcode Scanner with Slimmed-Down OpenCV and Webcam
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 main.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.