![]() |
怕老婆的大熊猫 · vcpkg_cmake_configure ...· 2 月前 · |
![]() |
旅行中的抽屉 · Ubuntu18.04系统下Opencv2. ...· 2 月前 · |
![]() |
温柔的小蝌蚪 · CMake笔记-Linux下安装CMake3 ...· 4 周前 · |
![]() |
无邪的弓箭 · GitHub - ...· 4 天前 · |
![]() |
骑白马的蚂蚁 · 时间库Moment.js ...· 6 月前 · |
![]() |
飞奔的口罩 · 厦门大学2025年硕士研究生招生考试初试科目 ...· 7 月前 · |
![]() |
想发财的葡萄酒 · 西藏自治区地质灾害信息_西藏自治区自然资源厅· 11 月前 · |
![]() |
千杯不醉的人字拖 · TensorFlow 2.6 ...· 1 年前 · |
![]() |
酷酷的金针菇 · 姐妹们推荐的言情 | 2020.11.6日 ...· 1 年前 · |
For your final project, you are going to use going to need to use some additional library. In order to do this, you'll need to understand the basics about C++ build process.
There are a multitude of open-source build systems for C++:
Make
, designed by
Stu Feldman
CMake
by Kitware
Buck
by Facebook
Bazel
by Google
Ninja
Also, most of the above C++ build system are also language agnostic, so they are just general purpose build systems.
We are going to be using
CMake
, since it is typically the standard for high
level build systems for C++ projects.
When you click the Green 🔨 button in CLion,
3
events occur (in this order):
Any statements starting with a
#
are called
preprocessor directives
. These
directives are typically used to organize projects (via
#include
), selectively
compile pieces of code (via
#define
, usually for platform reasons),
and to create macros (also via
#define
). There are other uses cases too, but
these are the main use cases you'll encounter.
An important topic is
#include
statements. In order to use other pieces of
code, your current code will need to known of the existence of other pieces of
code. A
header file
is a file that typically contains declarations of existing
code: in other words, they typically signify the existence of an implementation.
Including header files in your code will allow you to
compile
the code.
It is important to understand that the extension of your header file doesn't
matter since the compiler never sees the header file (think about what
#include
does) and won't try to process it.
Now, there are two ways to include a file. For example:
#include < nlohmann/json.hpp > #include " nlohmann/json.hpp "
The
<>
syntax means that the preprocessor will search for the specified
file from a list of system headers and user-specified include directories.
The
""
syntax will typically make the preprocessor start the search
relative to the file in which the directrive was used. If that fails,
the preprocessor will fall back to using the
<>
syntax. This means that
using
""
is stronger than using
<>
. Typically, you'll want to use the
""
syntax when the file you specified is actually the relative path specified.
If the file to be included is in a parent directory, you should use the
<>
syntax. For this project, if you ever find yourself typing
#include "../../some/file.h"
, you are probably doing something incorrectly.
See more about the preprocessor here: https://en.wikipedia.org/wiki/C_preprocessor .
The compiler is responsible for generating a corresponding object file from each inputted compilation unit (usually referred to as a translation unit ). A translation unit contains the source file after the preprocessor is done expanding and preprocessing the file.
Some common C++ compilers and environments are:
gcc
/
g++
, GNU environment
clang
/
clang++
, LLVM environment
MSVC
, Microsoft Visual C++ environment
MinGW
/
MSYS2
, "Minimalist GNU for Windows" environment
Cygwin
, yet another C++ environment for Windows
So, let's say you are using
g++
. Then you would compile your library files
like:
(for C++ 11 features in this case). Then this will generate
hello.o
and
world.o
, which are object files.
A library can then be created using these object files.
This is where everything comes together!
When you create an executable, you'll need to link individual object files or libraries together. This step is crucial, because in this step all of the symbols are verified, and the linker ensures that symbols have definitions, well, at least when using static libraries.
Check out this blog post for more information.
Why the #$@&%*! are we using CMake? Why not use XCode or Visual Studio?
In order to get you prepared for CS 225 and summer internships, we thought it would be useful to understand build systems. XCode and Visual Studio both have integrated build systems, so if something goes wrong, it is very hard to determine what the root cause is. With CMake, you are in control of building your project, so it is more configurable and flexible. CS 225 will use Makefiles, which are even more low level than CMake. CMake can generate Makefiles.
add_executable(name source1 [source2 ...])
to make a new executable.
For your final project, you probably won't need to use this.
add_library(name source1 [source2 ...])
to make a new library.
You might use this if you are using a Cinder Block that does not provide
a
CMake
file.
target_link_libraries
to link libraries to a
target
. Note that a target is a CMake construct,
which could either be an executable or another library.
target_include_directories(target [items1...])
to add a directory to the user-specified include directories.
This is pretty much all you'll need to know from vanilla CMake.
For some reason, the cmake files we distributed cause an annoying deprecation warning to
be displayed in
red
text. You can supress these warning by going
to
File > Settings > Build, Execution, Deployment > CMake
, and then for the CMake options,
add
-Wno-dev
. The warning has to do with old CMake syntax.
Here are some more commands that will probably be used for this project.
Below are examples. You should be able to follow the pattern. You don't need to know every little detail.
In
final-project-<github-name>/CMakeLists.txt
:
This will download a Git repository into your build folder. The first argument is the name that you want to refer to the entity as.
Then, you'll do something like this, still in
final-project-<github-name>/CMakeLists.txt
:
Notice that
gflags
is a
CMake
project, so you can add it as a subdirectory
of your project. This piece of code adds
gflags
as a subdirectory of your
project, which will expose any targets that
gflags
defines.
NOTE
: It is important that your variable names are all lowercase for
FetchContent
.
The
FetchContent_Populate(<name>)
method will convert
<name>
into lowercase,
so just make all of the names lowercase. See more here:
https://cmake.org/pipermail/cmake/2018-March/067185.html
.
Header-only libraries are pretty common in C++. One advantage of a header-only
library is that there is no need to link, so it is typically seen as very
simple to integrate into an existing project because you only need to
include
the header file(s). A downside is that every time you compile your
program, you potentially have to compile the entire header-only library
into your translation unit(s). This will lead to slow compile times.
Catch2
is an example of a header-only library.
In
final-project-<github-name>/CMakeLists.txt
:
and then:
FetchContent_GetProperties (catch2) if (NOT catch2_POPULATED) FetchContent_Populate (catch2) add_library (catch2 INTERFACE ) target_include_directories (catch2 INTERFACE ${ catch2_SOURCE_DIR }/single_include) endif ()
Notice that this second block is a little different then what we previously did.
Whether or not
Catch2
is a CMake project, all we need are the header files
that need to be included. So we create a new library called
catch2
, which
is declared as an
INTERFACE
, which just means that there are no source files
to compile. Then, we specify which files should be added to the user-specified
include directories if this library is to be used.
The directory
single_include/
is specific to Catch2, so you'll have to see
which directory your project needs in order to include header files. For example,
in the
snake
application, we used
SQLiteModernCpp
which has its include directory as
hdr/
.
These header-only libraries will typically be painless to integrate into your project.
First, let's say your
cinder
folder is at
~/Cinder
.
NOTE
: this might be different on your machine!! It might be
~/Downloads/Cinder
or it could be
/Users/rey/CLionProjects/Cinder
, or it could be
C:\Users\rey\Downloads\cinder_vs2015_0.9.2
, or
C:\Users\rey\cinder_vs2015_0.9.2\cinder_vs2015_0.9.2
,
or
/Users/rey/Downloads/cinder_0.9.2
, etc.
Formally,
${CINDER_PATH}
is defined as the directory where the directory
${CINDER_PATH}/proj/cmake
exists. Many of you had the pleasure of seeing this CMake
error message when trying to load the
CMake
snake project. This should clear up
some misunderstandings. So for example, your
snake
project already resides
in the
${CINDER_PATH}/my-projects/snake-<your-github-username>
.
In CMake, you should define and
literally
use the variable
${CINDER_PATH}
to refer to your
Cinder location.
Okay, next let's say you want to use the
Cinder-Notifications
CinderBlock. Here is what you need to do:
Note that this isn't necessarily required, but it is typically what you would do if you want to make changes to a repo you don't own on GitHub.
In CLion, go to
VCS > Get from Version Control
, and then clone your forked
version of the Cinder Block
into
${CINDER_PATH}/blocks/<name-of-cinder-block-repo>
using
the exact same name as the repo on GitHub. So for
Cinder-Notifications
,
you would type
https://github.com/redpaperheart/Cinder-Notifications.git
as the URL, and
${CINDER_PATH}/blocks/Cinder-Notifications
as the directory.
Remember, you won't type
${CINDER_PATH}
literally, you type whatever this
path is on your machine. Then click the
Clone
button and open the repo
in a new window.
If your Cinder Block is already a CMake project, then you shouldn't need to
follow the rest of these steps. In the new window, open up the
cinderblock.xml
file.
Take a close look at this file. This files tells you what files need to be compiled
and which directories need to be included. For example, in
${CINDER_PATH}/blocks/Cinder-Notifications/cinderblock.xml
,
there is:
This is giving you some instructions on how to build this project.
First, make the file
${CINDER_PATH}/blocks/Cinder-Notifications/proj/cmake/Cinder-NotificationsConfig.cmake
,
and place the following inside:
Sometimes, there are a lot of source files. Instead of declaring each and
every file in
${SOURCE_LIST}
, you can just use a CMake
GLOB
pattern for them instead:
You don't really need the
.h/.hpp/.H
files in the
GLOB
though.
Once you are done with this step, you can commit and push your changes to your fork. And if you want to, you can even create a Pull Request on the original CinderBlock GitHub repository with your fork!
Now, there are three targets by default in your project folder:
cinder-myapp
,
mylibrary
, and
test
. You are free to change the names of these
targets, just make sure to do a find and replace.
cinder-myapp
target is configured in
final-project/apps/CMakeLists.txt
.
mylibrary
target is configured in
final-project/src/CMakeLists.txt
.
test
target is configured in
final-project/tests/CMakeLists.txt
.
Fortunately, Cinder has some nice macros that take care of the
CMake commands that are needed to build your project (i.e.,
add_library
,
add_executable
, etc.).
In
final-project/src/CMakeLists.txt
, you'll find the macro
ci_make_library
:
This is a custom modification of the Cinder's
ci_make_app
macro, just adapted
for libraries instead. This means that unlike the
snake
application, you'll
be able to use
libcinder
and other Cinder Blocks in your library.
The
test
target is defined with the
ci_make_app
macro:
The
cinder-myapp
target is defined with the
ci_make_app
macro:
You can link to Cinder Blocks as well by adding the names of the Cinder Blocks to
the
BLOCKS
parameter. So for
Cinder-Notifications
, you would just do:
after you have added it's
Configure.cmake
file mentioned in the previous section.
Note, that Cinder Blocks are open source and might be out of date or not active.
So for example, when I tried to run my
cinder-myapp
target after adding the
Cinder-Notifications
Cinder Block, I got an error saying that some include path couldn't be found.
I had to modify
Cinder-Notifications/src/rph/NotificationManager.h
to include
cinder/Timeline.h
instead of
cinder/timeline.h
.
You can read more here .
Now, from the setup above, you are able to use your Cinder Block for files in the
cinder-myapp
target. So in
final-project/apps/my_app.cc
, I can write:
to use the Cinder Block.
After you used
FetchContent
, you just need to add the target
to the
LIBRARIES
argument for the
ci_make_xxx
macro(s).
Make sure you are using the correct target name if you went the
add_subdirectory()
route. The
README.md
or
USAGE.md
file of the CMake library might have
some more instructions.
You can run some of the included sample projects that are located in the
${CINDER_PATH}/samples
directory. Just open one of the projects in CLion,
and then you have the Load the
proj/cmake/CMakeLists.txt
file, if it isn't
loaded automatically.
Unfortunately, the CS 126 staff has probably never used whichever library you intend to use, so you'll probably know more about the library than we do. We can attempt to help with generic CMake issues, but you'll have to try on your own as well.
If you decide to make a Piazza post, please be as detailed as possible: the more detailed you are, the more likely someone will attempt to help you. So this means that at a minimum, you should have the following components in your question:
macOS 10.15.3, Apple clang version 11.0.0
Here is a good example: https://github.com/catchorg/Catch2/issues/1863 .
Also, you can make CMake related posts public. Some other people might be running into the same problem.
Whew, that's a lot to read ...
Hopefully, you now understand CMake a little better.
Here is the book where this file structure was taken from .
Below are some case studies.
The
cinderblock.xml
file is:
Note that this CinderBlock is only supported on MacOS.
Here is what needs to be added in
${CINDER_PATH}/blocks/ciSpeech/proj/cmake/ciSpeechConfig.cmake
:
Here is the
cinderblock.xml
file:
This
cinderblock.xml
file is a little more involved.
Notice how the compilation
claims
to be platform dependent.
It actually is not, since it makes some assumptions.
We have tested this on MacOS. For Windows, we had to mess around with the
PretzelGui
source code, so this was not ideal.
Also for this project, there are
<resource>
tags.
So, your
${CINDER_PATH}/blocks/PretzelGui/proj/cmake/PretzelGuiConfig.cmake
file will look like:
But wait, you need to do one more thing for the resources.
If you are on MacOS, make a file
${CINDER_PATH}/blocks/PretzelGui/src/Resource.h
with the contents:
**This will not be sufficient for Windows users because
loadResource
operates differently
on Windows. If you want to use a GUI library on Windows, please look into
simongeilfus/Cinder-ImGui
as it is painless to set up (already has proj/cmake stuff).
Notice that the filepath
${CINDER_PATH}/blocks/PretzelGui/src/Resource.h
is dependent on where your public include directory is.
In this example, it was
${PretzelGui_PROJECT_ROOT}/src
.
Now, in whichever CMake file you use this CinderBlock, you need to add the resources
to
ci_make_xxx
. For example, in
apps/CMakeLists.txt
, after the first two lines
of the file, I would add:
and then I would add to the
RESOURCES
argument of
ci_make_app
:
You can read more about resources in Cinder here: https://libcinder.org/docs/guides/resources/index.html .
Here is the
${CINDER_PATH}/blocks/NanoguiBlock/proj/cmake/NanoguiBlockConfig.cmake
file:
It looked as if the
cinderblock.xml
was missing some specifications of some include paths.
Here is the
cinderblock.xml
file:
Here is the corresponding
${CINDER_PATH}/blocks/Cinder-OpenCV3/proj/cmake/Cinder-OpenCV3Config.cmake
file: