Issue
I am trying to learn how to code on a Raspberry PI. I am coming from coding using Windows and VS Code. Now I am using Linux Mint 19.1 and ssh to access Raspbian 4.14.
The problem is that, after a long battle with downloading the library that I am trying to use, after installing a compiler, creating a file, and finding the right command to run and include paths I get undefined reference to
errors.
I am trying to compile the simplest example code from https://github.com/hzeller/rpi-rgb-led-matrix because I started with this project. I don't have any other code or example.
Here are the commands that I've wrote in command line:
sudo apt-get install g++
mkdir 4panel
cd 4panel
sudo nano main.cpp
git clone https://github.com/hzeller/rpi-rgb-led-matrix.git
sudo g++ -Wall -Irpi-rgb-led-matrix/include main.cpp -o main
The following commands should be:
sudo chmod +x main
./main
main.cpp contains the same code as https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/examples-api-use/minimal-example.cc
#include "led-matrix.h"
#include <unistd.h>
#include <math.h>
#include <stdio.h>
#include <signal.h>
using rgb_matrix::GPIO;
using rgb_matrix::RGBMatrix;
using rgb_matrix::Canvas;
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
static void DrawOnCanvas(Canvas *canvas) {
/*
* Let's create a simple animation. We use the canvas to draw
* pixels. We wait between each step to have a slower animation.
*/
canvas->Fill(0, 0, 255);
int center_x = canvas->width() / 2;
int center_y = canvas->height() / 2;
float radius_max = canvas->width() / 2;
float angle_step = 1.0 / 360;
for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) {
if (interrupt_received)
return;
float dot_x = cos(a * 2 * M_PI) * r;
float dot_y = sin(a * 2 * M_PI) * r;
canvas->SetPixel(center_x + dot_x, center_y + dot_y,
255, 0, 0);
usleep(1 * 1000); // wait a little to slow down things.
}
}
int main(int argc, char *argv[]) {
RGBMatrix::Options defaults;
defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat"
defaults.rows = 32;
defaults.chain_length = 1;
defaults.parallel = 1;
defaults.show_refresh_rate = true;
Canvas *canvas = rgb_matrix::CreateMatrixFromFlags(&argc, &argv, &defaults);
if (canvas == NULL)
return 1;
// It is always good to set up a signal handler to cleanly exit when we
// receive a CTRL-C for instance. The DrawOnCanvas() routine is looking
// for that.
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
DrawOnCanvas(canvas); // Using the canvas.
// Animation finished. Shut down the RGB matrix.
canvas->Clear();
delete canvas;
return 0;
}
Errors:
/tmp/cci8MGL5.o: In function `main':
main.cpp:(.text+0x264): undefined reference to `rgb_matrix::RGBMatrix::Options::Options()'
main.cpp:(.text+0x2a8): undefined reference to `rgb_matrix::CreateMatrixFromFlags(int*, char***, rgb_matrix::RGBMatrix::Options*, rgb_matrix::RuntimeOptions*, bool)'
collect2: error: ld returned 1 exit status
Possible fixes that I found:
- Link .cpp files in a strict order when compiling:
sudo g++ main1.cpp main2.cpp
- Link .cpp files in a strict order when compiling:
- The need of a
makefile
- The need of a
While searching for similar issues and hopefully finding some magic fix, I ended up more puzzled than before. I have no idea if what I did is what I was supposed to do.
- What cpp files to link if I have just one ?
- Do I need to link all the .cpp and .h files that the library has ?
- I don't understand a thing about
makefile
or why it even exists.
- I don't understand a thing about
My issue may be specific to my situation but I believe that the answers will help me with future projects.
You are free to answer all of my questions and misunderstandings and help me understand what is going on or answer just the question related to the title: Why makefile
?
Solution
The problem is that, after a long battle with downloading the library that I am trying to use, after installing a compiler, creating a file, and finding the right command to run and include paths I get undefined reference to errors.
There are already numerous questions and answers on SO pertaining to such problems, especially What is an undefined reference/unresolved external symbol error and how do I fix it?
Possible fixes that I found:
- Link .cpp files in a strict order when compiling:
sudo g++ main1.cpp main2.cpp
- [SO question with answers saying roughly the same as the previous]
Not typically relevant. Although link order of different objects / libraries is significant, that rarely affects the case when all the needed C and / or C++ source files are specified in the same compilation command.
- The need of a makefile
Using make
does not inherently solve the problem, and it is certainly not necessary for solving the problem. Writing a makefile that provides for a successful build, whether directly or via a build tool such as CMake or the Autotools, requires you to understand at least at a high level how compilers and linkers work, what their inputs and outputs are, and what their arguments and options mean.
- What cpp files to link if I have just one ?
You do not link a .cpp
(directly) at all. You compile it and link the result to any needed libraries and any other objects. g++
will try to do that all in one step by default, in which case the process is conventionally called "compiling", not "linking", even though both functions are performed. g++
can be made to compile only, without linking, by specifying the -c
option to it (which is common in makefiles, as it turns out), and it will happily link objects and libraries without compiling anything if you don't name any source files to it.
Anyway, your case is not at all that of just one .cpp
file. It is your one .cpp
file plus all those of the library you are trying to use.
- Do I need to link all the .cpp and .h files that the library has ?
You do not need to compile (directly) or link any .h files. But if you want to use the library whose sources you downloaded, you do need to compile all the sources, and you probably should link them into an actual library. Furthermore, unless you produce and use a static library, you should also install the library you build. It looks like the library sources include a makefile, so you would be well advised to use make
to perform those steps.
- I don't understand a thing about makefile or why it even exists.
A makefile is the input to make
. A well-written one defines rules for how to build one or more "targets", and expresses the prerequisites (generally original sources or other targets) for doing so. make
uses these, together with a set of built-in rules, to figure out what steps to take to build the target(s) you ask it to build, and also what steps it can skip. It is typical, for example, to write makefiles so that if you build a multisource project, modify only one source file, and then build again, only the modified source gets recompiled.
This may seem trivial when you're used to projects with only a handful of sources, but it becomes important in larger projects, where full rebuilds are expensive -- many hours, in some cases. But even if a full build takes only a few minutes, if you really only needed to spend a few seconds then that adds up to a lot of wasted time over a full day of work.
Overall, then:
When do I need to use/have a makefile?
Never. But providing a good one and using make
to build your project has these among its advantages:
- memorializing all the needed build commands, including options and arguments, in a persistent, actionable form, and therefore
- providing for consistent, automated builds.
Those can also be achieved by a simple build script, but make
also can give you
- faster builds by building only those pieces that are out of date, and
- built-in knowledge of how to build certain kinds of targets from corresponding sources.
Answered By - John Bollinger