Common Development Tools
In practical development, besides knowledge of Linux, developers often need to modify system configuration files and edit code. Proficiently using editor tools can help developers improve their development speed and programming efficiency. This chapter will introduce Vim and Nano editors, as well as GCC and other code compilers. These tools come pre-installed on Core3566, and users can directly use them.
1. Nano Editor
Nano is a simple and user-friendly terminal text editor, suitable for beginners or developers with limited customization needs for the editor. It provides basic editing functions such as insert, delete, copy, and paste, along with a straightforward interface and easy-to-use operations. Nano is convenient for quick editing or fixing code, as well as editing configuration files and other small tasks. Although Nano's features are relatively simple, due to its simplicity and ease of use, it remains a preferred choice for developers in certain scenarios.
To start Nano, simply enter "nano" in the terminal. Alternatively, you can open a specific file by adding its path after the command. If the file does not exist, Nano will create it. For example, in the Raspberry Pi terminal, typing "sudo nano luckfox.txt" will open the "luckfox.txt" file for editing, just like using a notepad.
- The top line displays the version of the Nano editor and the name of the currently edited file. The bottom two lines show the most commonly used shortcuts in the editor, with the "^" symbol representing Ctrl and "M-" representing Alt.
- Common shortcuts:
File Management
Ctrl+S Save
Ctrl+O Save As
Ctrl+X Exit Nano
Ctrl+R Insert File into Buffer
Ctrl+G Get HelpEditing
Alt+N Toggle line numbering on/off
Shift+↑ or ↓ Select lines upward or downward
Alt+3 Comment or uncomment selected lines or block
Alt+U Undo
Alt+E RedoSearch
Ctrl+Q Start backward search
Ctrl+W Start forward search
Alt+Q Search for the next match backward
Alt+W Search for the next match forward
2. Vim Editor
Vim is a highly customizable text editor widely used by developers for coding work in a terminal environment. Vim boasts powerful editing features and an extensive plugin ecosystem, enabling developers to efficiently edit and organize code. It supports multiple programming languages and provides abundant shortcuts and commands for quick navigation, editing, and debugging of code. Due to its high customizability and powerful features, Vim enjoys broad application and high regard among developers. The following is a simple introduction to its usage and common commands.
For convenience, the following three lines are usually added to the Vim configuration file:
set nu # Show line numbers
syntax on # Enable syntax highlighting
set tabstop=4 # Set tab width to four spaces
2.1 Vim Modes
Basically, Vim can be divided into three modes: Command mode, Insert mode, and Last line mode:
- Command mode: Controls the movement of the screen cursor, deletion of characters, words or lines, and copying/moving of certain sections
- Insert mode: Allows inputting characters and editing the file
- Last line mode: Used for saving or exiting Vim, as well as configuring editing settings like searching for strings or displaying line numbers
2.2 Common Commands
Opening, Saving, and Closing Files (vi command mode):
vim filename Open the "filename" file :w Save the file :q Exit the editor; use the following command if the file has been modified :q! Exit the editor without saving :wq Exit the editor and save the file :wq! Force exit the editor and save the file ZZ Exit the editor and save the file ZQ Exit the editor without saving Inserting Text or Lines (vi command mode; after executing the following command, it enters insert mode, and pressing ESC exits insert mode):
a Add text to the right of the current cursor position i Add text to the left of the current cursor position A Add text at the end of the current line I Add text at the beginning of the current line O Create a new line above the current line o Create a new line below the current line R Replace (overwrite) text at the current cursor position and beyond J Merge the current line with the next line (still in command mode) Deleting and Undoing Characters or Lines (vi command mode):
x Delete the current character nx Delete n characters starting from the cursor dd Delete the current line ndd Delete n lines, including the current line u Undo the previous action U Undo all changes to the current line Copying and Pasting (vi command mode):
yy Copy the current line to the clipboard nyy Copy n lines starting from the current line to the clipboard yw Copy from the cursor to the end of the word to the clipboard nyw Copy n words starting from the cursor to the clipboard y^ Copy from the cursor to the beginning of the line to the clipboard y$ Copy from the cursor to the end of the line to the clipboard p Paste the content from the clipboard after the cursor P Paste the content from the clipboard before the cursor
3. gcc Compiler
The three core components of the gcc toolchain are gcc-core, Binutils, and glibc.
- gcc-core: Also known as the gcc compiler, it is the heart of the gcc toolchain and includes compilers for various languages such as C, C++, Fortran, etc. It can compile source code into object files or executable files.
- Binutils: It is a collection of binary tools developed by GNU, including the assembler (as), linker (ld), object file format conversion tools (objcopy, objdump), debugger (gdb), and other tools used to create, modify, debug, and analyze executable and object files.
- glibc: GNU C Library is the C standard library in the gcc toolchain, providing essential system functions and APIs. It serves as a core library for user-space program development in Linux systems.
- These three components are interdependent, forming a complete gcc toolchain that enables developers to conveniently carry out software development and debugging. In Linux systems, these components are typically installed by default, providing an efficient development environment. In many cases, the term "gcc compiler" is used to refer to the entire gcc compilation toolchain.
3.1 gcc Compiler
gcc on Linux is an open-source compiler, with the full name GNU Compiler Collection, developed by the GNU Project. It supports multiple programming languages such as C, C++, Objective-C, Fortran, Ada, etc., and is capable of compiling large and complex applications. gcc is a portable compiler that supports various hardware platforms like ARM, X86, etc. Additionally, gcc can be used not only as a native compiler but also for cross-platform cross-compilation. You can check the version using the following command:
gcc -v
3.1.1 gcc gcc Compilation Steps
Compiling a C file into an executable on Linux using gcc involves four steps: Preprocessing, Compiling, Assembling, and Linking.
Preprocessing: In this stage, the preprocessor processes the source code, removing comments, expanding macros, and generating a new file (usually ending with .i).
- All "#define" directives are removed, and all macro definitions are expanded.
- Conditional preprocessing directives are processed, such as: "#if #ifdef #elif #else #endif".
- All "#include" preprocessing directives are processed.
- All comments "//" and "/* */" are removed.
- Line numbers and file name identifiers are added for generating line number information during compilation and displaying line numbers for compilation errors or warnings.
- All "#pragma" compiler directives are retained.
Compiling: In this stage, the compiler performs lexical analysis, syntax analysis, semantic analysis, and optimization on the preprocessed file, generating corresponding assembly code files (usually ending with .s).
Assembling: In this stage, the assembly code is converted into machine-executable binary code, generating binary code files (usually ending with .o).
Linking: During the linking process, the linker resolves the references of functions and variables and matches them with those defined in other object files, ultimately creating an executable file.
3.1.2 gcc Compilation Process
The syntax for using gcc is as follows:
gcc [options] input_file
gcc has hundreds of options, and here we will introduce some common ones:
- No options: This will generate an executable file called "a.out" in the same directory as the source file. For example,
gcc test.c
will generate the executable file "a.out". - -c: Specifies that the compiler should only compile the source file and generate an object file without linking.
- -o: Specifies the output file name for the compiler. For example,
gcc main.c -o myprogram
. - -Wall: Enables all warnings to detect potential code issues.
- -O: Enables code optimization to improve the efficiency of the generated code.
- -g: Generates debugging information for runtime debugging.
- -I: Specifies the search path for header files. For example,
gcc -I/usr/local/include myfile.c
. - -L: Specifies the search path for library files. For example,
gcc -L/usr/local/lib myfile.c
. - -l: Links the specified library file. For example,
gcc myfile.c -lm
. - -std: Specifies the C++ standard to use. For example,
gcc -std=c++11 myfile.cpp
. - -pthread: Enables thread support.
- No options: This will generate an executable file called "a.out" in the same directory as the source file. For example,
Compilation process:
# Compile directly into an executable file
gcc test.c -o test
# The above command is equivalent to performing the following operations
# Preprocessing: Combines header files into C code, converting *.c to *.i
gcc -E test.c -o test.i
# Compilation: Converts C code to assembly code, converting *.i to *.s
gcc -S test.i -o test.s
# Assembly: Converts assembly code to machine code, converting *.s to *.o (object file)
gcc -c test.s -o test.o
# Linking: Links the call relationships between different files and converts one or more *.o files into the final executable file
gcc test.o -o testIn practical programming, we usually generate executable files directly from the source file. Taking a simple C program as an example:
#include <stdio.h>
int main()
{
printf("hello, luckfox\n");
return 0;
}Compilation and execution:
gcc test.c -o test
./testOutput:
linaro@linaro-alip:~$ ./test
hello, luckfox
linaro@linaro-alip:~$
3.1.3 Cross-Compilation
If we want the compiler to run on an x86 architecture platform and generate an ARM architecture executable program, this compilation process where the compiler and target program run on different architectures is called cross-compilation.
Install the ARM-GCC compiler:
# Execute the following command on the host machine
sudo apt install gcc-arm-linux-gnueabihfAfter installation, compile and generate the ARM architecture executable program:
arm-linux-gnueabihf-gcc test.c -o test
Check which architecture the executable program belongs to:
file test
linaro@linaro-alip:~$ file test
test: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=c4bfa4bceebabc10960c9689759cad948782e91c, not stripped
linaro@linaro-alip:~$
3.2 g++ Compiler
g++ is the GNU compiler for C++. The internal process of compiling programs with g++ is similar to gcc. When compiling C++ programs with g++, it automatically links to the C++ standard library without the need for manual specification.
4. Makefile Basics
4.1 Make Tool
- The make tool is a software automation tool used for building software. It automates the process of compiling, linking, and packaging by reading rules specified in the Makefile.
- The make tool resolves the tedious and complex tasks of manual compilation, linking, and packaging during software development, especially in large projects where manual tasks become challenging and error-prone due to the volume of source code and complex file dependencies. Invoking the make tool is straightforward; simply enter the "make" command in the terminal.
4.2 Makefile
Makefile is a file used for automating the compilation of programs. It contains a series of rules that indicate how to compile the program, generate executable files, and clean up temporary compilation files, among other tasks. The filename of the Makefile can be either "makefile" or "Makefile".
4.3 Explicit Rules in Makefile
In a Makefile, the '#' symbol represents a comment that will not be included in the compilation. The instruction lines in the Makefile must start with a Tab key, and an error will occur if they begin with four spaces. The basic syntax of a Makefile rule is as follows:
target: dependencies
<Tab>commandIf you want to compile a "test.c" file in the current folder, the Makefile content should be:
all:
gcc test.c -o testThe above program can also be written as:
all:test.o
gcc test.o -o test
test.o:test.c
gcc -c test.cSince "all" depends on "test.o", the "gcc -c test.c" command must be executed first to generate the "test.o" file before executing "gcc test.o -o test". So, the execution order after entering the "make" command will be:
pi@raspberrypi:~/test $ make
gcc -c test.c
gcc test.o -o test
pi@raspberrypi:~/test $In a Makefile, ".PHONY" is a special target used to specify a list of phony targets (targets that do not correspond to actual files). These targets usually represent commands or operations that need to be executed rather than generating files. Using ".PHONY" informs the make tool not to check whether the corresponding files exist when executing phony targets. Without using ".PHONY", if a target name defined in the Makefile matches a filename, the make tool may produce errors. For example, if we want to use "make clean" to remove the object files (files with the ".o" extension) and the executable file generated by the previous "make" command, the following is an example Makefile defining the ".PHONY" target and phony targets:
all:test.o
gcc test.o -o test
test .o:test.c
gcc -c test.c
PHONY:clean
clean:
rm -rf *.o test
4.4 Custom Variables
- Variables are essential concepts in a Makefile. They can be used in various parts of the Makefile, such as targets, dependencies, or commands. Variable assignment can be done using the following methods:
- =: Direct assignment. The value of the variable is recursively expanded when used. For example: VAR = value.
- ?=: Conditional assignment. The variable is assigned a value only if it is not already defined. For example: VAR ?= value.
- :=: Simple assignment. The value of the variable is immediately expanded upon assignment. For example: VAR := value.
- +=: Appending to a variable. The value is appended to the end of the existing variable value. For example: VAR += value.
- Variables are referenced using $(). For example: $(VAR). When using variables, the Makefile will automatically expand the variable's value and replace the variable name within $().
4.5 Predefined Variables
Predefined variables are system-defined variables with fixed names and default values. You can use the default value or redefine these variables. Below are some common predefined variables:
Variable Name Meaning CC Name of the C compiler, default value is "cc" (which means using gcc by default) RM Name of the file deletion program, default value is "rm -f" CFLAGS C compiler (gcc) options, no default value. For example: -Wall, -g, -o CPP Name of the C preprocessor, default value is "$(CC) -E" CPPFLAGS C preprocessor options, no default value CXXFLAGS C++ compiler (g++) options, no default value
4.6 Automatic Variables
Automatic variables have specific meanings and cannot be modified. Below are some commonly used automatic variables:
Automatic Variable Reference Meaning $< Name of the first dependency file $@ Complete name of the target file $^ All non-repeated dependency files, separated by spaces $? Dependency files that are newer than the target file, separated by spaces For example, in a project with "luckfox.c", "luckfox.h", "main.c", and "Makefile" files, with the following content:
luckfox.h sample program:
#ifndef _LUCKFOX_H
#define _LUCKFOX_H
void luckfox(void);
#endifluckfox.c sample program:
#include <stdio.h>
#include "luckfox.h"
void luckfox(void)
{
printf("hello, luckfox\n");
}main.c sample program:
#include <stdio.h>
#include "luckfox.h"
int main()
{
luckfox();
}The content of the Makefile is as follows:
luckfox:luckfox.o main.o
gcc luckfox.o main.o -o luckfox
luckfox.o:luckfox.c
gcc -c luckfox.c -o luckfox.o
main.o:main.c
gcc -c main.c -o main.o
.PHONY:clean
clean:
rm -rf *.o luckfox
Using automatic variables can simplify the Makefile and reduce complexity, especially in projects with many files:
var:=luckfox.o main.o
luckfox:$(var)
gcc $^ -o luckfox
%.o:%.c
gcc -c $< -o $@
.PHONY:clean
clean:
rm -rf *.o luckfoxvar: Variable representing dependency files luckfox.o and main.o;
$^: Represents all dependency files, luckfox.o and main.o, which is equivalent to $(var);
%.o: Represents all .o files inside, namely luckfox.o and main.o;
%.c: Represents all .c files inside, namely luckfox.c and main.c;
$<: Represents luckfox.c and main.c;
$@: Represents the target files generated from luckfox.c and main.c, namely luckfox.o and main.o.Compiling individual C source files does not require writing explicit commands in the Makefile, as make can infer them automatically. For example, make can automatically use 'cc -c main.c -o main.o' to compile 'main.c' into 'main.o'. The above program can be further simplified as:
var:=luckfox.o main.o
luckfox:$(var)
cc $^ -o luckfox
%.o:%.c
cc -c $< -o $@
.PHONY:clean
clean:
rm -rf *.o luckfox