This question already has answers here:
How to set CMake to have root directory in include paths
(2 answers)
CMake: C++ include relative to base directory
(1 answer)
How to avoid relative paths in include folder
(2 answers)
Closed 4 days ago.
Project Sturcture
I am learning how to build a C++/cmake template. The structure looks like:
.
├── CMakeLists.txt
├── README.md
└── src
├── CMakeLists.txt
└── geometry
├── CMakeLists.txt
└── line
├── CMakeLists.txt
├── line.cpp
└── line.h
Every CMakeLists.txt looks like the following:
./CMakeLists.txt:
cmake_minimum_required(VERSION 3.20.0)
project(cmake_template VERSION 0.0.1 LANGUAGES CXX)
add_subdirectory(src)
./src/CMakeLists.txt:
add_subdirectory(geometry)
./src/geometry/CMakeLists.txt:
add_subdirectory(line)
./src/geometry/line/CMakeLists.txt:
add_library(line line.cpp)
target_include_directories(line PUBLIC .)
And the implementation looks like:
line.h:
#include <string>
class Line{
private:
std::string name;
public:
Line(std::string);
virtual void display();
};
line.cpp:
#include "src/geometry/line/line.h"
#include <iostream>
Line::Line(std::string name):name(name){}
void Line::display(){
std::cout << "A line with name " << this->name << "\n";
}
Compile and error
The command I run is cmake -S . -B build && cmake --build build/, and it shows the error:
[1/2] Building CXX object src/geometry/line/CMakeFiles/line.dir/line.cpp.o
FAILED: src/geometry/line/CMakeFiles/line.dir/line.cpp.o
/usr/bin/clang++ -I../src/geometry/line/. -g -MD -MT src/geometry/line/CMakeFiles/line.dir/line.cpp.o -MF src/geometry/line/CMakeFiles/line.dir/line.cpp.o.d -o src/geometry/line/CMakeFiles/line.dir/line.cpp.o -c ../src/geometry/line/line.cpp
../src/geometry/line/line.cpp:1:10: fatal error: 'src/geometry/line/line.h' file not found
#include "src/geometry/line/line.h"
^~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
ninja: build stopped: subcommand failed.
The code can compile if I change to #include "line.h"
My questions are:
How can I include the absolute path without change the current structure? I.e., I don't want to change CMakeLists.txt in the upper level, only ./src/geometry/line/CMakeLists.txt can be changed.
How can I use <> to include the absolute path? I.e., #include <src/geometry/line/line.h>? The reason of using <> is from the Canonical CPP Project Structure.
Related
I've noticed that when I try to build a c++ project with some flags, e.g., "-Wall -Wmissing-include-dirs", bazel gives a missing include directory.
The project structure is the following
root
├── src
│ ├── CMakeLists.txt
│ ├── BUILD.bazel
│ └── main.cpp
├── CMakeLists.txt
├── README.md
└── WORKSPACE
src/main.cpp
#include<iostream>
int main()
{
std::cout << "Compiled\n";
return 0;
}
src/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(main)
set(CMAKE_CXX_FLAGS "-Wall -Wmissing-include-dirs -Ipath/that/doesnt/exist")
set(SOURCES main.cpp)
add_executable( main ${SOURCES})
src/BUILD.bazel
cc_binary(
name = "main",
srcs = ["main.cpp"],
copts = ["-Ipath/that/doesnt/exist -Wall -Wmissing-include-dirs"],
)
When compiling main.cpp with cmake (mkdir build && cd build && cmake .. && make), I see an expected warning for path/that/doesnt/exist (since it doesn't exist), e.g.,
cc1plus: warning: path/that/doesnt/exist: No such file or directory [-Wmissing-include-dirs]
but when I compile with bazel (bazel build //src:main) I get an additional missing include
cc1plus: warning: path/that/doesnt/exist: No such file or directory [-Wmissing-include-dirs]
cc1plus: warning: bazel-out/k8-fastbuild/bin/external/bazel_tools: No such file or directory [-Wmissing-include-dirs]
Question 1: Where does bazel-out/k8-fastbuild/bin/external/bazel_tools come from and why is it missing?
Question 2: How do I write my bazel build so that this warning doesn't appear?
If I were to remove the inclusion of path/that/doesnt/exist from the cmake and bazel builds, and compile with flags -Wall -Werror -Wmissing-include-dirs, then the cmake build succeeds, but the bazel one fails.
Minimum working example found here: https://github.com/btk-learning/learn-to-build-cmake-bazel
This question already has an answer here:
LNK2019 - unresolved external symbol - C++ - SDL2 Library [duplicate]
(1 answer)
Closed 2 years ago.
I'm having problems linking SDL2 to my project, which probably are because I'm new to CMake, and I don't fully know how to create new projects with it. Every time and method I've tried to fix this problem has resulted in one of the following problems:
"Cannot locate WinMain#16"
Multiple undefined reference errors
undefined reference to `SDL_main`
I have attempted to use find_package, linking directly to files, linking to library files, following tutorials (for example https://trenki2.github.io/blog/2017/06/02/using-sdl2-with-cmake/), searching for answers (most of them talk about using find_package which I can't get to work) (for example Using SDL2 with CMake).
Project dependencies
SDL 2.0.12, CMake 3.17.0, 7-Zip, mingw32-make, wget
The project is supposed to be cross-platform, but the main devenv is Windows 10. All scripts are run from the root folder "vivaria".
Project structure
vivaria/
├── build/
│ └── [cmake build files]
├── buildtools/
│ └── SDL2/SDL2-2.0.12/lib/x86 (and x64)
│ ├── SDL2.lib
│ └── SDL2main.libs
├── deploy/
├── resources/
├── scripts/
│ ├── build_windows_debug_x86.bat
│ └── install_buildtools_windows.bat
└── src/
├── CMakeLists.txt
└── vivaria.cpp
build_windows_debug_x86.bat
cmake -G "MinGW Makefiles" -S .\src\ -B .\build\ -DCMAKE_BUILD_TYPE=Debug
cd .\build\
mingw32-make
vivaria.cpp
#include <iostream>
#include "SDL.h"
int main() {
if (SDL_Init(SDL_INIT_VIDEO) != 0){
std::cout << "Hello world" << std::endl;
return 1;
}
SDL_Quit();
return 0;
}
CMakeLists.txt: undefined reference to SDL_main
cmake_minimum_required(VERSION 3.17)
project(Vivaria VERSION 1.0.0)
set(CMAKE_CXX_GLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -lmingw32")
# set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../buildtools")
set(SDL2_DIR "${INCLUDE_DIR}/SDL2/SDL2-2.0.12")
set(SDL2_INCLUDE_DIRS "${SDL2_DIR}/include")
set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/../deploy")
# Support both 32 and 64 bit builds
if (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
set(SDL2_LIBRARIES "${SDL2_DIR}/lib/x64/SDL2main.lib;${SDL2_DIR}/lib/x64/SDL2.lib")
else ()
set(SDL2_LIBRARIES "${SDL2_DIR}/lib/x86/SDL2main.lib;${SDL2_DIR}/lib/x86/SDL2.lib")
endif ()
# link dependencies
include_directories(${SDL2_INCLUDE_DIRS})
link_directories(${SDL2_LIBRARIES})
# Project files and linking
set(SOURCES vivaria.cpp)
add_executable(${PROJECT_NAME} vivaria.cpp)
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES})
Console output
F:\Koodit\Vivaria\build>mingw32-make
[ 50%] Linking CXX executable Vivaria.exe
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: F:/Koodit/Vivaria/src/../buildtools/SDL2/SDL2-2.0.12/lib/x86/SDL2main.lib(Win32/Release/SDL_windows_main.obj):(.text[_main_getcmdline]+0xd1): undefined reference to `SDL_main'
collect2.exe: error: ld returned 1 exit status
CMakeFiles\Vivaria.dir\build.make:104: recipe for target 'Vivaria.exe' failed
mingw32-make[2]: *** [Vivaria.exe] Error 1
CMakeFiles\Makefile2:91: recipe for target 'CMakeFiles/Vivaria.dir/all' failed
mingw32-make[1]: *** [CMakeFiles/Vivaria.dir/all] Error 2
Makefile:99: recipe for target 'all' failed
mingw32-make: *** [all] Error 2
install_buildtools_windows.bat
set SDL2=SDL2-devel-2.0.12-VC.zip
set DOWNLOAD_DIR=%cd%\buildtools
set OUTPUT_DIR=%cd%\buildtools\SDL2
wget "https://libsdl.org/release/%SDL2%" -P "%DOWNLOAD_DIR%"
7z x "%DOWNLOAD_DIR%\%SDL2%" -y -o%OUTPUT_DIR%
del /F /Q %DOWNLOAD_DIR%\%SDL2%
As you can see I'm still "a bit" new to CMake, but I'm trying to learn.
Oof. Everything about your CMakeLists is wrong.
Never set CMAKE_CXX_FLAGS in your CMakeLists.txt
Never use include_directories or link_directories
Never use target_link_libraries without a visibility specifier.
Never set paths to libs manually.
Don't set your target names equal to the project name. It's pointless complexity and bad style.
This is all you need:
cmake_minimum_required(VERSION 3.16)
project(Vivaria VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(SDL2 REQUIRED)
add_executable(Vivaria vivaria.cpp)
target_link_libraries(Vivaria PRIVATE SDL2::SDL2)
Your code also has an error. You need to add this line to the top of your file
#define SDL_MAIN_HANDLED
First, use vcpkg to install SDL2. Then, with the above changes, this compiles and runs for me using the commands:
> mkdir build
> cd build
> cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=X:/path/to/vcpkg.cmake ..
Thanks to Tsyvarev!
Setting the macro SDL_MAIN_HANDLED in the source file fixed the problem.
#define SDL_MAIN_HANDLED // insert this
#include <iostream>
#include "SDL.h"
int main() {
if (SDL_Init(SDL_INIT_VIDEO) != 0){
std::cout << "Hello world" << std::endl;
return 1;
}
std::cout << "Hello world 2" << std::endl;
SDL_Quit();
return 0;
}
I have a simple c++ project which includes eigen. I'm able to compile the project on my own machine but having trouble to compile it to webassembly with emscripten.
Project structure:
.
├── CMakeLists.txt
├── include
│ └── HelloWasm
│ └── my_lib.h
└── src
├── main.cpp
└── my_lib.cpp
File contents:
CMakeLists.txt
cmake_minimum_required( VERSION 3.0 )
project( HelloWasm )
# flags
# include files
include_directories( ./include .include/HelloWasm ./src )
# target
add_executable( HelloWasm ./src/main.cpp ./src/my_lib.cpp )
# 3rd party libs
find_package(Eigen3 REQUIRED NO_MODULE)
include_directories(${EIGEN3_INCLUDE_DIR})
include/HelloWasm/my_lib.h
#include <iostream>
#include <Eigen/Dense>
using namespace std;
using Eigen::MatrixXd;
class MyLib
{
private:
protected:
public:
MyLib()
{
}
~MyLib()
{
}
void eigen_test();
};
src/main.cpp
#include <iostream>
#include "HelloWasm/my_lib.h"
using namespace std;
int main()
{
MyLib my_lib;
my_lib.eigen_test();
}
src/my_lib.cpp
#include "HelloWasm/my_lib.h"
void MyLib::eigen_test()
{
MatrixXd m(2, 2);
m(0, 0) = 3;
m(1, 0) = 2.5;
m(0, 1) = -1;
m(1, 1) = m(1, 0) + m(0, 1);
cout << '\n'
<< m << endl;
}
Compiling the project successfully locally:
mkdir build && cd build
cmake ..
make
➜ ./HelloWasm
3 -1
2.5 1.5
Errors when trying to compile to webassemply
(I tried following the steps provided in the emscripten docs)
mkdir build && cd build
cmake ..
emcmake cmake ..
output:
configure: cmake .. -DCMAKE_TOOLCHAIN_FILE=/Users/me/programming/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_CROSSCOMPILING_EMULATOR="/Users/me/programming/emsdk/node/12.9.1_64bit/bin/node"
-- Configuring done
-- Generating done
CMake Warning:
Manually-specified variables were not used by the project:
CMAKE_TOOLCHAIN_FILE
-- Build files have been written to: /Users/me/programming/sandbox/cpp_sandbox/so_question_project/build
Now running make:
➜ emmake make
make: make
Scanning dependencies of target HelloWasm
[ 33%] Building CXX object CMakeFiles/HelloWasm.dir/src/main.cpp.o
[ 66%] Building CXX object CMakeFiles/HelloWasm.dir/src/my_lib.cpp.o
[100%] Linking CXX executable HelloWasm
[100%] Built target HelloWasm
Obviously that did not create a .wasm file...
Doing the following:
em++ CMakeFiles/HelloWasm.dir/src/main.cpp.o CMakeFiles/HelloWasm.dir/src/my_lib.cpp.o -o helloWasm.js
output:
em++: warning: CMakeFiles/HelloWasm.dir/src/main.cpp.o is not a valid input file [-Winvalid-input]
em++: warning: CMakeFiles/HelloWasm.dir/src/my_lib.cpp.o is not a valid input file [-Winvalid-input]
em++: error: no input files
note that input files without a known suffix are ignored, make sure your input files end with one of: ('.c', '.i', '.cpp', '.cxx', '.cc', '.c++', '.CPP', '.CXX', '.C', '.CC', '.C++', '.ii', '.m', '.mi', '.mm', '.mii', '/dev/null', '.bc', '.o', '.obj', '.lo', '.dylib', '.so', '.a', '.ll', '.h', '.hxx', '.hpp', '.hh', '.H', '.HXX', '.HPP', '.HH')
What am I missing here...?
Change:
em++ CMakeFiles/HelloWasm.dir/src/main.cpp.o CMakeFiles/HelloWasm.dir/src/my_lib.cpp.o -o helloWasm.js
To:
em++ CMakeFiles/HelloWasm.dir/src/main.cpp.o CMakeFiles/HelloWasm.dir/src/my_lib.cpp -o helloWasm.js
Emscripten can't compile my_lib.cpp.o because it's already compiled to machine code (it's an object file). You have to use a .cpp file, not .cpp.o.
I have a suggestion for you.
First, you can put your lib files (my_lib.h and my_lib.cpp) on same directory.
Second, you can create a folder for your applications (HelloWasm) and put your executable codes (main.cpp) in this folder.
Finally, you can create CMakeLists.txt file for each folder.
This is your new directory tree:
.
├── CMakeLists.txt
├── Applications
│ └── HelloWasm
│ ├── CMakeLists.txt
│ └── main.cpp
└── Libraries
├── CMakeLists.txt
├── my_lib.h
└── my_lib.cpp
CMakeLists.txt (the first one) :
cmake_minimum_required(VERSION 3.0)
project(LIBRARYANDAPPS)
#This function is starting build and looking all folders.
function( start_build )
file( GLOB_RECURSE components "${CMAKE_SOURCE_DIR}/*/CMakeLists.txt" )
foreach( component ${components} )
get_filename_component( path ${component} PATH )
add_subdirectory( ${path} )
endforeach( )
endfunction( )
start_build()
CMakeLists.txt (for HelloWasm):
#You can add Emscripten flags like this.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
-std=c++11 --bind \
-s USE_WEBGL2=1 -s FULL_ES3=1 --memory-init-file 0")
find_package(Eigen3 REQUIRED NO_MODULE)
include_directories(${EIGEN3_INCLUDE_DIR})
include_directories("${CMAKE_SOURCE_DIR}/Libraries")
ADD_EXECUTABLE(HelloWasm main.cpp)
TARGET_LINK_LIBRARIES(HelloWasm MyLib Eigen)
CMakeLists.txt (for Library):
find_package(Eigen3 REQUIRED NO_MODULE)
include_directories(${EIGEN3_INCLUDE_DIR})
ADD_LIBRARY(MyLib STATIC my_lib.cpp my_lib.h)
TARGET_LINK_LIBRARIES(MyLib Eigen)
With these edits, you can create bigger projects, libraries. And I think more useful like this.
This question already has answers here:
Linking Rust application with a dynamic library not in the runtime linker search path
(3 answers)
How do I specify the linker path in Rust?
(3 answers)
Where should I place a static library so I can link it with a Rust program?
(3 answers)
Closed 2 years ago.
I have written a C++ library with a C wrapper and want to call those functions from my Rust project. However, when I try to link my Rust project to the C library, I get a long error with the note
note: /usr/bin/ld: cannot find -l../cpp_library/build/libdisplay.so
I have tried passing an absolute path to the library and received a similar error.
My combined project has the following directory and contents
├── cpp_library
│ ├── CMakeLists.txt
│ ├── include
│ │ └── display.h
│ └── src
│ ├── display.cpp
│ └── main.cpp
└── rust_project
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── src
└── main.rs
CMakeLists.txt
cmake_minimum_required(VERSION 3.1.0)
project(directory_test)
set(CMAKE_BUILD_TYPE Release)
#Bring the headers into the project
include_directories(include)
set(SOURCES "src/display.cpp")
#Generate the shared library from the sources
add_library(display SHARED ${SOURCES})
add_executable(display_test "src/main.cpp" ${SOURCES})
display.h
extern "C" {
void display();
}
display.cpp
#include <iostream>
#include "display.h"
void display(){
std::cout << "A C++ println" << std::endl;
}
main.cpp
#include "display.h"
int main() {
display();
}
build.rs
fn main() {
println!("cargo:rustc-link-search=../cpp_library/build/");
println!("cargo:rustc-link-lib=../cpp_library/build/libdisplay.so");
}
main.rs
extern {
fn display();
}
fn main() {
println!("Hello, world!");
unsafe {
display();
}
}
You can also see the project on Github.
I build the C library with
cd cpp_library
mkdir build
cd build
cmake ..
make
When I build the Rust project, I get the error shown above.
How am I supposed to link the C library to the Rust project?
Update 1
The posts How do I specify the linker path in Rust? and Where should I place a static library so I can link it with a Rust program? indicate how to add a directory to the library search path and link a library. I have tried these solutions, but still receive the error mentioned above.
Following the advice of Linking Rust application with a dynamic library not in the runtime linker search path, I compiled the rust project with
cargo rustc -- -C link-args='-Wl,-rpath,$ORIGIN/../../../cpp_library/build/'
but received the same error. I also tried passing an absolute path and received the same result.
I removed the redundant #[link(name = "display")], but this did not resolve the problem.
This question already has answers here:
Getting base name of the source file at compile time
(12 answers)
Closed 5 years ago.
I use __FILE__ during logging and debug my program. And when I use it, I get full source file path. So, I wanna write my own macro similar the macro __FILE__.
I looked for a solution, but I found nothing about it.
So, does it exist a CMake way to implement user macro, that will be generate something data similar the __FILE__ macro?
Just remove the path from __FILE__.
Possible solution:
#include <libgen.h>
#include <stdio.h>
#define LOG(message) \
do { \
char* filename = basename(__FILE__); \
printf("In %s: %s", filename, message); \
} while(false);
Example has to be extended by check of log level. And instead of a fixed message a variadic number of arguments should be used.
Note: This is for Unix. Assume your OS provides a similar function.
As I mentioned in a comment already, you can create a CMake function that loops over all source files of a given target and adds a macro definition to the compile flags for that source file.
function(add_filepath_macro target)
get_target_property(SOURCE_FILES ${target} SOURCES)
foreach (FILE_PATH IN LISTS SOURCE_FILES)
file(RELATIVE_PATH RELATIVE_FILE_PATH ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH})
set_property(SOURCE ${FILE_PATH} APPEND PROPERTY COMPILE_DEFINITIONS FILEPATH="${RELATIVE_FILE_PATH}")
endforeach()
endfunction()
This now defines the macro FILEPATH in every TU that is part of the target you pass to it.
There is one caveat though: In contrast to the regular __FILE__ macro you can't use the FILEPATH macro in header files, because including one such header in different translation units immediately causes an ODR violation and thus undefined behaviour.
Demo
Let's see how this works for a very small example. Start with the following project structure:
.
├── cmake
│ └── add_filepath_macro.cmake
├── CMakeLists.txt
├── lib
│ ├── a.cpp
│ ├── a.h
│ └── CMakeLists.txt
└── main.cpp
The cmake/add_filepath_macro.cmake file contains nothing but the code shown above.
The we have the library files
// lib/a.h
#pragma once
char const* foo();
// lib/a.cpp
#include "a.h"
char const* foo() {
return FILEPATH;
}
with corresponding CMakeLists.txt
# lib/CMakeLists.txt
add_library(liba
a.cpp
)
target_include_directories(liba PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_filepath_macro(liba)
as well as a small demo executable
// main.cpp
#include <iostream>
#include "a.h"
int main() {
std::cout << foo() << '\n' << FILEPATH << '\n';
}
and finally the project CMakeLists.txt
# CMakeLists.txt
# not tested with older version, but I guess it should work with anything > 3.0
cmake_minimum_required(VERSION 3.9)
project(cmake_filepath_macro_demo LANGUAGE CXX)
include(cmake/add_filepath_macro.cmake)
add_subdirectory(lib)
add_executable(demo
main.cpp
)
add_filepath_macro(demo)
Running the resulting executable then produces the following output
$ ./demo
lib/a.cpp
main.cpp