>  기사  >  운영 및 유지보수  >  리눅스에서 gmake란 무엇인가?

리눅스에서 gmake란 무엇인가?

青灯夜游
青灯夜游원래의
2022-11-09 19:04:413036검색

Linux에서 gmake는 C 언어 소프트웨어 구축에 널리 사용되는 프로그램인 GUN make이며, Linux 커널과 기타 일반적으로 사용되는 GNU/Linux 프로그램 및 소프트웨어 라이브러리를 구축하는 데 사용됩니다. GNU Make는 셸 명령을 자동화하고 반복적인 작업을 수행하는 데 도움을 주는 프로그램으로, 소스 코드 파일을 프로그램이나 라이브러리로 컴파일하는 등 파일을 다른 형식으로 변환하는 데 자주 사용됩니다.

리눅스에서 gmake란 무엇인가?

이 튜토리얼의 운영 환경: linux7.3 시스템, Dell G3 컴퓨터.

gmake는 GUN make입니다. Linux 이외의 플랫폼에서는 일반적으로 make가 차지하므로 GUN make를 gmake라고 불러야 합니다.

GNU Make는 C 언어 소프트웨어 구축에 널리 사용되는 프로그램입니다. Linux 커널과 일반적으로 사용되는 기타 GNU/Linux 프로그램 및 소프트웨어 라이브러리를 빌드하는 데 사용됩니다.

대부분의 임베디드 소프트웨어 개발자는 경력의 어느 시점에서 작은 라이브러리를 컴파일하거나 전체 프로젝트를 구축하는 데 GNU Make를 사용할 것입니다. Make에는 수많은 대안이 있지만 기능 세트와 광범위한 지원으로 인해 여전히 새로운 소프트웨어의 빌드 시스템으로 선택되는 경우가 많습니다.

이 문서에서는 GNU Make의 일반적인 개념과 기능을 설명하고 Make 빌드를 최대한 활용하는 방법에 대한 조언이 포함되어 있습니다. 이것은 제가 가장 좋아하는/가장 일반적으로 사용되는 Make 개념과 기능에 대한 간략한 소개입니다. GNU Make는?

GNU Make는 쉘 명령을 자동화하고 반복적인 작업을 수행하는 데 도움을 줄 수 있는 프로그램입니다. 소스 코드 파일을 프로그램이나 라이브러리로 컴파일하는 등 파일을 다른 형식으로 변환하는 데 자주 사용됩니다. 이는 전제 조건을 추적하고 명령 계층을 실행하여 대상을 생성함으로써 수행됩니다.

GNU Make 매뉴얼은 꽤 길지만 제가 찾은 최고의 참고자료이므로 꼭 읽어보시길 권합니다: https://www.gnu.org/software/make/manual/html_node/index.html

Make를 선택해야 하는 경우

Make는 다른 프로젝트의 빌드 시스템에 포함될 소규모 C/C++ 프로젝트 또는 라이브러리를 빌드하는 데 적합합니다. 대부분의 빌드 시스템에는 make 기반 하위 프로젝트를 통합하는 방법이 있습니다. 대규모 프로젝트의 경우 보다 현대적인 빌드 시스템을 사용하기가 더 쉽습니다.

다음과 같은 상황에서는 Make가 아닌 빌드 시스템을 사용하는 것이 좋습니다.

빌드되는 대상(또는 파일) 수가 수백 개(또는 결국 수백 개가 될 경우)입니다. 변수, 대상 정의 및 환경 구성을 설정하고 저장하는 "구성" 단계가 필요합니다. 프로젝트는 내부 또는 비공개로 유지되며 최종 사용자가 구축할 필요가 없습니다. 디버깅이 실망스러운 노력이라는 것을 알 수 있습니다. 빌드해야 하는 것은 크로스 플랫폼이며 macOS, Linux 및 Windows에서 빌드할 수 있습니다. 이러한 경우 CMake, Bazel, Meson 또는 기타 최신 빌드 시스템을 사용하는 것이 더 즐거운 경험일 수 있습니다.

Make 호출

make를 실행하면 현재 디렉터리에서 Makefile이라는 파일이 로드되고 기본 대상을 업데이트하려고 시도합니다(대상에 대해서는 나중에 자세히 설명). Make는 GNUmakefile, makefile 및 makefile이라는 이름의 파일을 순서대로 검색합니다.

-f/ --file 매개변수를 사용하여 특정 makefile을 지정할 수 있습니다:

$ make -f foo.mk 대상 수에 제한 없이 지정할 수 있습니다. 위치 인수로 나열하십시오.

#Typical Target $ make clean all -C 인수를 사용하여 Make 디렉토리를 전달할 수 있으며 처음에 해당 디렉토리에 CD를 넣은 것처럼 Make가 실행됩니다.

$ make -C some/sub/directory 재미있는 사실: git을 -C와 함께 실행하여 동일한 효과를 얻을 수도 있습니다!

병렬 호출

-j 또는 -l 옵션이 제공되면 Make 작업을 병렬로 실행할 수 있습니다. 제가 들었던 한 가지 지침은 작업 제한을 프로세서 코어 수의 1.5배로 설정하라는 것입니다: #코어가 4개인 머신:

make -j

make -j 흥미롭게도 -l "load Limit" 옵션을 사용하는 것을 발견했습니다. -j "job" 옵션을 사용하는 것보다 CPU 활용도가 약간 더 좋습니다. YMMV

현재 머신의 CPU 수를 프로그래밍 방식으로 찾는 방법에는 여러 가지가 있습니다. 간단한 방법은 python multiprocessing.cpu_count() 함수를 사용하여 시스템이 지원하는 스레드 수를 얻는 것입니다(하이퍼 스레드 시스템의 경우 이는 많은 컴퓨터 리소스를 소비하지만 시스템을 허용하는 것보다 더 나을 수 있음). 무한한 작업을 생성합니다). #서브셸에서 Python의 cpu_count() 함수 호출

make -l

(python -c "import multiprocessing;print (multiprocessing.cpu_count())")

병렬 호출 중 출력

Make가 다음과 같은 경우 출력이 많은 명령을 병렬로 실행하면 stdout에서 출력이 엇갈리게 표시될 수 있습니다. 이 문제를 해결하기 위해 Make에는 출력 -동기화 옵션이 있습니다.

각 목표가 완료되면 다른 레시피의 출력을 방해하지 않고 레시피의 전체 출력을 인쇄하는 --output-sync=recurse를 사용하는 것이 좋습니다.

레시피가 recursive Make를 사용하는 경우 전체 recursive Make의 출력도 함께 출력합니다.

Makefile 분석 Makefile에는 대상 생성 규칙이 포함되어 있습니다. Makefile의 일부 기본 구성 요소는 다음과 같습니다.

#Comments are prefixed with the '#' symbol

#A variable assignment
FOO = "hello there!"

#A rule creating target "test", with "test.c" as a prerequisite
test: test.c
 # The contents of a rule is called the "recipe", and is
 # typically composed of one or more shell commands.
 # It must be indented from the target name (historically with
 # tabs, spaces are permitted)

 # Using the variable "FOO"
 echo $(FOO)

 # Calling the C compiler using a predefined variable naming
 # the default C compiler, '$(CC)'
 $(CC) test.c -o test

위 예제의 각 부분을 살펴보겠습니다.

Variables

변수는 $(FOO) 구문을 사용합니다. 여기서 FOO는 변수 이름입니다.

Make에는 다른 데이터 유형이 없기 때문에 변수에는 일반 문자열이 포함됩니다. 변수에 추가하면 공백과 새 내용이 추가됩니다.

FOO = one
FOO += two
# FOO is now "one two"

FOO = one
FOO = $(FOO)two
# FOO is now "onetwo"

변수 할당

GNU Make 구문에서 변수는 두 가지 방법으로 할당됩니다.

오른쪽 표현식은 리터럴 할당입니다. - 이는 변수를 사용할 때 표현식을 평가하는 C/C++의 매크로와 매우 유사합니다.

FOO = 1
BAR = $(FOO)
FOO = 2
# prints BAR=2
$(info BAR=$(BAR))

표현식의 결과를 변수에 할당합니다. 할당 중에 표현식이 확장됩니다. : 위의 $(info…) 함수는 표현식을 인쇄하는 데 사용되며, 이는 makefile을 디버깅할 때 매우 편리합니다. *'

명시적, 암시적 또는 자동으로 설정되지 않은 변수는 빈 문자열로 평가됩니다.

환경 변수

환경 변수는 Make 실행 환경으로 전달됩니다. 다음 makefile을 예로 들어 보겠습니다.

FOO = 1
BAR := $(FOO)
FOO = 2
# prints BAR=1
$(info BAR=$(BAR))

make를 실행할 때 쉘 명령에서 YOLO 변수를 설정하면 다음 값이 설정됩니다.

$(info YOLO variable = $(YOLO))

참고: Make는 "대상 없음" 오류를 인쇄합니다. makefile에 나열된 대상이 없습니다!

?= 할당 구문을 사용하면 Make는 변수에 값이 없는 경우에만 값을 할당합니다.

Makefile:

$ YOLO="hello there!" make
YOLO variable = hello there!
make: *** No targets.  Stop.

그런 다음 $(CC)를 재정의할 수 있습니다. makefile :

$ CC=clang make

또 다른 일반적인 패턴은 추가 플래그 삽입을 허용하는 것입니다. makefile에서는 변수를 직접 할당하는 대신 변수를 추가합니다.

CFLAGS += -Wall

이를 통해 환경에서 추가 플래그를 전달할 수 있습니다.$ CC=clang make

另一个常见的模式是允许插入额外的标志。在makefile中,我们将追加变量而不是直接赋值给它。

CFLAGS += -Wall

#默认CC为gcc
CC ? = gcc

이것은 매우 유용합니다!

가장 중요한 변수

사용되는 특별한 범주의 변수를 재정의 변수라고 합니다. 이 명령줄 옵션을 사용하면 환경이나 Makefile에 설정된 값이 무시됩니다!

Makefile:

$ CFLAGS='-Werror=conversion -Werror=double-promotion' make

Command:
# any value set elsewhere
YOLO = "not overridden"
$(info $(YOLO))

Targeted Variables

이 변수는 레시피 상황에 따라 사용 가능합니다. 또한 필수 레시피와도 작동합니다!

# setting "YOLO" to different values in the environment + makefile + overriding
# variable, yields the overriding value
$ YOLO="environment set" make YOLO='overridden!!'
overridden!!
make: *** No targets.  Stop.

암시적 변수

이러한 변수는 Make에 의해 사전 정의됩니다(동일한 이름의 다른 변수 유형으로 재정의되지 않는 한). 몇 가지 일반적인 예:

# set the -g value to CFLAGS
# applies to the prog.o/foo.o/bar.o recipes too!
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
 echo $(CFLAGS) # will print '-g'

자동 변수

이러한 변수는 Make에서 설정하고 레시피 컨텍스트에서 사용할 수 있는 특수 변수입니다. 중복된 이름을 방지하는 데 유용합니다(반복하지 마세요).

몇 가지 일반적인 자동 변수:

$(CC) - the C compiler (gcc)
$(AR) - archive program (ar)
$(CFLAGS) - flags for the C compiler
Full list here:

https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html

target(target)

target은 규칙 구문의 왼쪽입니다.

# $@ : the target name, here it would be "test.txt"
test.txt:
 echo HEYO > $@

# $^ : name of all the prerequisites
all.zip: foo.txt test.txt
 # run the gzip command with all the prerequisites "$^", outputting to the
 # name of the target, "$@"
 gzip -c $^ > $@
See more at: https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html

target은 거의 항상 명명된 파일입니다. 이는 Make가 마지막 수정 시간을 사용하여 대상이 전제 조건보다 최신인지 오래된지, 다시 빌드해야 하는지 여부를 추적하기 때문입니다.
Make를 호출할 때 빌드하려는 대상을 다음과 같이 지정하여 지정할 수 있습니다. 위치 인수 :

arget: prerequisite
 recipe

如果您没有在命令中指定目标,Make将使用makefile中指定的第一个目标,称为“默认目标”(如果需要,也可以覆盖默认目标)。

虚假phony目标

有时候设置元目标是很有用的,比如all, clean, test等等。在这些情况下,您不希望Make检查名为all/clean等的文件。

Make提供.PHONY目标语法,将目标标记为不指向文件:

假设我们的项目构建了一个程序和一个库foo和foo.a;如果我们想要 在默认情况下,我们可以创建一个'all'规则来构建两者 .PHONY:all all : foo foo.a

如果你有多个假目标,一个好的模式可能是将每个目标都附加到定义它的.PHONY中:

# the 'all' rule that builds and tests. Note that it's listed first to make it
# the default rule
.PHONY: all
all: build test

# compile foo.c into a program 'foo'
foo: foo.c
 $(CC) foo.c -o foo

# compile foo-lib.c into a library 'foo.a'
foo.a: foo-lib.c
 # compile the object file
 $(CC) foo-lib.c -c foo-lib.o
 # use ar to create a static library containing our object file. using the
 # '$@' variable here to specify the rule target 'foo.a'
 $(AR) rcs $@ foo-lib.o

# a phony rule that builds our project; just contains a prerequisite of the
# library + program
.PHONY: build
build: foo foo.a

# a phony rule that runs our test harness. has the 'build' target as a
# prerequisite! Make will make sure (pardon the pun) the build rule executes
# first
.PHONY: test
test: build
 ./run-tests.sh

请注意! !. phony目标总是被认为是过期的,因此Make将总是运行这些目标的配方(因此也运行任何具有. phony先决条件的目标!)小心使用! !

隐式规则

隐含规则由Make提供。我发现使用它们会让人感到困惑,因为在幕后发生了太多的行为。你偶尔会在野外遇到它们,所以要小心。

# this will compile 'test.c' with the default $(CC), $(CFLAGS), into the program
# 'test'. it will handle prerequisite tracking on test.c
test: test.o
Full list of implicit rules here:

https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html

模式的规则

模式规则允许你编写一个通用规则,通过模式匹配应用于多个目标:

# Note the use of the '$<&#39; automatic variable, specifying the first
# prerequisite, which is the .c file
%.o: %.c
 $(CC) -c $< -o $@

or

OBJ_FILES = foo.o bar.o

# Use CC to link foo.o + bar.o into &#39;program&#39;. Note the use of the &#39;$^&#39;
# automatic variable, specifying ALL the prerequisites (all the OBJ_FILES)
# should be part of the link command
program: $(OBJ_FILES)
    $(CC) -o $@ $^

先决条件

如上所述,Make将在运行规则之前检查这些目标。它们可以是文件或其他目标。

如果任何先决条件比目标更新(修改时间),Make将运行目标规则。

在C项目中,你可能有一个将C文件转换为目标文件的规则,如果C文件发生变化,你希望目标文件重新生成:

foo.o: foo.c
 # use automatic variables for the input and output file names
 $(CC) $^ -c $@

自动的先决条件

对于C语言项目来说,一个非常重要的考虑是,如果C文件的#include头文件发生了变化,那么将触发重新编译。这是通过gcc/clang的-M编译器标志完成的,它将输出一个.d文件,然后用Make include指令导入。

.d文件将包含.c文件的必要先决条件,因此任何头文件的更改都会导致重新构建。

点击这里查看更多详情:

https://www.gnu.org/software/make/manual/html_node/Automatic-Prerequisites.html

http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/

基本形式可能是:

# these are the compiler flags for emitting the dependency tracking file. Note
# the usage of the &#39;$<&#39; automatic variable
DEPFLAGS = -MMD -MP -MF $<.d

test.o: test.c
    $(CC) $(DEPFLAGS) $< -c $@

# bring in the prerequisites by including all the .d files. prefix the line with
# &#39;-&#39; to prevent an error if any of the files do not exist
-include $(wildcard *.d)

Order-only 先决条件

这些先决条件只有在不存在的情况下才会构建;如果它们比目标更新,则不会触发目标重新构建。

典型的用法是为输出文件创建一个目录;将文件发送到目录将更新其mtime属性,但我们不希望由此触发重新构建。

OUTPUT_DIR = build

# output the .o to the build directory, which we add as an order-only
# prerequisite- anything right of the | pipe is considered order-only
$(OUTPUT_DIR)/test.o: test.c | $(OUTPUT_DIR)
 $(CC) -c $^ -o $@

# rule to make the directory
$(OUTPUT_DIR):
 mkdir -p $@

recipe

“recipe”是创建目标时要执行的shell命令列表。它们被传递到子shell中(默认为/bin/sh)。如果target在recipe运行后更新,则认为规则是成功的(但如果没有更新,则不视为错误)。

foo.txt:
 # a simple recipe
 echo HEYO > $@

如果配方中的任何一行返回非零退出代码,Make将终止并打印一条错误消息。你可以通过前缀-字符来告诉Make忽略非零退出码:

.PHONY: clean
clean:
 # we don't care if rm fails
 -rm -r ./build

在recipe行前面加上@将禁止在执行之前echo该行:

clean:
 @# this recipe will just print 'About to clean everything!'
 @# prefixing the shell comment lines '#' here also prevents them from
 @# appearing during execution
 @echo About to clean everything!

Make会在运行recipe上下文中展开变量/函数表达式,但不会处理它。如果你想访问shell变量,请使用$:

USER = linus

print-user:
 # print out the shell variable $USER
 echo $$USER

 # print out the make variable USER
 echo $(USER)

function

Make函数的调用语法如下:

$(function-name arguments) 其中arguments是用逗号分隔的参数列表。

For example:

FILES=$(wildcard *.c)

# you can combine function calls; here we strip the suffix off of $(FILES) with
# the $(basename) function, then add the .o suffix
O_FILES=$(addsuffix .o,$(basename $(FILES)))

# note that the GNU Make Manual suggests an alternate form for this particular
# operation:
O_FILES=$(FILES:.c=.o)

用户定义函数

reverse = $(2) $(1)

foo = $(call reverse,a,b)

# recursive wildcard (use it instead of $(shell find . -name '*.c'))
# taken from https://stackoverflow.com/a/18258352
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))

C_FILES = $(call rwildcard,.,*.c)

shell函数

你可以让Make调用一个shell表达式并捕获结果:

TODAYS_DATE=$(shell date --iso-8601)

不过,我在使用这个功能时很谨慎;它会增加对你使用的任何程序的依赖,所以如果你正在调用更奇特的程序,确保你的构建环境是受控的(例如在容器中或使用Conda)。

make的条件表达式

FOO=yolo
ifeq ($(FOO),yolo)
$(info foo is yolo!)
else
$(info foo is not yolo :( )
endif

# testing if a variable is set; unset variables are empty
ifneq ($(FOO),)  # checking if FOO is blank
$(info FOO is unset)
endif

# "complex conditional"
ifeq ($(FOO),yolo)
$(info foo is yolo)
else ifeq ($(FOO), heyo)
$(info foo is heyo)
else
$(info foo is not yolo or heyo :( )
endif

make include

sources.mk:

SOURCE_FILES :=
bar.c
foo.c \

Makefile:

include sources.mk

OBJECT_FILES = $(SOURCE_FILES:.c=.o)

%.o: %.c (CC) -c ^ -o $@

make eval

# generate rules for xml->json in some weird world
FILES = $(wildcard inputfile/*.xml)

# create a user-defined function that generates rules
define GENERATE_RULE =
$(eval
# prereq rule for creating output directory
$(1)_OUT_DIR = $(dir $(1))/$(1)_out
$(1)_OUT_DIR:
 mkdir -p $@

# rule that calls a script on the input file and produces $@ target
$(1)_OUT_DIR/$(1).json: $(1) | $(1)_OUT_DIR
 ./convert-xml-to-json.sh $(1) $@
)

# add the target to the all rule
all: $(1)_OUT_DIR/$(1).json
endef

# produce the rules
.PHONY: all
all:

$(foreach file,$(FILES),$(call GENERATE_RULE,$(file)))

请注意,使用Make的这个特性的方法可能会让人很困惑,添加一些有用的注释来解释意图是什么,对您未来的自己会很有用!

VPATH

VPATH是一个特殊的Make变量,它包含Make在查找先决条件和目标时应该搜索的目录列表。

它可以用来将对象文件或其他派生文件发送到./build目录中,而不是把src目录弄得乱七八糟:

# This makefile should be invoked from the temporary build directory, eg:
# $ mkdir -p build && cd ./build && make -f ../Makefile

# Derive the directory containing this Makefile
MAKEFILE_DIR = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))

# now inform Make we should look for prerequisites from the root directory as
# well as the cwd
VPATH += $(MAKEFILE_DIR)

SRC_FILES = $(wildcard $(MAKEFILE_DIR)/src/*.c)

# Set the obj file paths to be relative to the cwd
OBJ_FILES = $(subst $(MAKEFILE_DIR)/,,$(SRC_FILES:.c=.o))

# now we can continue as if Make was running from the root directory, and not a
# subdirectory

# $(OBJ_FILES) will be built by the pattern rule below
foo.a: $(OBJ_FILES)
 $(AR) rcs $@ $(OBJ_FILES)

# pattern rule; since we added ROOT_DIR to VPATH, Make can find prerequisites
# like `src/test.c` when running from the build directory!
%.o: %.c
 # create the directory tree for the output file  
 echo $@
 mkdir -p $(dir $@)
 # compile
 $(CC) -c $^ -o $@

touch file

# our tools are stored in tools.tar.gz, and downloaded from a server
TOOLS_ARCHIVE = tools.tar.gz
TOOLS_URL = https://httpbin.org/get

# the rule to download the tools using wget
$(TOOLS_ARCHIVE):
 wget $(TOOLS_URL) -O $(TOOLS_ARCHIVE)

# rule to unpack them
tools-unpacked.dummy: $(TOOLS_ARCHIVE)
 # running this command results in a directory.. but how do we know it
 # completed, without a file to track?
 tar xzvf $^
 # use the touch command to record completion in a dummy file
 touch $@

调试makefile

对于小问题,我通常使用printf的Make等效函数,即$(info/warning/error)函数,例如当检查不工作的条件路径时:

ifeq ($(CC),clang)
$(error whoops, clang not supported!)
endif

要调试为什么规则在不应该运行的情况下运行(或者相反),可以使用——debug选项:https://www.gnu.org/software/make/manual/html_node/Options-Summary.html

我建议在使用此选项时将stdout重定向到文件,它会产生大量输出。

profile

For profiling a make invocation (e.g. for attempting to improve compilation times), this tool can be useful:

https://github.com/rocky/remake

Check out the tips here for compilation-related performance improvements:

https://interrupt.memfault.com/blog/improving-compilation-times-c-cpp-projects

verbose flag

# Makefile for building the 'example' binary from C sources

# Verbose flag
ifeq ($(V),1)
Q :=
else
Q := @
endif

# The build folder, for all generated output. This should normally be included
# in a .gitignore rule
BUILD_FOLDER := build

# Default all rule will build the 'example' target, which here is an executable
.PHONY:
all: $(BUILD_FOLDER)/example

# List of C source files. Putting this in a separate variable, with a file on
# each line, makes it easy to add files later (and makes it easier to see
# additions in pull requests). Larger projects might use a wildcard to locate
# source files automatically.
SRC_FILES = \
    src/example.c \
    src/main.c

# Generate a list of .o files from the .c files. Prefix them with the build
# folder to output the files there
OBJ_FILES = $(addprefix $(BUILD_FOLDER)/,$(SRC_FILES:.c=.o))

# Generate a list of depfiles, used to track includes. The file name is the same
# as the object files with the .d extension added
DEP_FILES = $(addsuffix .d,$(OBJ_FILES))

# Flags to generate the .d dependency-tracking files when we compile.  It's
# named the same as the target file with the .d extension
DEPFLAGS = -MMD -MP -MF $@.d

# Include the dependency tracking files
-include $(DEP_FILES)

# List of include dirs. These are put into CFLAGS.
INCLUDE_DIRS = \
    src/

# Prefix the include dirs with '-I' when passing them to the compiler
CFLAGS += $(addprefix -I,$(INCLUDE_DIRS))

# Set some compiler flags we need. Note that we're appending to the CFLAGS
# variable
CFLAGS += \
    -std=c11 \
    -Wall \
    -Werror \
    -ffunction-sections -fdata-sections \
    -Og \
    -g3

# Our project requires some linker flags: garbage collect sections, output a
# .map file
LDFLAGS += \
    -Wl,--gc-sections,-Map,$@.map

# Set LDLIBS to specify linking with libm, the math library
LDLIBS += \
    -lm

# The rule for compiling the SRC_FILES into OBJ_FILES
$(BUILD_FOLDER)/%.o: %.c
 @echo Compiling $(notdir $<)
 @# Create the folder structure for the output file
 @mkdir -p $(dir $@)
 $(Q) $(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@

# The rule for building the executable "example", using OBJ_FILES as
# prerequisites. Since we're not relying on an implicit rule, we need to
# explicity list CFLAGS, LDFLAGS, LDLIBS
$(BUILD_FOLDER)/example: $(OBJ_FILES)
 @echo Linking $(notdir $@)
 $(Q) $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@

# Remove debug information for a smaller executable. An embedded project might
# instead using [arm-none-eabi-]objcopy to convert the ELF file to a raw binary
# suitable to be written to an embedded device
STRIPPED_OUTPUT = $(BUILD_FOLDER)/example-stripped

$(STRIPPED_OUTPUT): $(BUILD_FOLDER)/example
 @echo Stripping $(notdir $@)
 $(Q)objcopy --strip-debug $^ $@

# Since all our generated output is placed into the build folder, our clean rule
# is simple. Prefix the recipe line with '-' to not error if the build folder
# doesn't exist (the -f flag for rm also has this effect)
.PHONY: clean
clean:
 - rm -rf $(BUILD_FOLDER)

$ V=1 make

make presents

Make:

target을 최대한 활용하기 위한 제안 목록은 일반적으로 실제 파일이어야 합니다. 하위 MAKE 명령을 실행할 때는 항상 (MAKE)를 사용하세요. .phony 타겟을 사용하지 마세요. 규칙이 파일 아티팩트를 생성하는 경우 이를 가장하는 대신 대상 지정을 고려하십시오. 암시적 규칙을 사용하지 마십시오. C 파일의 경우 자동으로 추적을 포함하려면 .d를 사용해야 합니다. 주의해서 메타프로그래밍을 사용하세요. 규칙에서 자동 변수를 사용합니다. 규칙과 Make의 경로가 정확히 동일하도록 항상 @를 레시피 출력 경로로 사용하십시오. 특히 복잡한 동작이나 미묘한 구문이 사용되는 경우 makefile에서 주석을 자유롭게 사용하십시오. 당신의 동료들(그리고 미래의 자신)도 당신에게 감사할 것입니다. -j 또는 -l 옵션을 사용하여 Make를 병렬로 실행하세요! 규칙 완료를 추적하기 위해 touch 명령을 사용하지 마세요.

기타

오픈 소스 프로젝트에서 automake를 만날 수도 있습니다(./configure를 찾으세요). 스크립트). 이것은 makefile 생성을 위한 관련 도구이며 살펴볼 가치가 있습니다(특히 광범위하게 이식해야 하는 C 소프트웨어를 작성하는 경우).

오늘날 GNU Make에는 많은 경쟁자가 있으므로 모두가 그 경쟁자를 조사해 보시기 바랍니다. 몇 가지 예:

CMake는 매우 인기가 높으며(Zephyr 프로젝트에서 사용함) 살펴볼 가치가 있습니다. 트리 외부 빌드를 매우 쉽게 만듭니다. Bazel은 선언적 구문을 사용합니다(Make의 명령형 접근 방식과 비교). Meson은 cmake와 같은 메타 빌더이지만 기본적으로 Ninja를 백엔드로 사용하며 매우 빠릅니다.

관련 권장 사항: "Linux 영상 튜토리얼"

위 내용은 리눅스에서 gmake란 무엇인가?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.