I was able to compile Freetype2 library for arm, arm64, x86, x86_64 architectures. These are steps I done to compile it.
Created standalone toolchains for architectures mentioned above.
make_standalone_toolchain.py \
--arch arm64 \
--api 26 \
--stl=libc++ \
--install-dir=my-arm64-toolchain
Set some envirement variables
export PATH=$PATH:`pwd`/my-toolchain/bin
target_host=aarch64-linux-android
export AR=$target_host-ar
export AS=$target_host-as
export CC=$target_host-gcc
export CXX=$target_host-g++
export LD=$target_host-ld
export STRIP=$target_host-strip
export CFLAGS="-fPIE -fPIC"
export LDFLAGS="-pie"
Configure freetype for compilation
./configure --host=aarch64-linux-android --prefix=/home/freetype-arm64 --without-zlib --without-harfbuzz --with-png=no
And finally make && install
Compilation was successfull and I was able to get Static freetype library.
I added library to my android studio libs folder.
This is folder structure for library:
libs---freetype
|---${ANDROID_ABI}
|---include
|---freetype2
|---freetype
---|Bunch of header files
|---ftbuild.h
|---lib
|---libfreetype.a
This is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
add_definitions("-DGLM_FORCE_SIZE_T_LENGTH -DGLM_FORCE_RADIANS")
add_subdirectory(src/main/cpp/glm)
add_library(freetype STATIC IMPORTED)
set_target_properties(freetype PROPERTIES IMPORTED_LOCATION${PROJECT_SOURCE_DIR}/libs/freetype/${ANDROID_ABI}/lib/libfreetype.a)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall")
add_library(native-lib SHARED
src/main/cpp/native-lib.cpp
src/main/cpp/graph.cpp
src/main/cpp/text.cpp
src/main/cpp/graphDataWorker.cpp
)
target_include_directories(native-lib PRIVATE ${PROJECT_SOURCE_DIR}/libs/freetype/${ANDROID_ABI}/include/freetype2)
target_link_libraries(native-lib
android
freetype
GLESv2
EGL
glm
atomic
log
OpenSLES
)
The problem is that I can use library inside my code and android studio is not giving me any errors, but then i try to run application it gives me this error:
linker command failed with exit code 1 (use -v to see invocation)
PS: I Can't see freetype headers in android studio file tree aswell, but can see other libraries.
Question: Am I importing library in a wrong way or this is cross-compilation issue and if so how to do it properly?
I done a lot of research on this issue and was unable to find a solution
Any help will be appreciated.
Unless you mixed paths when installing your build of FreeType2, it seems that the compilation of the library is incorrect, since the linker complains about the library being for x86_64 architecture.
On Linux, usually you can check the architecture the library is built for with command file. Another way to check the architecture is to use the objdump tool that is shipped with Android NDK (if I remember correctly it is generated with a toolchain as well). Its name follows the same pattern than the compiler/linker/... tools, $target_host-objdump.
I would recommend to compile FreeType2 using CMake instead of autotools, at least in my case I successfully did it that way.
Here is a sample of CMake configuration I wrote to cross-compile FreeType2 for Android. It is a little complex because it is part of a project where I build several libraries.
The idea is that my CMake configuration uses ExternalProject to download and compile Freetype. My top-level CMakeLists.txt contains:
# CMakeLists.txt
set(DEVENV_CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}
-DCMAKE_C_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_C_FLAGS_${BUILD_TYPE}=${CMAKE_C_FLAGS_${BUILD_TYPE}}
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_CXX_FLAGS_${BUILD_TYPE}=${CMAKE_CXX_FLAGS_${BUILD_TYPE}}
-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
)
if(BUILD_SHARED_LIBS)
list(APPEND DEVENV_CMAKE_ARGS -DBUILD_STATIC_LIBS=OFF -DLIBTYPE=SHARED)
else()
list(APPEND DEVENV_CMAKE_ARGS -DBUILD_STATIC_LIBS=ON -DLIBTYPE=STATIC)
endif()
if(DEFINED ANDROID_TOOLCHAIN_DIR)
get_filename_component(_toolchain_dir ${ANDROID_TOOLCHAIN_DIR} ABSOLUTE)
list(APPEND DEVENV_CMAKE_ARGS -DANDROID_TOOLCHAIN_DIR=${_toolchain_dir})
endif()
if(DEFINED CMAKE_ANDROID_ARCH_ABI)
list(APPEND DEVENV_CMAKE_ARGS -DCMAKE_ANDROID_ARCH_ABI=${CMAKE_ANDROID_ARCH_ABI})
endif()
if(CMAKE_TOOLCHAIN_FILE)
get_filename_component(_toolchain_file ${CMAKE_TOOLCHAIN_FILE} ABSOLUTE)
list(APPEND DEVENV_CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${_toolchain_file})
endif()
add_subdirectory(freetype)
So the variable DEVENV_CMAKE_ARGS contains appropriate CMake arguments for cross-compiling that reflect my current CMake configuration. Under directory freetype, the CMakeLists.txt contains:
# freetype/CMakeLists.txt
project(freetype)
include(ExternalProject)
if(NOT FREETYPE_GIT_REPOSITORY)
set(FREETYPE_GIT_REPOSITORY "https://git.savannah.gnu.org/git/freetype/freetype2.git")
endif()
set(FREETYPE_GIT_REPOSITORY "${FREETYPE_GIT_REPOSITORY}"
CACHE STRING "Git repository for library Freetype."
)
if(NOT FREETYPE_GIT_TAG)
set(FREETYPE_GIT_TAG "VER-2-9")
endif()
set(FREETYPE_GIT_TAG "${FREETYPE_GIT_TAG}"
CACHE STRING "Tag or branch of Git repository to build."
)
message(STATUS "Freetype Git repository: ${FREETYPE_GIT_REPOSITORY}")
message(STATUS "Freetype Git tag: ${FREETYPE_GIT_TAG}")
externalproject_add(freetype
GIT_REPOSITORY "${FREETYPE_GIT_REPOSITORY}"
GIT_TAG "${FREETYPE_GIT_TAG}"
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/freetype/src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/freetype/build"
CMAKE_ARGS ${DEVENV_CMAKE_ARGS}
)
Basically this CMakeLists.txt uses ExternalProject to download and compile FreeType2 in the build directory.
And I use a toolchain file for Android:
# android.cmake
set(CMAKE_SYSTEM_NAME "Android")
set(CMAKE_ANDROID_STANDALONE_TOOLCHAIN ${ANDROID_TOOLCHAIN_DIR})
Sorry if it is overly complex; it comes from a project where I was playing with cross-compiling for Android. I hope it helps anyway.
Related
I am developing a C++ library, including OpenCV, which will be used in a cross-platform Xamarin solution through a wrapper and the NuGet packaging system (see this guide). I configured a CMakeLists.txt file but I simply cannot get OpenCV to be correctly linked for both static (iOS) and dynamic (Android) libraries.
I tried to change the OpenCV_DIR variable, install and build OpenCV from sources and manually include the content of the OpenCV_INCLUDE_DIRS variable but nothing worked. I also noticed that the linking works when only using cv::Point. But the linking does not work when using cv::Mat, which I do not understand the reason.
The following is the CMakeLists.txt that I am using :
cmake_minimum_required (VERSION 3.2)
project (MyLib C CXX)
enable_testing()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
MESSAGE(STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS})
# Source and headers files
set(SOURCES File1.cpp File2.cpp)
set(HEADERS File1.h File2.h)
# Library
if(BUILD_SHARED_LIBS)
add_library (MyLib SHARED ${SOURCES} ${HEADERS})
target_compile_definitions(MyLib PUBLIC IS_BUILDING_SHARED)
else()
add_library (MyLib STATIC ${SOURCES} ${HEADERS})
endif()
# Dependencies
set(OpenCV_DIR /usr/local/Cellar/opencv/4.5.0_1/lib/cmake/opencv4)
find_package(OpenCV REQUIRED)
message(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")
message(STATUS "OpenCV_LIBS = ${OpenCV_LIBS}")
message(STATUS "OpenCV_DIR = ${OpenCV_DIR}")
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(MyLib ${OpenCV_LIBS})
The following shows the location of OpenCV's files that are used during the build process. Everything seems alright.
-- OpenCV_INCLUDE_DIRS = /usr/local/Cellar/opencv/4.5.0_1/include/opencv4
-- OpenCV_LIBS = opencv_calib3d;opencv_core;opencv_dnn;opencv_features2d;opencv_flann;opencv_gapi;opencv_highgui;opencv_imgcodecs;opencv_imgproc;opencv_ml;opencv_objdetect;opencv_photo;opencv_stitching;opencv_video;opencv_videoio;opencv_alphamat;opencv_aruco;opencv_bgsegm;opencv_bioinspired;opencv_ccalib;opencv_datasets;opencv_dnn_objdetect;opencv_dnn_superres;opencv_dpm;opencv_face;opencv_freetype;opencv_fuzzy;opencv_hfs;opencv_img_hash;opencv_intensity_transform;opencv_line_descriptor;opencv_mcc;opencv_optflow;opencv_phase_unwrapping;opencv_plot;opencv_quality;opencv_rapid;opencv_reg;opencv_rgbd;opencv_saliency;opencv_sfm;opencv_shape;opencv_stereo;opencv_structured_light;opencv_superres;opencv_surface_matching;opencv_text;opencv_tracking;opencv_videostab;opencv_viz;opencv_xfeatures2d;opencv_ximgproc;opencv_xobjdetect;opencv_xphoto
-- OpenCV_DIR = /usr/local/Cellar/opencv/4.5.0_1/lib/cmake/opencv4
Android
The following is the commands that I am using to build the Android dynamic library (.so). I have installed the NDK and am building for each ABI (x86, x86_64, armeabi-v7a, arm64-v8a).
cmake ../.. -DCMAKE_TOOLCHAIN_FILE=/Users/$USER/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=21 -DANDROID_ABI=$abi_name -DBUILD_SHARED_LIBS=ON
cmake --build . --config Release
I directly get an error when building the library which is the following.
ld: error: /usr/local/Cellar/opencv/4.5.0_1/lib/libopencv_gapi.4.5.0.dylib: unknown file type
ld: error: /usr/local/Cellar/opencv/4.5.0_1/lib/libopencv_stitching.4.5.0.dylib: unknown file type
[...]
ld: error: /usr/local/Cellar/opencv/4.5.0_1/lib/libopencv_rapid.4.5.0.dylib: unknown file type
ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
iOS
The following is the commands that I am using to build the iOS static library (.a). I am using leetal's ​cmake toolchain file from this repository.
cmake ../.. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../../ios.toolchain.cmake -DPLATFORM=OS64COMBINED -DBUILD_SHARED_LIBS=OFF
cmake --build . --config Release
The compilation of the static library seems to work because no error message is printed. However, when the library is used in the final Xamarin solution, the linked library cannot be found and the following error is shown.
Native linking failed, undefined symbol: cv::Mat::deallocate(). Please verify that all the necessary frameworks have been referenced and native libraries are properly linked in. (MT5210)
Question
What am I missing in order to properly compile and link OpenCV into my C++ library ?
I am working on macOS Big Sur and uses the following tools versions:
cmake : 3.20.0-rc5
ndk : 23.0.7196353
apple clang : 12.0.0
I hope that the description of my problem is clear enough and I thank you in advance for any help.
We had same problems, including Xamarin's DllNotFoundException with message from last comment, which led me to this topic. What fixed the exception for us in the end was linking statically to OpenCV *.a libs instead of linking to the shared libopencv_java4.so file. So we now have a huge 30MB nativelib.so file for each android ABI in build output, instead of a pair of small nativelib.so and libopencv_java4.so per ABI. CMakeLists looks like this:
set( OpenCV_DIR "~/opencv/build/OpenCV-android-sdk/sdk/native/jni" )
find_package( OpenCV REQUIRED )
target_link_libraries( # Specifies the target library.
nativelib
${OpenCV_LIBS})
Another thing in our project is we use OpenCV optional modules and had to create a custom OpenCV build, which I guess ensures our native library and OpenCV are compiled against same NDK version. I suppose using the prebuilt OpenCV distribution and compiling against a different NDK version could lead to problems too otherwise.
I would like to link my Debug executable with external library built in Release version using FetchContent.
I'm able to link my Debug executable with Debug built library and similar with Release and Release using:
project(CMakeDemo)
set(FETCHCONTENT_QUIET OFF)
FetchContent_Declare(
ZLIB
URL https://zlib.net/zlib-1.2.11.tar.gz
)
FetchContent_MakeAvailable(ZLIB)
add_executable(CMakeDemo main.cpp)
target_link_libraries(CMakeDemo ZLIB)
So when I execute from a build directory on Windows:
cmake ../
cmake --build .
Then zlib and my executable is built in Debug version and my executable is linked with that zlib Debug version.
But how to enhance the CMake to build my executable in Debug version but zlib in Release version and link my Debug executable with the zlib Release version? How to achieve that using FetchContent_Declare?
(I believe this has to be some common approach because for example when someone wants to use Google Test framework or zlib in a project then for sure he wants to use these external libraries in Release version always)
FetchContent() will integrate the dependency, here ZLIB, in your worktree like an add_subdirectory() so flags will be identical (if ZLIB is correctly configured to be use as subproject, spoiler: this is not the case you'll need to patch it...).
If you really want to build it in Release, you should try to use ExternalProject() and execute_process()
to build and install it at configure time then you can use find_package() to retrieve this pre-installed dependency.
I know that the question is old, but as I have been struggling a whole day with the same issue, and wanted to share my progress. Please bear in mind I'm a cmake newbie, and I'm not sure if what I'm doing conforms to best practices, and the example code I provide might have mistakes. It can also definitely be significantly improved - but I believe is sufficient to get someone off the ground, if they're having the same issue.
For my use case, I wanted the external libraries to be built at cmake configure time, instead of build time. To that effect, I used execute_process to configure and build the external libraries. This is the function I created for that effect:
function(fetch_and_build_lib)
set(options "")
set(oneValueArgs LIB_NAME GIT_REPOSITORY GIT_TAG PACKAGE_NAME)
set(multiValueArgs CONFIG_ARGS CONFIG_TYPES BUILD_ARGS COMPONENTS)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT DEFINED ARG_LIB_NAME)
message(FATAL_ERROR "fetch_and_build_lib called without LIB_NAME")
endif()
if (NOT DEFINED ARG_GIT_REPOSITORY)
message(FATAL_ERROR "fetch_and_build_lib called without GIT_REPOSITORY")
endif()
if (NOT DEFINED ARG_GIT_TAG)
message(FATAL_ERROR "fetch_and_build_lib called without GIT_TAG")
endif()
if (DEFINED ARG_PACKAGE_NAME)
set(PACKAGE_NAME ${ARG_PACKAGE_NAME})
else()
set(PACKAGE_NAME ${ARG_LIB_NAME})
endif()
include(FetchContent)
FetchContent_Declare(${ARG_LIB_NAME}
GIT_REPOSITORY "${ARG_GIT_REPOSITORY}"
GIT_TAG "${ARG_GIT_TAG}"
)
FetchContent_GetProperties(${ARG_LIB_NAME}
POPULATED LIB_POPULATED
)
message(STATUS "Checking if ${ARG_LIB_NAME} is populated: ${LIB_POPULATED}")
if (NOT LIB_POPULATED})
message(STATUS "Populating ${ARG_LIB_NAME}...")
FetchContent_Populate(${ARG_LIB_NAME})
set(LIB_BINARY_DIR ${${ARG_LIB_NAME}_BINARY_DIR})
set(LIB_SOURCE_DIR ${${ARG_LIB_NAME}_SOURCE_DIR})
message(STATUS "Configuring ${ARG_LIB_NAME}...")
execute_process(
COMMAND ${CMAKE_COMMAND}
"-S ${LIB_SOURCE_DIR}"
"-B ${LIB_BINARY_DIR}"
"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}"
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
${ARG_CONFIG_ARGS}
WORKING_DIRECTORY "${LIB_BINARY_DIR}"
COMMAND_ECHO STDOUT
RESULT_VARIABLE result_config
)
if(result_config)
message(FATAL_ERROR "Failed to config ${ARG_LIB_NAME} exited with result: ${result_config}")
else()
message(STATUS "${ARG_LIB_NAME} configuring complete!")
endif()
foreach(config_type IN LISTS ARG_CONFIG_TYPES)
set(${ARG_LIB_NAME}_CONFIG_TYPE "${config_type}" CACHE INTERNAL "Config/build type for ${ARG_LIB_NAME}")
message(STATUS "Building ${ARG_LIB_NAME}... with CONFIG: ${config_type}")
execute_process(
COMMAND ${CMAKE_COMMAND}
--build "${LIB_BINARY_DIR}"
--config "${config_type}"
WORKING_DIRECTORY "${LIB_BINARY_DIR}"
COMMAND_ECHO STDOUT
${ARG_BUILD_ARGS}
RESULT_VARIABLE result_build
)
endforeach()
if(result_build)
message(FATAL_ERROR "Failed to build ${ARG_LIB_NAME} with result: ${result_build}")
else()
message(STATUS "${ARG_LIB_NAME} build complete!")
endif()
endif()
if (DEFINED ARG_COMPONENTS)
find_package(${PACKAGE_NAME} REQUIRED
COMPONENTS ${ARG_COMPONENTS}
PATHS ${LIB_BINARY_DIR}
NO_DEFAULT_PATH
)
else()
find_package(${PACKAGE_NAME} REQUIRED
PATHS ${LIB_BINARY_DIR}
NO_DEFAULT_PATH
)
endif()
endfunction()
What it does is as follows:
Downloads from git a repository with a specific tag
Configures the build system of the downloaded library
Builds the library as part of the main project configure stage
Makes sure that FindPackage can discover the newly built library
Then, in my CMakeLists.txt for my project, I use it like so:
add_executable(main main.cpp)
fetch_and_build_lib(
LIB_NAME "mylib"
GIT_REPOSITORY "https://github.com/myRepo/myLib.git"
GIT_TAG "1.2.3"
CONFIG_ARGS "-DMYLIB_DOC=OFF;-DMYLIB_TEST=OFF"
CONFIG_TYPES "debug, release"
PACKAGE_NAME "MyLIB"
)
find_package(MyLIB REQUIRED)
#Use Release version of library for Debug, MinSizeRel, RelWithDebInfo build types:
set_target_properties(MyLIB PROPERTIES
MAP_IMPORTED_CONFIG_MINSIZEREL Release
MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release
MAP_IMPORTED_CONFIG_DEBUG Release
)
target_link_libraries(main myLib::myLib)
This invokes the previously defined function that downloads, configures and builds the library. It assumes that the library correctly exports its targets, and then remaps the imported configs to the current project config.
This is what actually ends up linking the debug lib with the release executable.
CMake documentation for MAP_IMPORTED_CONFIG_
Mandatory disclaimer:
Mixing release libs and debug executables is filled with pitfalls, both for shared and static libraries. I'm not discussing whether this is a good idea or not, or what the pitfalls are. Be sure you are aware of the limitations and what is safe and what is not safe to do when doing such a mix.
I'm trying to use CLion to create a SDL2 project.
The problem is that the SDL headers can't be found when using #include's.
My CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8.4)
project(ChickenShooter)
set(SDL2_INCLUDE_DIR C:/SDL/SDL2-2.0.3/include)
set(SDL2_LIBRARY C:/SDL/SDL2-2.0.3/lib/x64)
include_directories(${SDL2_INCLUDE_DIR})
set(SOURCE_FILES main.cpp)
add_executable(ChickenShooter ${SOURCE_FILES})
target_link_libraries(ChickenShooter ${SDL2_LIBRARY})
My test main.cpp:
#include <iostream>
#include "SDL.h" /* This one can't be found */
int main(){
if (SDL_Init(SDL_INIT_VIDEO) != 0){
std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1;
}
SDL_Quit();
return 0;
}
Thank you for any help you could give me.
Edit:
I'm using Windows and CLion is configured to use cygwin64.
This blog post shows how you can do it: Using SDL2 with CMake
On Linux you can use a recent CMake (e.g. version 3.7) and using SDL2 works out of the box.
cmake_minimum_required(VERSION 3.7)
project(SDL2Test)
find_package(SDL2 REQUIRED)
include_directories(SDL2Test ${SDL2_INCLUDE_DIRS})
add_executable(SDL2Test Main.cpp)
target_link_libraries(SDL2Test ${SDL2_LIBRARIES})
Under Windows you can download the SDL2 development package, extract it somewhere and then create a sdl-config.cmake file in the extracted location with the following content:
set(SDL2_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include")
# Support both 32 and 64 bit builds
if (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
set(SDL2_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2main.lib")
else ()
set(SDL2_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2main.lib")
endif ()
string(STRIP "${SDL2_LIBRARIES}" SDL2_LIBRARIES)
When you now configure inside the CMake-GUI application there will be a SDL2_DIR variable. You have to point it to the SDL2 directory where you extracted the dev package and reconfigure then everything should work.
You can then include SDL2 headers by just writing #include "SDL.h".
Don't set the path to SDL2 by hand. Use the proper find command which uses FindSDL. Should look like:
find_file(SDL2_INCLUDE_DIR NAME SDL.h HINTS SDL2)
find_library(SDL2_LIBRARY NAME SDL2)
add_executable(ChickenShooter main.cpp)
target_include_directories(ChickenShooter ${SDL2_INCLUDE_DIR})
target_link_libraries(ChickenShooter ${SDL2_LIBRARY})
If SDL2 is not found, you have to add the path to SDL2 to CMAKE_PREFIX_PATH, that's the place where CMake looks for installed software.
If you can use Pkg-config, its use might be easier, see How to use SDL2 and SDL_image with cmake
If you feel more comfortable to use a FindSDL2.cmake file similar to FindSDL.cmake provided by CMake, see https://brendanwhitfield.wordpress.com/2015/02/26/using-cmake-with-sdl2/
You can also pull in the SDL source repository as a submodule and build/link it statically along with your main program via add_subdirectory() and target_link_libraries():
cmake_minimum_required( VERSION 3.18.0 )
project( sdl2-demo )
set( SDL_STATIC ON CACHE BOOL "" FORCE )
set( SDL_SHARED OFF CACHE BOOL "" FORCE )
# 'external/sdl' should point at a SDL
# repo clone or extracted release tarball
add_subdirectory( external/sdl )
add_executable(
${CMAKE_PROJECT_NAME}
"src/main.cpp"
)
target_link_libraries( ${CMAKE_PROJECT_NAME} SDL2main SDL2-static )
(At least as of the release-2.0.9 tag, possibly earlier.)
I recently discovered the latest version of SDL2 (version 2.0.12) now comes with all the required CMake config/install scripts, so there's no need to use FindSDL anymore.
I downloaded the SDL source from https://www.libsdl.org/download-2.0.php then from the root folder ran...
cmake -S . -B build/debug -G Ninja -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_BUILD_TYPE=Debug
cmake --build build/debug --target install
This will build and install the debug version of the library, you can then also run...
cmake -S . -B build/release -G Ninja -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_BUILD_TYPE=Release
cmake --build build/release --target install
Which will build and install the release version of the library (and because the SDL CMake script uses DEBUG_POSTFIX the release version of the library won't overwrite the debug one as the debug versions all have 'd' appended to their name).
In your CMakeLists.txt file you can then simply do this:
find_package(SDL2 REQUIRED)
add_executable(${PROJECT_NAME} ...)
target_link_libraries(
${PROJECT_NAME} PRIVATE
SDL2::SDL2
SDL2::SDL2main
You'll need to tell your application where to find the SDL install folder if you used a custom location as I've done in the example. To do this from the root folder of your app run:
cmake -S . -B build/debug -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=</absolute/path/to/install/dir>
cmake --build build/debug
Note: You can use $(pwd) (*nix/macOS) or %cd% (Windows) to create a hybrid relative path which can be very useful.
You can omit both DCMAKE_INSTALL_PREFIX and DCMAKE_PREFIX_PATH if you want to install SDL to the default system location.
In the examples I've opted to use the Ninja generator as it is consistent across macOS/Windows - it can be used with MSVC/Visual Studio, just make sure you run this (path may differ slightly depending on year/version) to add Ninja to your path.
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat
Update:
One other thing I remembered which is useful on Windows is the ability to copy the SDL .dll file into the application binary directory, this can be achieved like so:
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:SDL2::SDL2>
$<TARGET_FILE_DIR:${PROJECT_NAME}>
VERBATIM)
endif()
Using the SDL2 CMake module that I developed, you can integrate the SDL2 library easily in a modern and portable approach.
You should just copy the module in cmake/sdl2 (Or just clone the modules repo) in your project:
git clone https://github.com/aminosbh/sdl2-cmake-modules cmake/sdl2
Then add the following lines in your CMakeLists.txt:
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2)
find_package(SDL2 REQUIRED)
target_link_libraries(${PROJECT_NAME} SDL2::Main)
Note: If CMake didn't find the SDL2 library (in Windows), we can specify the CMake option SDL2_PATH as follows:
cmake .. -DSDL2_PATH="/path/to/sdl2"
For more details, please read the README.md file.
The SDL2 CMake modules support other related libraries : SDL2_image, SDL2_ttf, SDL2_mixer, SDL2_net and SDL2_gfx.
You can find a list of examples/samples and projects that uses these modules here : https://github.com/aminosbh/sdl-samples-and-projects
With the compiled version of SDL2-2.0.9 with MinGW-w64 in Windows, the following configuration works for me:
find_package(SDL2 REQUIRED)
add_executable(sdl-test ${SOURCES})
target_link_libraries(sdl-test
mingw32
SDL2::SDL2main
SDL2::SDL2
)
A longer explanation
By reading SDL2Targets.cmake file, I've learned that SDL2 is providing several targets:
SDL2::SDL2main (lib/libSDL2main.a)
SDL2::SDL2 (lib/libSDL2.dll.a)
SDL2::SDL2-static (lib/libSDL2-static.a)
Each of them has INTERFACE_INCLUDE_DIRECTORIES defined, which means we don't need to manually specify include_directories for SDL2.
But by only adding SDL2::SDL2main and SDL2::SDL2 as target_link_libraries is not enough. The g++ compiler might be complaining about "undefined reference to `WinMain'".
By inspecting the compiler options, I found that the SDL2 libraries are added before -lmingw32 option. In order to make the -lmingw32 option comes before SDL2 libraries, we have to also specify mingw32 as the first target_link_libraries. Which will make this configuration working.
The command that I have used for building it is:
$ mkdir build && cd build && cmake .. -G"MinGW Makefiles" && cmake --build .
The only small problem here is in the finally generated compiler options, the -lmingw32 option is duplicated. But since it doesn't affect the linking process, I've ignored it for now.
On Linux, in Clion, this works:
cmake_minimum_required(VERSION 3.20)
project(first_game)
set(CMAKE_CXX_STANDARD 14)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES})
You don't seems to have a CMake error whike generating your make file. But I think your problem is, the SDL Header are located in a subfolder named "SDL2".
Change your CMakeLists.txt to include
C:/SDL/SDL2-2.0.3/include/SDL2
Instead of
C:/SDL/SDL2-2.0.3/include
I had the same problem and none of the other solutions worked.
But I finally got it working by following this solution : How to properly link libraries with cmake?
In a nutshell, the problem was that the SDL2 library was not linked properly in my CMakeLists.txt. And by writing this into the file, it worked (more explainations in the other thread) :
project (MyProgramExecBlaBla) #not sure whether this should be the same name of the executable, but I always see that "convention"
cmake_minimum_required(VERSION 2.8)
ADD_LIBRARY(LibsModule
file1.cpp
file2.cpp
)
target_link_libraries(LibsModule -lpthread)
target_link_libraries(LibsModule liblapack.a)
target_link_libraries(LibsModule -L/home/user/libs/somelibpath/)
ADD_EXECUTABLE(MyProgramExecBlaBla main.cpp)
target_link_libraries(MyProgramExecBlaBla LibsModule)
Highlighting the steps of how I was able to eventually accomplish this using the FindSDL2.cmake module:
Download SDL2-devel-2.0.9-VC.zip (or whatever version is out after this answer is posted) under the Development Libraries section of the downloads page.
Extract the zip folder and you should see a folder similar to "SDL2-2.0.9". Paste this folder in your C:\Program Files(x86)\ directory.
Copy the FindSDL2.cmake module and place it in a new "cmake" directory within your project. I found a FindSDL2.cmake file in the answer referenced in the Accepted Answer: https://brendanwhitfield.wordpress.com/2015/02/26/using-cmake-with-sdl2/
Find the SET(SDL2_SEARCH_PATHS line in the FindSDL2.cmake and add your copied development directory for SDL2 as a new line: "/Program Files (x86)/SDL2-2.0.9" # Windows
Within my CMakeLists.txt, add this line: set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
After this, running CMake worked for me. I'm including the rest of my CMakeLists just in case it further clarifies anything I may have left out:
cmake_minimum_required(VERSION 2.8.4)
project(Test_Project)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# includes cmake/FindSDL2.cmake
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
set(SOURCE_FILES src/main.cpp src/test.cpp)
add_executable(test ${SOURCE_FILES})
# The two lines below have been removed to run on my Windows machine
#INCLUDE(FindPkgConfig)
#PKG_SEARCH_MODULE(SDL2 REQUIRED sdl2)
find_package(SDL2 REQUIRED)
INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(chip8 ${SDL2_LIBRARY})
Hope this helps somebody in the near future.
by the time of my answer, SDL2 is provided with sdl2-config executable (as I understand, developers call him "experimental").
After "make install" of SDL2 you can try calling it from terminal with
sdl2-config --cflags --libs to see what it outputs.
And then you can add call to it in your makefile:
set(PROJECT_NAME SomeProject)
project(${PROJECT_NAME})
execute_process(COMMAND /usr/local/bin/sdl2-config --libs RESULT_VARIABLE CMD_RES OUTPUT_VARIABLE SDL2_CFLAGS_LIBS ERROR_VARIABLE ERR_VAR OUTPUT_STRIP_TRAILING_WHITESPACE)
message("SDL2_CFLAGS_LIBS=${SDL2_CFLAGS_LIBS}; CMD_RES=${CMD_RES}; ERR_VAR=${ERR_VAR}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${SDL2_CFLAGS_LIBS}")
set(SOURCE_FILES main.cpp)
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
Here I have a problem - if I only put an executable name without path like
execute_process(COMMAND sdl2-config --libs <...>
I get error "No such file", i.e. cmake does not search in current path and I don't know how to write it properly by now.
One more notice: in my makefile I do not user --cflags option, because cmake finds includes correctly and I do not need to specify them explicitly.
For your information, I was able to successfully cmake and compile SDL2_ttf while linking to SDL2 source code.
At first I was getting errors due to cmake not being able to locate SDL2, even though it was specified in cmake using the SLD2_DIR variable in cmake.
It seems that for some reason cmaking SDL2 fails to create the SDL2Targets.cmake file which is searched for by SDL2_ttf
If this is the case for you, get the SDL2Targets.cmake file from https://bugs.archlinux.org/task/57972 and modify the file like so:
You can remove the following lines:
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
if(_IMPORT_PREFIX STREQUAL "/")
set(_IMPORT_PREFIX "")
endif()
and add this one:
set(_IMPORT_PREFIX "C:/SDL2-2.0.12")
Obviously change the filepath to the place you unpacked the SDL2 source code
I'm not sure if this is exactly your issue, but there it is.
I'm building a project in Cpp that will communicate with my Java apps via rabbitmq and post updates to twitter. I'm using a few libraries from github
rabbitmq-c
Rabbit installed to /usr/local/lib64
jansson - json library
I installed this a while back for another project, went to /usr/local/lib
twitcurl - C lib for Twitter API
Got installed to /usr/local/lib
If it matters, I'm using CLion as my IDE, which displays jansson and rabbit under auto-complete when defining includes - so that's picking the libs off my system somehow
e.g.
#include <jansson.h>
#include <amqp.h>
I link them using the target_link_libraries(name libs...) and I see output saying
build$ cmake ..
CMake Error at CMakeLists.txt:30 (target_link_libraries):
Cannot specify link libraries for target "twitcurl" which is not built by
this project.
I set LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64
I try to set the CMAKE_LIBRARY_PATH to include usr/local/lib and lib64 but doesn't seem to have any effect. Here's my CMakeLists.txt file
#
# This is a CMake makefile. You can find the cmake utility and
# information about it at http://www.cmake.org
#
cmake_minimum_required(VERSION 2.6)
set(PROJECT_NAME twitterUpdater)
set(SOURCE_FILES main.cpp)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} "/usr/local/lib"
"/usr/local/lib64")
project(${PROJECT_NAME})
find_package(X11 REQUIRED)
find_package(OpenCV REQUIRED)
IF (X11_FOUND)
INCLUDE_DIRECTORIES(${X11_INCLUDE_DIR})
LINK_LIBRARIES(${X11_LIBRARIES})
ENDIF ( X11_FOUND )
IF (OpenCV_FOUND)
include_directories(${OpenCV_INCLUDE_DIRS})
link_libraries(${OpenCV_LIBS})
ENDIF(OpenCV_FOUND)
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
target_link_libraries(${project_name} twitcurl jansson rabbitmq)
What's confusing me is another project I have uses jansson by simply adding it here TARGET_LINK_LIBRARIES(${project_name} dlib jansson)
What did I miss?? Thanks
CMake variables are case sensitive, thus the variable ${project_name} results in an empty string. Use ${PROJECT_NAME} instead, i.e.:
target_link_libraries(${PROJECT_NAME} twitcurl jansson rabbitmq)
Running CMake with the flag --warn-uninitialized helps you detect mistakes like this.
I've compiled a C++ static library by using CMake as my building tool and I want to link it to my iOS app.
I created a simple 'Empty' application in Xcode and linked my library called libengine.a to it.
I tried to compile my iOS project and the linker gave me this warning:
ignoring file /Users/.../build/engine/libengine.a,
file was built for archive which is not the architecture being linked (i386):
/Users/.../build/engine/libengine.a
As I understand it, I need to compile my library for ARM processors. The problem is I don't know how.
I think CMake really lacks good tutorials.
Anyways, my CMake scripts are attached below.
Any help would be greatly appreciated.
Thanks, Tal.
Here is my main CMake script:
cmake_minimum_required(VERSION 2.8)
project(movie-night)
if (DEFINED PLATFORM)
include(toolchains/ios.cmake)
endif()
add_definitions(-Wall)
set(DEBUG)
if (DEFINED DEBUG)
add_definitions(-g)
endif()
if (DEFINED RELEASE)
add_definitions(-O3)
endif()
add_subdirectory(engine)
add_subdirectory(ui)
add_subdirectory(test)
Here is my toolchains/ios.cmake file:
set(CMAKE_SYSTEM_NAME Darwin)
set(CMAKE_SYSTEM_PROCESSOR arm)
Just use this toolchain file: http://code.google.com/p/ios-cmake/ and use it as
cmake -DCMAKE_TOOLCHAIN_FILE=path_to_your_toolchain_file
Then, in CMakeLists.txt:
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch armv7")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch armv7")
By following cmake-toolchains documentation I did like below:
cmake -G Xcode -B build \
-DCMAKE_SYSTEM_NAME=iOS \
-DCMAKE_Swift_COMPILER_FORCED=true \
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0
Note: That assignment CMAKE_OSX_DEPLOYMENT_TARGET=11.0 is not a mistake when targeting iOS.
I've been using an updated version of the iOS CMake toolchain for quite a while: https://github.com/leetal/ios-cmake
This creates an Xcode project which you can drag into an existing iOS project if necessary.
I wrote a blog post with more details: https://blog.tomtasche.at/2019/05/how-to-include-cmake-project-in-xcode.html
There is a second version of iOS.cmake located at:
https://ceres-solver.googlesource.com
Note: You may find that both versions of iOS.cmake will only build x86 versions of your project. CMake now sets CMAKE_OSX_SYSROOT to the (latest) Mac OS X SDK available on your system. You can fix this by modifying your copy of iOS.cmake to always set CMAKE_OSX_SYSROOT. You can do this by commenting out a couple of lines your copy of iOS.cmake:
# -- Under CMake 3.4.2, CMAKE_OSX_SYSROOT is automatically defined to point to the latest Mac OS X SDK. --
# -- So, the iOS SDK is never found. Grab the correct CMAKE_OS_SYSROOT and ignore any prior setting. --
# If user did not specify the SDK root to use, then query xcodebuild for it.
# if (NOT CMAKE_OSX_SYSROOT)
execute_process(COMMAND xcodebuild -version -sdk ${XCODE_IOS_PLATFORM} Path
OUTPUT_VARIABLE CMAKE_OSX_SYSROOT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
message (STATUS "Using SDK: ${CMAKE_OSX_SYSROOT} for platform: ${IOS_PLATFORM}")
message (STATUS "be sure the previous line points to the correct SDK")
# endif ( )