IT/개발

make명렁어 사용법

알콩달콩아빠 2023. 11. 11. 18:14
728x90
반응형

1. make명렁어 사용법

1) make란?
- 목적: 대규모 소프트웨어 생성을 위한 기본 도구이다. 즉 여러개의 소스들을 묶어서 한번에 컴파일해주며 소스모듈의 일부를 수정했을 때에도 전체적으로 일관성을 유지시켜 주기 위해 사용.

-C dir
Makefile을 계속 읽지 말고 우선은 dir로 이동하라는 것이다. 순환 make에 사용된다.

-d
Makefile을 수행하면서 각종 정보를 모조리 출력해 준다.
(-debug) 출력량이 장난이 아님... 결과를 파일로 저장해서 읽어보면 make 의 동작을 대충 이해할 수 있다.

-h (-help)
옵션에 관한 도움말을 출력한다.


-f (-file)
file 에 해당하는 파일을 Makefile로써 취급한다.

-r
내장하고 있는 각종 규칙(Suffix rule 등)을 없는 것으로 (-no-builtin-rules)간주한다.
따라서 사용자가 규칙을 새롭게 정의해 주어야 한다.
-t (-touch)
파일의 생성 날짜를 현재 시간으로 갱신한다.

-v (-version)
make의 버전을 출력한다.

-p (-print-data-base)
make에서 내부적으로 세팅되어 있는 값들을 출력한다.


-k
make는 에러가 발생하면 도중에 실행을 포기하게 되는데 (-keep-going) -k 는 에러가 나더라도 멈추지 말고 계속 진행하라는 뜻

make file예제와 수행 시에 나타나는 에러들과 대처 방안

make를 수행하게 되면 이상한 에러에 당황을 하게 되는 경우가 많아, 도대체 어디가 틀렸는지 감을 못잡는 경우가 허다하다.
그런데 make 매뉴얼에도 에러에 대한 종류와 그 대처 방안에 대해서는 거의 언급이 없는 관계로 이 부분은 필자의 경험에 의거해서 작성한다.
(에러의 원인, 대처 방안이 모두 다 틀렸을 수도 있다는 것을 염두에 두기 바랍니다.)
make 예1)



<Makefile 예제 2>
------------------------------------------------------------------
.SUFFIXES = .c .o

CC = gcc

INC = <- include 되는 헤더 파일의 패스를 추가한다.
LIBS = <- 링크할 때 필요한 라이브러리를 추가한다.
CFLAGS = -g $(INC) <- 컴파일에 필요한 각종 옵션을 추가한다.

OBJS = <- 목적 파일의 이름을 적는다.
SRCS = <- 소스 파일의 이름을 적는다.

TARGET = <- 링크 후에 생성될 실행 파일의 이름을 적는다.

all : $(TARGET)

$(TARGET) : $(OBJS)
$(CC) -o $@ $(OBJS) $(LIBS)

dep :
gccmakedep $(INC) $(SRCS)

clean :
rm -rf $(OBJS) $(TARGET) core

new :
$(MAKE) clean
$(MAKE)

------------------------------------------------------------------
위의 예제에서 바꿔야 할 부분은 표시를 해 두었다.
자신의 파일들로 적당히 고쳐 준 다음 make dep 을 수행시켜 본다. 그러면 자동으로 의존 관계가 생성된다.
% make dep <- 자동으로 의존 관계 생성
% make <- make 동작

지금까지의 강좌를 이해하고 있다면 위의 Makefile의 독해란 어렵지 않을 것이다. 개략적인 사항만 설명하기로 한다.

.SUFFIXES = .c .o
make 내부에서 정의된 확장자 규칙을 이용하기 위한 것이다.
make는 자동적으로 .c와 .o로 끝나는 파일들간에 정의된 규칙이 있는지 찾게 되고 적당한 규칙을 찾아서 수행하게 된다.

CFLAGS = -g $(INC)
CFLAGS 매크로를 재정의 하고 있다. -g 는 디버그 정보를 추가하라는 것이고, $(INC)는 컴파일할때 필요한 include 패스를 적어 두는 곳이다.

all : $(TARGET)
make는 Makefile을 순차적으로 읽어서 가장 처음에 나오는 규칙을 수행하게 된다.
여기서 all 이란 더미타겟(dummy target)이 바로 첫 번째 타겟으로써 작용하게 된다.
관습적으로 all이란 타겟을 정의해 두는 것이 좋다. 결과 파일이 많을 때도 all의 의존 관계(dependency)로써 정의해 두면 꽤 편리하다.

dep : gccmakedep $(INC) $(SRCS)
의존 관계를 자동적으로 생성해 주기 위한 것이다. 헤더 파일의 패스까지 추가되어야 한다는 것에 주의하기 바람. 이것은 내부적으로 gcc가 작동되기 때문이다.

위의 예제를 C++ 파일에 이용하기 위해서는 .SUFFIEX , CC, CFLAGS 그리고 타겟에 대한 명령어를 아래의 내용과 같이 바꾸면 된다.

Makefile:17: *** missing separator. Stop.
Makefile을 작성할 때 명령어(command)부분은 모두 TAB 문자로 시작해야 한다고 첫 번째 장부터 강조하였다.
위의 에러는 TAB 문자를 쓰지 않았기 때문에 make가 명령어인지 아닌지를 구별 못하는 경우이다.

대처: 17번째 줄(근처)에서 명령어가 TAB 문자로 시작하게 바꾼다.

make: *** No rule to make target `io.h', needed by `read.o'. Stop.
위의 에러는 의존 관계에서 문제가 발생했기 때문이다. 즉 read.c가 io.h에 의존한다고 정의되어 있는데, io.h를 찾을 수 없다는 에러이다.

대처: 의존 관계에서 정의된 io.h가 실제로 존재하는지 조사해 본다. 없다면 그 이유를 한번 생각해 본다.
make dep를 다시 실행시켜서 의존 관계를 다시 생성시켜 주는 것도 하나의 방법이다.

Makefile:10: *** commands commence before first target. Stop.
위의 에러는 '첫 번째 타겟이 나오기 전에 명령어가 시작되었다'는 애매한 에러 메시지이다. 필자가 경험한 이 에러의 원인은 주로 긴 문장을 여러 라인에 표시를 하기 위해서 ''를 사용할 때, 이를 잘못 사용했기 때문인 것 같다. 즉 ''부분은 라인의 가장 끝문자가 되어야 하는데 실수로 ''뒤에 스페이스를 몇 개 집어넣으면 여지없이 위의 에러가 발생한다.

대처: 10번째 줄(근처)에서 ''문자가 있거든 이 문자가 라인의 가장 끝문자가 되도록 한다. 즉 ''문자 다음에 나오는 글자(스페이스가 대부분) 는 모조리 없애 버린다.

make를 수행시키면 의도했던 실행 파일은 안생기고 이상한 행동만 한다. 가령 make clean 했을 때와 같은 행동을 보인다.

make는 천재가 아니라는 점을 생각해야 한다. make는 Makefile의 내용을 읽다가 첫 번째 타겟으로 보이는 것을 자신이 생성시켜야 할 결과 파일이라고 생각한다. 따라서 clean 부분을 Makefile의 첫번째 타겟으로 정해 버리면 위와 같은 결과가 나타나게 된다.

대처: 예제 17에서 all 이라는 필요 없는 타겟을 하나 만들어 두었다. 이것은 make가 all 을 첫 번째 타겟으로 인식시키기 위함이었다. 따라서 자신이 생성시키고 싶은 결과 파일을 첫 번째 타겟이 되게 하던지, 아니면 예제 17처럼 all과 같은 더미 타겟(dummy target)을 하나 만들어 둔다. 그리고 make clean, make dep 같은 부분은 Makefile의 끝부분에 만들어 두는 것이 안전하다.

이미 컴파일했던 파일을 고치지 않았는데도 다시 컴파일한다.
이 행동은 make가 의존 관계를 모르기 때문이다. 즉 사용자가 의존 관계를 설정해 주지 않았다는 말이 된다. 따라서 make는 무조건 모든 파일을 컴파일해서 실행 파일을 만드는 일이 자신이 할 일이라고 생각하게 된다.

대처: 목적 파일, 소스 파일, 헤더 파일들의 의존 관계를 설정해 주어야 한다. gccmakedep *.c 라고 하면 Makefile의 뒷부분에 자동적으로 의존 관계를 만들어 준다. 그외의 다른 파일들에 대해서는 사용자가 적절하게 의존 관계를 설정해 주어야 한다.


<Makefile 예제 18>
------------------------------------------------------------------
.SUFFIXES = .cc .o

CXX = g++
CXXFLAGS = -g $(INC)

$(TARGET) : $(OBJS)
$(CXX) -o $@ $(OBJS) $(LIBS)

------------------------------------------------------------------
물론 각자의 취향에 따라서 Makefile을 만들어도 된다. 여기서 제시하고 있는 Makefile은 어디까지나 필자의 관점에서 만든 Makefile을 소개하고 있는 것뿐이니까...

라이브러리와의 링크가 필요한 필요한 Makefile

라이브러리를 만들기 위한 Makefile은 어떤 차이가 있을까. 앞의 예제와 거의 흡사하다.
다만 TARGET 이 실행 파일이 아니고 라이브러리라는 것뿐. 그리고 라이브러리 만드는 방법을 알고 있어야 한다는 것...

=> 참고: 라이브러리 만드는 방법을 소개하기로 한다. read.o, write.o를 libio.a로 만들어 보자.
라이브러리를 만들기 위해서는 ar 유틸리티와 ranlib 유틸리티가 필요하다. (자세한 설명은 man 을 이용)
% ar rcv libio.a read.o write.o

a - read.o <- 라이브러리에 추가 (add)
a - write.o

% ranlib libio.a <- libio.a의 색인(index)을 생성

그럼 위의 과정을 Makefile로 일반화시킨다면 어떻게 될까? 아주 조금만 생각하면 된다. 예제 17에서 TARGET를 처리하는 부분만 아래와 같이 바꾸어 보자.

<Makefile 예제 19>
------------------------------------------------------------------
TARGET = libio.a

$(TARGET) : $(OBJS)
$(AR) rcv $@ $(OBJS) <- ar rcv libio.a read.o write.o
ranlib $@ <- ranlib libio.a
------------------------------------------------------------------

ELF 기반에서 동적 라이브러리(dynamic library, shared library)를 만들어 보기로 하자.
ELF 상에서는 동적 라이브러리 만드는 방법이 이전에 비해 아주 간단해 졌다.
(옛날에 모티프 소스 가지고 동적 라이브러리 만든다고 고생한 것에 비하면 세상이 너무 좋아진 것 같음)
BSD계열의 유닉스를 사용해 본 사람이라면 비슷하다는 것을 느낄 것이다.
그럼 read.c write.c를 컴파일해서 libio.so.1을 만들어 보자.(so는 shared object를 의미, 뒤의 .1은 동적 라이브러리의 버전을 의미)


% gcc -fPIC -c read.c <- -fPIC을 추가해서 컴파일한다.
% gcc -fPIC -c write.c
% gcc -shared -Wl,-soname,libio.so.1 -o libio.so.1 read.o write.o

동적 라이브러리를 만들기 위한 옵션

위와 같이 하면 libio.so.1 가 생성된다.
사용자가 만든 동적 라이브러리를 사용하는 방법에 대해서는 간단히만 언급하기로 한다.
우선 libio.so.1 을 /usr/lib로 옮겨서 ldconfig -v 해서 라이브러리 설정을 갱신해 주던지,
아니면 LD_LIBRARY_PATH를 지정해 두면 된다. 아래는 test.c를 libio.so.1과 링크 시키는 예제이다.

% gcc -c test.c
% gcc -o test test.o -L. -lio <- 현재 디렉토리에 있다고 가정

예제 17을 약간 고쳐서 동적 라이브러리를 자동적으로 만들어 보자. 이번엔 완전한 내용의 Makefile을 소개한다.

<Makefile 에제 19>

------------------------------------------------------------------
.SUFFIXES = .c .o

CC = gcc

INC =
LIBS =
CFLAGS = -g $(INC) -fPIC <- -fPIC 추가

OBJS = read.o write.o
SRCS = read.c write.c

TARGET = libio.so.1 <- libio.so.1이 최종 파일

all : $(TARGET)
$(TARGET) : $(OBJS)
$(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS)

dep :
gccmakedep $(INC) $(SRCS)

clean :
rm -rf $(OBJS) $(TARGET) core

simple ex1)
vi Makefiletest
clean :
rm -rf mycat.o mycat

mycat : mycat.o
gcc -o mycat mycat.c

[c@localhost c]$ make -f Makefiletest clean
rm -rf mycat.o mycat
[c@localhost c]$ make -f Makefiletest
rm -rf mycat.o mycat


simple ex2)

touch a.h b.h c.h
vi main.c




#include "a.h"

extern void function_two();
extern void function_three();

int main() {
function_two();
function_three();
return 0;
}

vi 2.c

/* 2.c */
#include "a.h"
#include "b.h"

void function_two() {
}
vi 3.c

/* 3.c */
#include "b.h"
#include "c.h"

void function_three() {

}
vi Makefile


myapp: main.o 2.o 3.o
gcc -o myapp main.o 2.o 3.o
main.o: main.c a.h
gcc -c main.c
2.o: 2.c a.h b.h
gcc -c 2.c
3.o: 3.c b.h c.h
gcc -c 3.c


MACRO 를 이용한 DEBUG 추가 기능 make
=============================================
all : myapp

#Comfiler Name
CC = gcc

#Header File Position
INCLUDE = .

#Develop Option
CFLAGS = -O -Wall -ansi

myapp : main.o 2.o 3.o
$(CC) -o myapp main.o 2.o 3.o
main.o: main.c a.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
2.o : 2.c a.h b.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
3.o : 3.c b.h c.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
clean :
rm -f main.o 2.o 3.o



메크로 설명.
$? 현재 대상보다 최근에 변경된 필수 조건이 목록
$@ 현재 대상의 이름
$< 현재 필수 조건의 이름
$* 확장자를 제외하고 현재 필수 조건의 이름


-는 make 가 에러를 무시하게 한다. 예를 들어, 디렉토리를 만들기 원하고 디렉토리가 이미 존재할 경우에 에러를 무시하기 원한다면, mkdir 앞에 -를 추가하면 된다. 잠시 후에 '-'의 사용 예를 볼것이다.
@는 make 가 명령을 실행하기 표준 출력 장치로 출력하지 않도록 지시한다.
이것은 몇가지 지시어를 출력하기 위해 echo 를 사용하는 경우에 간편하다.

install 추가 옵션.
all : myapp

#Comfiler Name
CC = gcc

#INSTALL DIRECTORY
INSTDIR = ./bin
#Header File Position
INCLUDE = .

#Develop Option
CFLAGS = -O -Wall -ansi

myapp : main.o 2.o 3.o
$(CC) -o myapp main.o 2.o 3.o
main.o: main.c a.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
2.o : 2.c a.h b.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
3.o : 3.c b.h c.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
clean :
rm -f main.o 2.o 3.o
install:
@if [ -d $(INSTDIR) ];
then
cp -f myapp $(INSTDIR);
chmod a+x $(INSTDIR)/myapp;
chmod og-w $(INSTDIR)/myapp;
echo "Installed in $(INSTDIR)";
else
echo "Sorry, $(INSTDIR) does not exist";
fi



위에서 @ 는 make install 시 스크립트가 출력되지 않게 하기 위함.

MAKE 내장 규칙 출력하기 -p 옵션
make -p -f Makefile |more

=============================
Make 로 라이브러리 관리하기.
라이브러리란 .a (archive)를 가지고 오브젝트 파일의 집합을 포함 하는 파일이다.

vi Makefilelib

all : myapp

#Comfiler Name
CC = gcc

#INSTALL DIRECTORY
INSTDIR = ./bin
#Header File Position
INCLUDE = .

#Develop Option
CFLAGS = -g -Wall -ansi
#Option for release
#CFLAGS = -O -Wall -ansi

#LOCAL LIBRARY =
MYLIB = mylib.a

myapp : main.o $(MYLIB)
$(CC) -o myapp main.o $(MYLIB)
$(MYLIB) : $(MYLIB)(2.o) $(MYLIB)(3.o)
main.o : main.c a.h
2.o : 2.c a.h b.h
3.o : 3.c b.h c.h

clean :
-rm -f main.o 2.o 3.o $(MYLIB)
install:
if [ -d $(INSTDIR) ];
then
cp -f myapp $(INSTDIR);
chmod a+x $(INSTDIR)/myapp;
chmod og-w $(INSTDIR)/myapp;
echo "Installed in $(INSTDIR)";
else
echo "Sorry, $(INSTDIR) does not exist";
fi


---------------------------
comfile 결과
make -f Makefilelib
gcc -g -Wall -ansi -c -o main.o main.c
gcc -g -Wall -ansi -c -o 2.o 2.c
ar rv mylib.a 2.o
a - 2.o
gcc -g -Wall -ansi -c -o 3.o 3.c
ar rv mylib.a 3.o
a - 3.o
gcc -o myapp main.o mylib.a


파일들의 의존성 확인
[c@localhost MAKEDIR]$ gcc -MM main.c 2.c 3.c
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h


버전관리:
rcs -i
ci
co
rlog
rcsdiff

rcsdiff -r0.0 -r1.1 main.c
=================================================================
RCS file: main.c,v
rcsdiff: main.c,v: branch number 0 too low
숙제: 배포하기 patch 사용법. groff 도움말 만들기


=================================================================
cat 명령어 만들기 예제.
=================================================================
#include<stdio.h>
#include<fcntl.h>
int main(int argc,char *argv[]) {

int fd, nread;
char fname[100];

char buffer[512];
memset(fname ,0x00,sizeof(fname));
memset(buffer,0x00,sizeof(buffer));

strcpy(fname,argv[1]);
printf("fname=[%s]n",fname);
if(argc !=2) {
printf("n Usage: [%s] file_namen",argv[0]);
}

fd=open(fname, O_RDONLY);
if(fd==-1) {
printf("File Open Error Or Permission n");
exit(1);
}

while(1) {
memset(buffer,0x00,sizeof(buffer));

nread=read(fd,buffer,sizeof(buffer));

buffer[sizeof(buffer)]=0;
printf("%s", buffer);
if(nread < -1) {
perror("read error");
exit(1);
}
if(nread==0) {
break;
}
sleep(1);

//bzero(buffer,sizeof(buffer)); // buffer NULL initiallize
}
close(fd);
exit(0);
return 0;
}

설명) 함수
1. open() 시스템 함수.
파일읽기 전에 파일 오픈
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int open(cont char *pathname, int flag);

return 값: 참이면 filedescriptor , 에러 :-1

O_RDONLY :0 읽기위한 개방
O_WRONLY:1 쓰기위한 개방
O_RDWR :2 일기쓰기

2. read()
#include<unistd.h>
int read(int fd, char *buffer,int nbyte);
return 값: 참이면 일어들인 바이트수 , 파일의 끝이면 0, 에러 -1
fd: open return 값.
buffer:읽은 내용 받아올 임시 기억장소.
nbyte: sizeof(buffer);

3. close()
#include<unistd.h>
int close(int fd);
return value: true 0, 에러일 경우 -1
fd: open 함수 리턴값.


MYSQL INCLUDE 와 LIBRARY 참조 예제.
#####################
# program name :
# source name : Makefile
# programer :
# date : 2003.03.20
# description : make
######################
BIN_DIR = ./
CC = gcc
CFLAGS = -I/usr/local/mysql/include

LIB = -L/usr/local/mysql/lib/mysql -Imysqlclient
# /home/vmuhome/DEV/lib/commlib.a
# /home/vmuhome/DEV/lib/msgque.a
# -lm -lz -lsocket -lnsl

###########
APPS = mycat

OBJS = mycat.o

all: $(APPS)

clean:
rm -f core
rm -f $(APPS) $(OBJS)
############################
# mycat
############################

$(APPS) : $(OBJS)
$(CC) $(CFLAGS) -o $@ $@.o $(LIB)
#cntinitial: cntinitial.o
# $(CC) $(CFLAGS) -o $@ $@.o $(LIB)

 

출처 : make명렁어 사용법 : 네이버 블로그 (naver.com)

728x90
반응형