If you are using MacOS or Linux, it is probably already installed.
If you are using MacOS or Linux, it is probably already installed.
For windows, you can use visual studio's compiler, or emulate a another
If you are relying on visual studio, it can be difficult to use from the command line.
In this part of the workshop, we will use cmake to configure blender before compiling from scratch.
git clone https://projects.blender.org/blender/blender.git
We will create a simple program that computes the dot product of two vectors
$$ \left[\begin{array}{c c c} 1 &2 &3 \end{array}\right]*\left[\begin{array}{c} 2\4\6 \end{array}\right]=28 $$
We will begin by defining our main function and declaring some variables we will need. This script will be in the simple
folder and we will name it fun_math.cpp
int main(){
int length = 3;
double* vec1 = new double [3];
double* vec2 = new double [3];
return 0;
}
Then, we can fill in the values for our vectors
for (int i=0;i<length;i++){
vec1[i]=(double)i+1;
vec2[i]=((double)i+1)*2;
}
Next, we can write a function to compute a dot product: double dot_prod(double* v1, double* v2, int length){ double sum = 0; for (int i=0;i<length;i++){ sum +=v1[i]*v2[i]; } return sum; }
Finally, we can return the output so we know its working:
#include
This can be simply compiled from the root directory:
g++ ./simple/fun_math.cpp -o fun_math
Now we can run it
./simple/fun_math.exe
The previous example works well for when the program is simple, but when there are multiple functions, it is better to split them into multiple scripts. We will do this for our dot product function. In a folder named external_func
, create a file called dot_prod.cpp
, then copy the function into it. our old main
function into a new file as well in this folder. We can name it fun_math_lib.cpp
. fun_math_lib.cpp
should look like this
#include <iostream>
int main(){
int length = 3;
double* vec1 = new double [3];
double* vec2 = new double [3];
for (int i=0;i<length;i++){
vec1[i]=(double)i+1;
vec2[i]=((double)i+1)*2;
}
double result = dot_prod(vec1,vec2,length);
std::cout << result;
return 0;
}
and dot_prod.cpp
should look like this
double dot_prod(double* v1, double* v2, int length){
double sum = 0;
for (int i=0;i<length;i++){
sum +=v1[i]*v2[i];
}
return sum;
}
We are still calling dot_prod ino our main function, but the function is no longer defined in that script. We will have to tell the compiler what that function looks like. The main way of doing this is with header files. Lets create a header file named dot_prod.hpp
:
double dot_prod(double* v1, double* v2, int length);
Then we add #include "dot_prod.hpp
into one of the top lines of our main file.
Now our project is organized into two nice files. To compile, we need to tell the compiler about both of them.
g++ ./external_func/fun_math_lib.cpp ./external_func/dot_prod.cpp -o fun_math
You can see how this starts to get difficult when you have more files. Run the file to make sure it works.
You will likely need to incorporate an external library into your project at some point. One common example is a library called BLAS, which gives a bunch of linear algebra subroutines. So instead of having to write a dot product (for exmaple) function ourselves, we can use the one in the library. A library is essentially a big collection of those function scripts.
Instead of using the BLAS library, we will turn our function script into a library. In a new folder called library
, copy all the scripts from our previous folder. First, we will need to make an "object" file from our function script
g++ -c ./library/dot_prod.cpp -0 ./library/dot_prod
Then, we can turn this object file into a library using ar
ar rvs ./library/dot_prod.a ./library/dot_prod.o
usually, you would put many object files into a library. To compile our main function with the library, we can run
g++ ./library/fun_math_lib.cpp -L./library/ -l:dot_prod.a -o fun_math
Typically in project, you'll want to use many libraries, so we need a good way to keep track of them and our supporting scripts. This brings us to CMAKE
To start using cmake, we will go back to our simple folder and begin with the basics. cmake is driven from instructions located in CMakeLists.txt
files. First, we will create one of these files in our simple
folder. There are three main items needed in this file, as shown below:
cmake_minimum_required(VERSION 3.20)
project(fun_math VERSION 0.1.0 LANGUAGES CXX)
add_executable(fun_math fun_math.cpp)
Once we add these to our CMakeLists.txt
file, we are pretty much ready to use cmake. First let us create a build directory. Cmake creates a lot of files and you dont want them to pollute your working area.
mkdir build
cd build
Now we can run cmake, the first argument of the command is the directory where the CMakeLists.txt
is located
cmake ..\simple
Depending on how your system is set up, a few things could have happened. If you are on windows, a bunch of visual studio files were probably created. If you are on linux or mac, make files were probably created. These are both files for your "build system" They explain the configuration of your project and everything needed to run. They are basically scripts that will compile your code for you. An easy way to run them is by using
cmake --build .
This will create your fun_math executable. The location of it may vary. For me, I have to run
./Debug/fun_math.exe
Now we will get more into the things you can specify. For starters, you can specify the minimum version of C++
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
You can also define many complier options, such as how much optimization you want or which compiler flags to use. For example,
set(CMAKE_CXX_COMPILER \path\to\compiler)
We can also specify c++ files that we want to include as libraries. Lets copy our current CMakeLists.txt
file to the external_func
folder and add the following lines.
add_library(dot_prod dot_prod.cpp)
target_link_libraries(fun_math PUBLIC dot_prod)
target_include_directories(fun_math PUBLIC "${PROJECT_SOURCE_DIR}")
Breaking this down, the add_library
command tells cmake that we want to make a library out of the dot_prod.cpp
file. This is similar to how we made a library with the ar
command. The target_link_libraries
command tells cmake to link in that library when compiling the fun_math target. Since we created the library in the line above, cmake knows were to look for the library. Finally, the target_include_directories
tells cmake where to look for our header files, since it will not know that by default. Conveniently, CMake has special variables to refer to special parts of your project directory. "${PROJECT_SOURCE_DIR}"
refers to the directory where the project
function was last called.
It is worth noting that while we have all these files in the same directory, it is customary to separate library, header, and main files into their own directories. see here if you are curious.
Now lets test out our new cmake file. First clear out the contents of our last build. For powershell, we can use
rm *
Then
cmake ../external_func
cmake --build .
./Debug/fun_math.exe
From what I have seen, the most common incorporation of external libraries is by the previous method, or by using the find_package
, which we wont get into. However, there is another way to incorporate static libraries like the one we have already created. Lets copy our current CMakeLists.txt
file to the library
folder and add the following lines.
target_link_libraries(fun_math "${CMAKE_SOURCE_DIR}/dot_prod.a")
remove these lines
add_library(dot_prod dot_prod.cpp)
target_link_libraries(fun_math PUBLIC dot_prod)
Now, instead of creating a library then adding it to the library, we are just simply adding one that has already been created. There is a catch to this though. The compiler we used to create the library likely has to be the same compiler used by cmake or else the linking will fail. Now, I will get to show you what it looks like to use makefiles as the build system instead of visual studio's system. First, clean out the build folder like last time. If you have mingw64 gnu compilers, you can type:
cmake -G "MinGW Makefiles" ..\library
If you have another version on windows, you will need to use a different argument. If you are on linux or mac, you probably dont need an argument at all. You also might get an error and notification about viruses on windows. Windows detects executables being copied by cmake and deletes them, causing the errors and notifications. When this happens, I run the command again without errors. If we look at our build folder now, it is much cleaner. To build the project, we can simply type:
make
.\fun_math.exe
Finally, we can change how our program runs based on what happens in CMake.
For example, you might want to use find_package
to see if a certain library is available. If it isn't, you change your source code to use homemade functions instead of those provided by the library.
find_package(BLAS)
if (BLAS_FOUND)
add_compile_definitions(BLAS_FOUND=1)
else ()
add_compile_definitions(BLAS_FOUND=0)
endif()
Then in the source code or headers
#if BLAS_FOUND
#include <cblas.h>
#else
#include <dot_prod.hpp>
#endif
You would then need to add similar branching statements to you function calls or make both functions have the same interface.