1. Compile a c-file
The C language is a necessary programming language for every computer science student and most of the time you will be using IDEs like Visual studio, Visual studio code and Clion to write codes. However, what happens when you press the “Compile and Run” button on the IDE?
Let's now create a typical C file named "main.c" which should look like the following:
#include<stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
Now, instead of clicking on that tempting “play button”, we'll open a terminal and type:
gcc main.c -o hello
The -o
parameter specifies the name of the compiled executable, and the suffix is determined by the compiler according to the compilation platform. Here it is compiled as “hello.exe”.
With the following command, you will get the familiar “Hello, World!” output:
hello.exe ;or ./hello on Liunx
2. What a makefile does and how to use it
A makefile is a description file used to specify compilation behavior. When you use the make
command, you first compile the file according to the guidelines in the makefile.
The format of Makefile is probably like following:
target: dependencies
actions
Now we create a messge.h
header file with the message
function in it, and then we implement it in message.c
:
#include <stdio.h>
void message(){
printf("Hello, Miochan!\n");
}
Now introduce this function and header file in main.c:
#include<stdio.h>
#include "message.h"
int main() {
message();
// printf("Hello, World!\n");
return 0;
}
Now let's write the makefile file:
hello: main.o message.o
gcc main.o message.o -o hello
main.o: main.c
gcc -c main.c
message.o: message.c
gcc -c message.c
In this makefile file, gcc first compiles main.c and message.c into target files, i.e. .o
files. The -c
parameter indicates that only the target files (.o
files) are compiled and generated, not linked. After that, gcc links the target files and compiles them into executable file. The advantage of this is that gcc only needs to compile the file with modifications before linking, and does not need to repeat the compilation.
Then execute:
make
And it will also create hello.exe and output:
Hello, Miochan!
3. Phony Target (No relevance to Kafu)
During the compilation process, some intermediate files (e.g., .o files) are created that need to be cleaned up, or some test scripts may need to be executed to ensure reliability.So, Phony Target are needed to perform these operations.
Now we add a clean
tag to the makefile file:
hello: main.o message.o
gcc main.o message.o -o hello
main.o: main.c
gcc -c main.c
message.o: message.c
gcc -c message.c
clean:
rm -f *.o hello # on Linux
del /S *.o hello.exe # on Windows
Then execute:
make clean
gcc will clean up all the o-files and hello files
Tips: If a directory or file named clean exists in the directory, the command can not run as expectation because gcc will regard the argument as a name. You can explicitly specify clean
as a phony-target in the following way:
.PHONY: clean
By default, gcc reads compilation instructions from the first target, so what if we want to compile two files at the same time, like this:
hello: main.o message.o
gcc main.o message.o -o hello
world: main.o message.o
gcc main.o message.o -o world
main.o: main.c
gcc -c main.c
message.o: message.c
gcc -c message.c
You will notice that there is no output world file.
Use a phony-target named all
to solve this problem:
.PHONY: all clean
all: hello world
echo "Build all"
hello: main.o message.o
gcc main.o message.o -o hello
world: main.o message.o
gcc main.o message.o -o world
main.o: main.c
gcc -c main.c
message.o: message.c
gcc -c message.c
clean:
del /S *.o hello world
The echo output is used to indicate that the phony-target was executed.
Then execute:
make all clean
It will output both the hello
and world
files.
Tips: If the compilation commands are the same for both targets, you can write them as one file and use the automatic variable “$@
” at the end to indicate the output name:
.PHONY: all clean
all: hello world
echo "Build all"
hello world: main.o message.o
gcc main.o message.o -o $@
main.o: main.c
gcc -c main.c
message.o: message.c
gcc -c message.c
4. Defining Variables
For example, define some common compilation options as CFLAGS:
CFLAGS = -Wall -g -O2
Then it can be used in the compile command(just as Shell):
.PHONY: all clean
CFLAGS = -Wall -g -O2
all: hello world
@echo "Build all"
hello world: main.o message.o
gcc $(CFLAGS) main.o message.o -o $@
main.o: main.c
gcc $(CFLAGS) -c main.c
message.o: message.c
gcc $(CFLAGS) -c message.c
clean:
del /S *.o hello.exe world.exe
Targets can also be defined as variables, for example:
targets: hello world
sources: main.c message.c
objects: main.o message.o
Then use them:
.PHONY: all clean
CFLAGS = -Wall -g -O2
targets: hello world
sources: main.c message.c
objects: main.o message.o
all: $(targets)
@echo "Build all"
$(targets): $(objects)
gcc $(CFLAGS) $(objects) -o $@
main.o: main.c
gcc $(CFLAGS) -c main.c
message.o: message.c
gcc $(CFLAGS) -c message.c
clean:
del /S *.o hello.exe world.exe
But I think this would result in a significant reduction in readability and maintainability of the makefile file.
In addition to $@, there are these automatic variables:
Automatic Variable | Meaning |
$@ | indicates the target filename of the rule. If the target is a filename, it will be replaced by this variable. |
$< | indicates the name of the first dependency file. Useful in rules that compile a single source file. |
$? | lists all dependency files newer than the target, separated by spaces. This variable is often used for incremental compilation. |
$^ | lists all dependency files, with dependencies separated by spaces, and removes duplicates. |
$+ | similar to $^, but includes all dependency files, including duplicates. |
$* | indicates the base name of the target file. This is the remainder of the file name after the suffix is removed. |
5.CMake
CMake is a utility program for compiling c/cpp that uses CMakeLists.txt
to create Makefile
. A simple example of it is as follows:
cmake_minimun_required(VERSION 3.10)
project(HelloWorld)
set(SOURCE_FILES main.c message.c)
add_executable(hello ${SOURCE_FILES})
Use the following command to build:
cmake ./
If you run cmake on windows, use the following parameters to produce Unix style makefile files, otherwise the default options will be solution and project files prepared for Visual Studio. This is because cmake by default uses the most suitable generator found on Windows - Microsoft Visual Studio:
cmake -G "MinGW Makefiles" ..
The generated makefile file looks like the following:
# CMAKE generated file: DO NOT EDIT!
# Generated by "MinGW Makefiles" Generator, CMake Version 3.30
# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:
#=============================================================================
# Special targets provided by cmake.
# Disable implicit rules so canonical targets will work.
.SUFFIXES:
# Disable VCS-based implicit rules.
% : %,v
# Disable VCS-based implicit rules.
% : RCS/%
# Disable VCS-based implicit rules.
% : RCS/%,v
# Disable VCS-based implicit rules.
% : SCCS/s.%
# Disable VCS-based implicit rules.
% : s.%
.SUFFIXES: .hpux_make_needs_suffix_list
# Command-line flag to silence nested $(MAKE).
$(VERBOSE)MAKESILENT = -s
#Suppress display of executed commands.
$(VERBOSE).SILENT:
# A target that is always out of date.
cmake_force:
.PHONY : cmake_force
#=============================================================================
# Set environment variables for the build.
SHELL = cmd.exe
# The CMake executable.
CMAKE_COMMAND = "C:\Program Files\CMake\bin\cmake.exe"
# The command to remove a file.
RM = "C:\Program Files\CMake\bin\cmake.exe" -E rm -f
# Escaping for special characters.
EQUALS = =
# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = D:\Projects\makeFileLearning
# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = D:\Projects\makeFileLearning\build
#=============================================================================
# Targets provided globally by CMake.
# Special rule for the target edit_cache
edit_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Running CMake cache editor..."
"C:\Program Files\CMake\bin\cmake-gui.exe" -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : edit_cache
# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast
# Special rule for the target rebuild_cache
rebuild_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Running CMake to regenerate build system..."
"C:\Program Files\CMake\bin\cmake.exe" --regenerate-during-build -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache
# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast
# The main all target
all: cmake_check_build_system
$(CMAKE_COMMAND) -E cmake_progress_start D:\Projects\makeFileLearning\build\CMakeFiles D:\Projects\makeFileLearning\build\\CMakeFiles\progress.marks
$(MAKE) $(MAKESILENT) -f CMakeFiles\Makefile2 all
$(CMAKE_COMMAND) -E cmake_progress_start D:\Projects\makeFileLearning\build\CMakeFiles 0
.PHONY : all
# The main clean target
clean:
$(MAKE) $(MAKESILENT) -f CMakeFiles\Makefile2 clean
.PHONY : clean
# The main clean target
clean/fast: clean
.PHONY : clean/fast
# Prepare targets for installation.
preinstall: all
$(MAKE) $(MAKESILENT) -f CMakeFiles\Makefile2 preinstall
.PHONY : preinstall
# Prepare targets for installation.
preinstall/fast:
$(MAKE) $(MAKESILENT) -f CMakeFiles\Makefile2 preinstall
.PHONY : preinstall/fast
# clear depends
depend:
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles\Makefile.cmake 1
.PHONY : depend
#=============================================================================
# Target rules for targets named hello
# Build rule for target.
hello: cmake_check_build_system
$(MAKE) $(MAKESILENT) -f CMakeFiles\Makefile2 hello
.PHONY : hello
# fast build rule for target.
hello/fast:
$(MAKE) $(MAKESILENT) -f CMakeFiles\hello.dir\build.make CMakeFiles/hello.dir/build
.PHONY : hello/fast
main.obj: main.c.obj
.PHONY : main.obj
# target to build an object file
main.c.obj:
$(MAKE) $(MAKESILENT) -f CMakeFiles\hello.dir\build.make CMakeFiles/hello.dir/main.c.obj
.PHONY : main.c.obj
main.i: main.c.i
.PHONY : main.i
# target to preprocess a source file
main.c.i:
$(MAKE) $(MAKESILENT) -f CMakeFiles\hello.dir\build.make CMakeFiles/hello.dir/main.c.i
.PHONY : main.c.i
main.s: main.c.s
.PHONY : main.s
# target to generate assembly for a file
main.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles\hello.dir\build.make CMakeFiles/hello.dir/main.c.s
.PHONY : main.c.s
message.obj: message.c.obj
.PHONY : message.obj
# target to build an object file
message.c.obj:
$(MAKE) $(MAKESILENT) -f CMakeFiles\hello.dir\build.make CMakeFiles/hello.dir/message.c.obj
.PHONY : message.c.obj
message.i: message.c.i
.PHONY : message.i
# target to preprocess a source file
message.c.i:
$(MAKE) $(MAKESILENT) -f CMakeFiles\hello.dir\build.make CMakeFiles/hello.dir/message.c.i
.PHONY : message.c.i
message.s: message.c.s
.PHONY : message.s
# target to generate assembly for a file
message.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles\hello.dir\build.make CMakeFiles/hello.dir/message.c.s
.PHONY : message.c.s
# Help Target
help:
@echo The following are some of the valid targets for this Makefile:
@echo ... all (the default if no target is provided)
@echo ... clean
@echo ... depend
@echo ... edit_cache
@echo ... rebuild_cache
@echo ... hello
@echo ... main.obj
@echo ... main.i
@echo ... main.s
@echo ... message.obj
@echo ... message.i
@echo ... message.s
.PHONY : help
#=============================================================================
# Special targets to cleanup operation of make.
# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles\Makefile.cmake 0
.PHONY : cmake_check_build_system
Then just run the make
command in the current directory。
Appendix
Here are some common gcc parameters:
- -o: Specifies the name of the output file. For example, gcc -o program program.c will output the result of the compilation to an executable file named program.
- -c: Compiles and generates only the target file (.o file) without linking. For example, gcc -c program.c generates the program.o target file.
- -g: Include debugging information at compile time so that you can use a debugger (e.g. gdb) to debug your program.
- -Wall: turn on most warning messages to help find potential problems in the code.
- -Werror: convert all warnings to errors, which will cause the compilation to fail when the warning appears.
- -O0, -O1, -O2, -O3: set different optimization levels. -O0 is the default and does not optimize; -O1, -O2, and -O3 provide step-by-step enhanced optimization. -O3 is the highest optimization level.
- -std=: Specify the language standard to be used, such as -std=c11 or -std=c++14, etc., which is used to compile code conforming to a particular standard.
- -I: Specify the path to search for header files. For example, gcc -I /path/to/headers program.c will look for header files in the /path/to/headers directory.
- -L and -l: Specify the library file search path (-L) and the linked library (-l). For example, gcc program.o -L/path/to/library -llibname will link a library file named liblibname.so or liblibname.a in the /path/to/library path.
- -fPIC: Generate Position Independent Code, which is usually used to compile shared libraries.
- -f: Specify the name of the makefile file.
Reference:
- GeekHour. (2024, August 21). 20分钟Makefile光速入门教程 [Video]. Bilibili. https://www.bilibili.com/video/av112994969258637/
- Kitware. (n.d.). CMake Tutorial [Webpage]. CMake. https://cmake.org/cmake/help/latest/guide/tutorial/index.html
- GCC Developer Team. (n.d.). Option Summary [Webpage]. GCC, the GNU Compiler Collection. https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html
- GNU Project. (n.d.). Automatic Variables [Webpage]. GNU Make Manual. https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
- DaveM121. (2019, February 23). Instructions for deleting files using CMD [Online forum comment]. Microsoft Answers. https://answers.microsoft.com/en-us/windows/forum/all/instructions-for-deleting-files-using-cmd/7107aa3c-c49b-42f2-ad41-42d90d356d24
Comments | NOTHING