2025. 1. 3. 10:30ㆍProgramming/Linux Programming
- 목차
기본 문법
all
makefile을 작성하고 console에서 make를 실행하면 기본적으로 "all:" 부분에 정의된 내용이 수행됩니다. 다음은 간단한 makefile의 예 입니다.
OBJ = $(patsubst %c, %o, $(wildcard \*.c))
all : diary
diary: $(OBJ)
$(CC) -o $@ $^
위와 같이 makefile을 작성한 뒤 다음과 같이 make를 실행합니다.
~$ make
그럼 makefile이 위치한 폴더 내 모든 .c 파일들이 빌드되어 diary라는 파일을 생성합니다.
make options
-n, --just-print, --dry-run, --recon
"No-op"
The activity is to print what recipe would be used to make the targets up to date, but not actually execute it
-t, --touch
“Touch”. The activity is to mark the targets as up to date without actually changing them.
-q, --question
“Question”. The activity is to find out silently whether the targets are up to date already; but execute no recipe in either case
-W, --what-if=file, --assume-new=file, --new-file=file
The given files’ modification times are recorded by make as being the present time, although the actual modification times remain the same.
Automatic variables
변수 | 의미 |
---|---|
$@ | 타겟 이름 |
$% | 타겟 맴버명 |
$< | 동적으로 변화될 수 있는 미리 지정되는 필요 의존성 |
$? | 모든 필요 의존성 |
$^ | 중복이 제거된 미지 지정된 의존성 |
$+ | %^와 동일 |
$ | |
$* | 타겟 이름에서 패턴에 일치하는 부분을 제외한 부분인 stem을 참조 (fo.o에서 %.o이 패턴이면 stem은 foo) |
$(@D) | 타겟의 디렉토리 부분 |
$(@F) | 타겟의 파일 명 부분 |
Special Built-in Target Names
.PHONY
to make phony target
for making always (regardless file existence and modification time)
ex.
.PHONY: $(TARGET\_FULL) <-- to build the prerequisites always
ADVICE:
if there is target file exists, then target is not executed!
.SUFFIXES
ex.
.SUFFIXES: .c .o <-- for suffix rule check
.c.o:
#(CC) -c @< -o $@ <-- relationship between .c and .o $< (for the first thing) compile with -c, and make target as $@ with -o
.DEFAULT
describe a recipe for the target with no rule
.DEFAULT\_GOAL
selection of default goal from several targets
instead of using it, argument can be used for default target
.SUFFIXES
Makefile에서 .SUFFIXES는 중요한 확장자를 지정하는 특별한 지시자입니다. 이는 make 프로그램이 파일 확장자를 기반으로 자동으로 규칙을 적용할 때 사용됩니다.
.SUFFIXES의 주요 기능
중요 확장자 지정: .SUFFIXES 뒤에 나열된 확장자들은 make가 특별히 중요하게 처리합니다.
확장자 규칙 적용: 지정된 확장자를 가진 파일들에 대해 미리 정의된 규칙을 자동으로 적용합니다.
파일 검색 순서 결정: make는 .SUFFIXES에 나열된 확장자 순서에 따라 파일을 검색합니다.
사용 예시
makefile
.SUFFIXES: .o .c .f
%.o: %.c
$(CC) $(CFLAGS) -c $<
%.o: %.f
$(F77) $(FFLAGS) -c $<
이 예시에서 .o, .c, .f 확장자가 중요하게 취급되며, .c 파일과 .f 파일에 대한 컴파일 규칙이 자동으로 적용됩니다.
.c와 .f 입력에 대해 .o로 만드는 규칙을 지정한 경우이며 .c 파일의 경우 $(CC)를 사용해서 (보통 gcc 혹은 clang cc) 컴파일(ELF format인 object 파일화)를 수행합니다.
$<
변경될 수 있는 의존성 부분을 나타내며 a.c, a.c, c.c 파일이 있을때 각 파일이 이에 해당됩니다.
$^
역시 의존성이나 이는 변경되지 않는 의존성을 의미합니다.
diary: a.o, b.o
$(CC) -o $@ $^
여기서 target 명은 diary이며 이것의 의존성은 a.o파일과 b.o 파일입니다.
$@
타겟이름 입니다.
%o : %c 는 .c.o : 와 동일하게 처리됩니다.
.c.o:
$(CC) -c @< -o $@
주의사항
- * 확장자 규칙에서는 $^가 아닌 $<를 사용합니다.
- 기본적으로 make는 여러 확장자(.c, .f, .y, .l 등)에 대한 규칙을 내장하고 있습니다.
- .SUFFIXES를 수정하여 프로젝트에 맞는 확장자 규칙을 구성할 수 있습니다.
- .SUFFIXES를 효과적으로 사용하면 Makefile을 더 간결하고 유지보수하기 쉽게 만들 수 있습니다.
wildcard
SRC = $(wildcard \*.c)
위에서 wildcard는 폴더 내 모든 .c 파일들을 열거합니다.
OBJ = $(SRC:.c=.o)
a.c와 b.c파일이 있을때 이들을 모두 a.o와 b.o로 치환합니다.
Auto replacement
다음은 foo 변수에 할당된 .o로 끝나는 모든 이름들을 .c로 변경하고 이들을 bar라는 변수에 할당합니다.
foo:= a.o b.o c.o
bar:= $(foo:.o = .c)
conditional parts
if
$(if CONDITION, THEN-PART\[, ELSE-PART\])
: if condition expand to non-empty string --> then-part
: if condition expand to empty string --> else-part
상수값 옵션
OPT라는 조건을 지정하여 make를 수행할 수 있으며, 이는 다음과 같이 makefile 을 작성하면 됩니다.
ifdef OPT
CFLAGS = -O$(OPT) <-- calling make, make OPT=1 이런 식으로 호출하면 OPT가 정의 되어 있음
else
CFLAGS =
endif
위 makefile은 다음과 같이 실행할 수 있습니다.
'$ make' or '$ make OPT=1' or '$ make OPT=2'
for loop
makefile 내에서도 for loop를 사용한 반복문 처리를 수행할 수 있습니다.
1에서 5까지를 반복문으로 출력하고자 할때 다음과 같이 makefile을 작성할 수 있습니다.
all:
@for n in $(shell seq 1 5); do\\
echo $$n; \\
done
다음은 CC가 gcc로 정의된 경우와 그렇지 않은 경우 서로 다른 인자를 설정하여 빌드를 수행하는 에 입니다.
libs\_for\_gcc = -lgnu
normal\_libs =
foo:$(objects)
ifdq($(CC),gcc
$(CC) -o foo$(objects) $(libs\_for\_gcc)
else
$(CC) -o foo$(objects) $(normal\_libs)
endif
문자열 처리
Adding prefix/suffix
다음은 memo main 문자열 내 가 항목에 대해 .c를 suffix하는 예 입니다.
$(addsufix .c, memo main) => memo.c main.c
다음은 memo main 문자열 내 가 항목에 대해 src/를 prefix 하는 예 입니다.
$(addprefix src/, memo main) => src/memo src/main
단순 문자열 치환
문자열 치환 문법
$(subst FROM,TO,TEXT)
다음은 "feet on the street"을 "fEEt on the strEEt"으로 치환하는 예 입니다.
$(subst ee,EE,feet on the street)
pattern 치환
패턴 치환 문법
$(patsubst PATTERN, REPLACEMENT, TEXT)
다음은 x.c.c bar.c를 x.c.o bar.o로 치환하는 예 입니다.
$(patsubst %.c, %.o, x.c.c bar.c)
Functions
strip
strip은 문자열 내 공백 문자 등을 제거하는 함수 이며 문법은 다음과 같습니다.
$(strip STRING)
strip을 사용해서 문자열 내 공백문자들을 제거하려면 다음과 같이 스크립트를 작성합니다.
$(strip a b c ) <-- remove white space 'a b c'
sort
문자열 내 공백 문자로 구분된 각 청크들을 정렬해줍니다.
$(sort LIST)
$(sort foo bar lose) <-- bar foo lose
findstring
findstring은 입력 문자열 내에서 특정 문자열의 존재 유무를 확인하는 함수 입니다.
$(findstring FIND, IN)
ex.
$(filter PTTERN...,TEXT)
$(filter-out PATTERN...,TEXT)
dir
문자열 내에서 디렉토리 부분만 찾아서 반환해 주는 함수 입니다.
$(dir NAMES...)
ex.
$(dir src/foo.c hacks) --> 'src/ ./'.
dir 뒤의 나열된 file name 들 중,
directory만 찾아서 return함
join
문자열에 각 항목들을 합친 문자열을 반환합니다.
$(join a b, .c .o) --> a.c b.o
word
word n
n번째 단어를 추출합니다.
$(word 2, foo bar baz) --> bar
wordlist
$(wordlist S, E, TEXT)
words
$(words TEXT) <-- return the number of words in TEXT
firstword
$(firstword NAMES...)
foreach
foreach는 주어진 항목들을 반복하는 반복수행 함수 입니다.
$(foreach VAR, LIST, TEXT)
dirs := a b c d
files := $(foreach dir, $(dirs), $(wildcard $(dir)/\*))
$(dir) at the end means each of a b c d
put each variable in the LIST, then do text processing for each variable
that is, the same as following line
-> files := $(wildcard a/\* b/\* c/\* d/\*)
call
$(call VARIABLE, PARAM,PRAM,...)
reverse = $(2) $(1)
foo = $(call reverse, a, b) <-- return b, a instead of a, b
origin
origin은 선언된 변수가 어디서 왔는지 알아내는데 사용됨 (선언이 되어 있지 않으면 undefined가 return됨)
$(origin VARIABLE)
variable is the name of a variable to inquire about (in this reason, there is no $ in front of variable)
-> The result of this function is a string telling you how the variable variable was defined
ex.
environment <-- 환경 변수면 이것 return
file <-- makefiel 내 정의 되어 있는 경우
command line <-- command line 내 면
overrride <-- override directive로 선언된 것인 경우
automatic <-- automatic varialbe이면, (for the execution of the commands for each rule)
shell
files := $(shell echo \*.c) <-- 확장자가 c인 모든 file들이 listing
same as $(wildcard *.c)
contents := $(shell cat foo)
sets contents to the contents of the file ‘foo’, with a space (rather than a newline) separating each line.
files := $(shell echo \*.c)
Unless make is using a very strange shell, this has the same result as ‘$(wildcard *.c)’ (as long as at least one ‘.c’ file exists)
SH = $(shell ls \*.c)
shell 명령에 대한 결과가 변수에 할당됩니다.
make control
$(error TEXT...) <-- error message를 생성 (TEXT) and EXIT
$(warning TEXT...) <-- $(error) 와 동일하나 no EXIT
makedepend
$(error TEXT...) <-- error message를 생성 (TEXT) and EXIT
$(warning TEXT...) <-- $(error) 와 동일하나 no EXIT
makedepend는 makefiles내에서 dependencies를 생성합니다.
main.c로 main.o를 만드는데, main.c 내에 여러 header file들이 있을 시, header file들이 수정되면 main.c도 수정 되어야 함을 의미합니다.
이러한 include의 level이 다양할 수 있으며, 이러한 dependency들의 변경 사항을 확인해야 합니다.
makedepend는 header file과 c file간의 관계를 분석합니다. (parsing)
DO NOT DELETE THIS LINE -- make depend depends on it. <-- 이 line 부터 Makefile을 search한다.
diary.h <-- include --- memo.c -- memo.o
calendar.c -- calendar.o
main.c -- main.o
diary.h는 3개의 c file에 영향을 미침
사용예
SRCS = $(OBJS:.o=.c)\\
$(TARGET):$(OBJS)
$(CC) -o $@ $^
dep:
makedepend $(SRCS) <--- make dep을 수행하게 되면, 3개 file들이 어떤 file들에 종속적인지 분석 해 줌
clean:
rm -rf $(TARGET) $(OBJS)
$ make dep
Recursive make
source files들이 여러 directory 로 나눠져 있을때 최상위 directory 내에서의 make 한번으로 수행합니다.
subsystem:
cd subdir && $(MAKE(
or
subsystem:
$(MAKE) -C subdir
-C는 change directory 임
-- Makefile
-- calendar
-- Makefile
-- calendar.c
-- main
-- Makefile
-- diary.h
-- main.c
-- memo
-- Makefile
-- memo.c
OBJECTS = memo.o calendar.o main.o
all : MEMO =
CALENDAR \\
MAIN \\
diary
MEMO:
cd memo && make
CALENDAR:
cd calendar && make
MAIN:
cd main && make
diary: $(OBJECTS)
$(CC) -O $@ %^
clean:
cd memo && make clean
cd calendar && make clean
cd main && make clean
rm -rf \*.o diary
# make
cd memo && make
make\[1\]' Entering directory '/tmp/makefile-src/recursive make/memo' <-- \[1\]은 level을 의미 (\[0\]은 top level)
Implicit rules
자동으로 source file을 찾습니다.
foo: foo.o bar.o
cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS) <-- cc를 지정하지 않아도 자동으로 찾아서 수행
x:y.o z.o
이렇게만 하고 make하면
cc -c x.c -o x.o; cc -c y.c -o y.o 이런 식으로 자동으로 기본적인 rule을 생성해서 make를 진행한다.
Library 만들기
static library 만들기
다음은 정적 라이브러리를 만드는 예 입니다. 입력으로 file1.c과 file2.c를 받아 libmy.a 파일을 생성합니다.
gcc -c file1.c file2.c
ar rscv libmyj.a file1.o file2.o
ranlib:
lib 내의 function을 indexing
ex. OBJS에 정의된 파일들(이미 빌드 된 object 파일들)로 my lib을 생성하는 예 입니다.
$(TARET): $(OBJS)
$(AR) rscv $@ $^
Usage of a static library
gcc -o like like.c -L . -lmy <-- -L은 library를 어디서 찾을 거냐는 지정 (없을 시 표준 library directory에서 찾음)
Dynamic library
-fPIC <-- position independent compile option
-shared <-- gcc에서 shared library 생성 시 사용되는 option
libsomething.so <-- library name 명시
gcc -fPIC -g -c -Wall file1.c
gcc -fPIC -g -c -Wall file2.c
gcc -shared -WI ,-soname,libmystuff.so.1 -o libmystuff.so.1 file1.c file2.c
gcc -c -fPIC file1.c <-- compile 시 position independent 하게 object file을 생성
gcc -c -fPIC file2.c
gcc -shared -o libmy.so file1.so file2.so
Usage
gcc -o like like.o -L . -lmy <-- my만 주면 기본적으로 shared library를 먼저 찾음(만약 동일 이름의 static lib.이 있을 시)
./like을 수행하면 libmy.so를 찾을 수 없다고 ERROR msg.가 나타날 수 있음
defining library path, just like the following line, is required
export LD\_LIBRARY\_PATH=$LD\_LIBRARY\_PATH:/tmp/makefile-src/dynamic\\ library
ld.conf folder내에서 수정할 수 있도록 한다.
ldconfig <- configure dynamic linker's run-time bindings
ldd <- printing all so files for the execution file
ln -s <- create an link file for the real file
(symbolic link from a file in one subdirectory to a file in another subdirectory)
so 들 간에 사용하는 header file의 차이가 존재하면 (build time이 틀릴 시) - 외부 lib.의 경우 build를 못하니..
이런 경우에는 lib의 build time miss에 의해서 (즉, header file이 상이한채로 so들이 각각 build 되어서)
문제 야기
ex.
interface.h
class AudioSink {
...
void get\_val() = 0;
void get\_val1() = 0;
void get\_Val2() = 0;
};
class AudioOutput {
...
void get\_val();
void get\_val1();
void get\_val2();
};
lib_code.cpp
위 3개 모두 구현 각각은 printf("%s\\r\\n", \_\_FUNCTION\_\_);
interface_miss.h
get_val1 이 없음
class AudioSink {
...
void get\_val() = 0;
void get\_Val2() = 0;
};
class AudioOutput {
...
void get\_val();
void get\_val2();
};
nex.cpp
#include "interface\_miss.h"
...
AudioSink \*audio\_sink = new AudioOutput();
..
audio\_sink->get\_val2();
g++ -fPIC -c lib_code.cpp
g++ -fPIC -shared -o libnex.so nex.cpp
g++ lib_code.o app.cpp -o app -L . -lnex
./app
get_val
ex.
$(TARET): $(OBJS)
$(CC) -shared -Wl, -soname, $@ -o $@ $^
install:
cp $(TARET) /usr/lib <-- 다른 directory로 만들어진 shared library를 이동 (다른 곳에서도 사용할 수 있으니)
ldconfig -v <-- 이 자리에 새로운 library가 추가 되었다고 설정 file을 변경 하는 것
dymanic library is loading just when it is required, but it can loaded previously by using dlopen function
dlopen("~~~/\*.so", RTLD\_NOW);
'Programming > Linux Programming' 카테고리의 다른 글
cmake ctest 에서 test fail 시 log 출력 (0) | 2023.01.27 |
---|---|
rdynamic (0) | 2023.01.19 |
libfmt fPIC로 build 하기 (0) | 2022.12.27 |
CMake에서 cross compiler 지정 (0) | 2022.10.06 |
gdbus] method 등록 및 호출 (0) | 2021.09.27 |