2/14/2016

GNU Make 사용법 및 다양한 규칙

1. GNU Make의 Manual 

개발자라고 하면, Make를 모르는 사람이 없을 것이지만, 직접 Makefile을 처음부터 작성해야하는 일은 요즘 드문일 같다.
하지만, Makefile을 수정해야하는 일과 간혹은 처음부터 만들어야 하는 경우가 존재하며,
그때마다 opensource의 Makefile 파일을 참조하면서 보게 되지만, 매번 Makefile을 작성하기가 힘이 든다. 그래서 아래와 같이 정리하고 한다.

보통 Linux에서는 GNU Make사용하지만 다른 플랫폼에서 사용되는 Make와 완벽하게 호환이 된다고 착각하지 말기 바란다.
기본규칙과 GNU Make 내부 규칙을 가급적 분리해서 알아야 두면 편하다.

2.  Make의 기본구성 및 구조 

GNU Make or Make의 기본구성은 3가지로 구성 되며, 각 용어와 구성내용을 알아보자.
Makefile 분석을 하면 항상 기본형태는 아래와 같이 구성이 되며, 세부내용들은
천천히 메뉴얼과 함께 봐야할 부분들이다.

  • targets :  targets 으로 말하며, file name도 있으며, 일종 함수 같은 존재
  • prerequisites : 현재 targets의 recipe를 실행하기전에 다른 targets 들을 순차적으로 호출 
  • recipe : 실제 동작하는 기능이며, command 및 shell script 내부구현.  

targets : prerequisites     
        recipe
        …
# prerequisites는 스페이스로 구분자를 사용한다. 

targets : prerequisites ; recipe        
        recipe
        ....
# 세미콜론은 prerequisites 의 종료를 의미한다.

  • Simple Test Example 
다음은 내가 TEST 하기 위해 만든 간단한 예제이며, 각 내용을 주석으로  #로 표시하고  정리했다.

TARGET = MainTest            # Makefile안에 변수가 사용이 가능
                             # 변수는 암묵적으로 사용가능한 변수도 존재 CFLAGS, LDFLAGS

OBJS = tst1.o tst2.o         # tst1.o tst2.o  tst1.c와 tst2.c의 filname이며, target 지정가능
                             # Make 내부에 기본적으로 설정된 규칙이 있으며, SUFFIX 및 PATTERN에 의해 변경가능    
                             # 작성자가 규칙과 세부설정을 선언하지 않았다면 기본규칙처리

# make를 실행하면 기본으로 all이 실행된다. 

all: $(TARGET)

# $(OBJS)는 prerequisites들 중 하나이므로 먼저 실행 한다 .  최종 $(TARGET) 실행한다.  
# 
 
$(TARGET): $(OBJS)
        @echo ---------------------------------      
        @echo     Test Result Program
        @echo ---------------------------------
        @echo $?
#
# $? 는 The names of all the prerequisites 이므로, tst1.o , tst2.o 출력 
#

clean:
        @rm -rf *.o

#  @ 는 echo의 실행화면을 감춘다. 


  https://www.gnu.org/software/make/manual/html_node/Rule-Syntax.html#Rule-Syntax


2.1 Make의 기본구성의 규칙

Make의 Rule은 위에서 언급한 target prerequisites recipe 의 다양한 기능과 구조를 익히면될것 같다.

자세한 내용은 아래의 링크된 Manual을 참조

  • target 의 다양한 기능 
target 는 wildcard 와 변수도 사용가능하며, 다중 target 등에 다양하게 사용이 가능하다.
target의 특정목적의 target도 존재하니 이부분도 익혀두자.

  • prerequisites의 기능 및 자동기능 
기본적으로 두가지 Type의 기능이 존재하며, '|'로 이를 구분한다.

normal-prerequisites | order-only-prerequisites


1. normal-prerequisites 
target 보다 먼저 순서적으로 prerequisites 진행하지만, 종속적인 관계를 유지하고
prerequisites가 target 보다 시간적으로 새거라면  현재 target은 쓸모가 없어지고 동작되지 않는다.
쉽게말해서 빌드 할경우 수정하지 않은 파일을 빌드하지 못하게하는 기능이라고 생각하면된다.
위에서 의 기준은 항상 시간이며, 빌드를 할 경우 수정된 시간은 Make에게 중요하게 영향을 미친다.


2. order-only-prerequisites
위와 상관없이 강제로 순서대로 실행하는 방법이다.


  https://www.gnu.org/software/make/manual/html_node/Rules.html#Rules
  http://korea.gnu.org/manual/4check/make-3.77/ko/make_4.html#SEC19

  • recipe 의 규칙 
recipe의 기본 rule인 tab 과 항상 시작하는 것이고 중요한 것은 재귀용법인 것 같다.
그리고, 이부분이 핵심이기에 이 부분은 따로 분리해서 설명하겠다.

  https://www.gnu.org/software/make/manual/html_node/Recipes.html#Recipes


2.2 Make의 기본규칙 

Make의 기본구성과 그 사용법을 알았으니, 자세한 동작방식에 대해 알아보자
이를 알아보려면, Make 내부에서 암묵적으로 사용하되는 변수들을 알아야한다.

  • Make 내부에서 Filename에서 wildcard 사용법  
Make의 내부에 기본 wildcard 사용법을 익혀보자, '*' 와 '?' 사용이 주가 될것 같다.
주의사항이 이 내용은 filename에만 적용이 되고, shell에 의존적이다.

TEST를 위해 아래와 같이 구성했다

$ ls
Makefile  build  clean  cst.c  dst.c  est.c  print1  tst1.c  tst11.c  tst2.c  tst22222.c  tst3.c  

// *.c 는 전부 동일한 file 로 구성, build  clean print1diretory 이는 PHONY Target을 테스트

$ cat tst1.c // 전부 동일한 file들 

#include

int test01()
{
 printf("TEST 01  \n");
 return 0;
}


$  vi Makefile 

all: build

## filename or directory 와  구분 및 성능 향상이용  
.PHONY: print1 print2 print3 build clean

### 규칙에 적용될 filename 
SRC1 = *.c              ## wildcard '*' , 모든 filename 
SRC2 = $(wildcard *.c)  ## Make 내부함수이용 $(SRC1) 동일 
SRC3 = tst?.c           ## wildcard '?' , filename 중 한 charter 선택 
SRC3 ?= cst*.c          ## SRC3 변수가 선언이 안되었다면 설정이 된다.  

SRC5 += $(SRC3) est*.c
SRC6 += $(SRC3) dst*.c

# 고급스럽게 ,shell 이용해보자. 
FILES := $(shell echo *.c)

# build를 하기 위해서 모든 *.c를 *.o로 변경 
# 아래와 같이 $(var)에서 pattern을 replaements로 변경한다. 
# $(wildcard *.c) 모든 *.c를 에서 %.c ->%.o 로 변경  
#
# $(patsubst pattern,replacement,$(var)) 

OBJS = $(patsubst %.c,%.o,$(wildcard *.c))  # *.c를 *.o로 변경 

## wildcard '*' , 모든 filename 
clean:
        @rm -f *.o  

# print1 과 print2 는 결과 동일 
print1: ${SRC1}
        @echo $?

print2: ${SRC2}
        @echo $? 
#       @cat $? 

# tst1.c tst2.c tst3.c
print3: ${SRC3}
        @echo $? 
#       @cat $? 

# tst1.c tst2.c tst3.c est.c  
print4: ${SRC5}
        @echo $? 

# tst1.c tst2.c tst3.c dst.c  
print5: ${SRC6}
        @echo $?

# print1,2와 동일 
files: ${FILES}
        @echo $?

# buld *.o로 변경이 되었기 때문에 build 
build: ${OBJS}
        @echo $?  

  https://www.gnu.org/software/make/manual/html_node/Wildcards.html#Wildcards


  • 기본예제 
Makefile의 간단한 예제이며, 사용하기 쉬운 기본예제
  https://wiki.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-7.html


  • 에러처리  (중요)
Makefile을 만들다 보면 분명 에러가 발생한다 아래의 사이트를 참고해보자.
많은도움을 받는다.
  https://wiki.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-8.html


  • Make 의 변수 설정 및  추가 
  https://www.gnu.org/software/make/manual/html_node/Setting.html#Setting 
  https://www.gnu.org/software/make/manual/html_node/Appending.html#index-appending-to-variables
  https://www.gnu.org/software/make/manual/html_node/Shell-Function.html#Shell-Function

  • Make 의 Automatic Variables (자동변수)

공통된 규칙은 '$'로 시작이되며, target과 prerequisites 을 기준으로 이 변수를 사용을 한다.
사용되는곳은 recipe에서 주로 적용하여 사용한다.
사실 매번 사용법을 잊어버려 Manual을 보기때문에, 자세한 내용은 Manual을 보자.
주의사항은 prerequisite은 동작모드가 2가지 모드이니 이부분을 잘 이해하고 보기바란다.

  1. $@  : The file name of the target of the rule. (반드시 알아두자)
  2. $%  : The target member name
  3. $?  : The names of all the prerequisites
  4. $<  : The name of the first prerequisite
  5. $|  : The names of all the order-only prerequisites

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


  • Make의 Special Target 
Make내부에서 사용되는 특별한 Target이며, 선언하여 이를 사용한다.
많이 사용되는 것 두개를 다루면 다른 Special Target은 Manual 참조

.POHNY :  target 과 filename을 구분과 성능향상을 위해서 사용

 상위에서도 간단하게 작성했지만, target의 이름과 동일한 filename or directory가 존재한다면, 
make가 문제가 생긴다. 이를 방지하고자 사용하며, 이를 이용하면, 이를 체크할 필요가 없기에 성능이 향상이 된다.  

.SUFFIXES : The prerequisites of the special target, 즉  prerequisites의 접미사 규칙

.SUFFIXES : .c .o
.SUFFIXES : .cpp .o     ## 본인이 정의 하면 된다. 

.c.o:           ## 이와 같이 직접 정의 해줘도 정의 해줘도 되며, 세부설정이 가능하다. 
        $(CC) $(CFLAGS) $(INC_DIR) -c $<


  https://www.gnu.org/software/make/manual/html_node/Special-Targets.html#Special-Targets
  https://wiki.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make-3.html


  • Make의 빌드에 관련된 변수 (Implicit-Variables)
Make 내부에는 묵시적으로 빌드에 관련된 설정 및 옵션이 정해져 있지만 이를 본인 변경이 가능하다.
특히 임베디드에서는 이부분을 별도로 설정을 해야한다.

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

  • Make의 Pattern Rule  
Make 내부에는 묵시적으로 정해진 Pattern 규칙이 존재하지만, 이를 변경을 해야하는 경우가 많이 발생한다.
정확히 말해 Suffix 의 Pattern을 변경할때 필요하다. 

2.3 Make의 기능의 확장 

상위에서도 간단하게 사용했지만, GNU make 내부에 함수가 존재하며, 아래와 같이
이를 이용하면 편한하게 Make 작성이 가능하다.

  • Make 내부 안에서 사용가능한 함수 (중요)
  Make 내부에서 사용가능한 Macro 같은 함수로 다양한 함수를 제공하고 있다.

$(subst from,to,text)  // 대체하는 함수 

$(subst ee,EE,feet on the street)     //  fEEt on the strEEt’, ee->EE 대체 

$(filter pattern…,text) // filter 역할함수

sources := foo.c bar.c baz.s ugh.h
foo: $(sources)
        cc $(filter %.c %.s,$(sources)) -o foo       // foo.c bar.c baz.s 만 빌드 

$(foreach var,list,text)  // shell script의 반복문과 유사하다 , text는 결과로 생각하면 되겠다. 

dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))   // a/* b/* c/* /d* 


  https://www.gnu.org/software/make/manual/html_node/Functions.html#Functions


  • Make 내부의 조건문 (변수와 함께 사용)

ifeq ($(CC),gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
else
        $(CC) -o foo $(objects) $(normal_libs)
endif


  https://www.gnu.org/software/make/manual/html_node/Conditionals.html#Conditionals
  https://www.gnu.org/software/make/manual/html_node/Conditional-Example.html#Conditional-Example
  https://www.gnu.org/software/make/manual/html_node/Conditional-Syntax.html


3.  Make의 recipe 구현 

make의 recipe는 실제의 동작이며, 가장 중요한 부분이기에 어떻게 사용할 것인지,
응용은 어떻게 할것인지에 대해 좀 자세히 알아보자

3.1 recipe의 Linux command 적용 

Linux 내부에서 사용되는 다양한 command를 이용하여, Make 에 적용하는 방법이다.
가장 많이 사용하는 command를 아래와 같이 구성했으며, 이를 보고 간단히 구현을 해보자.

@는 command의 실행을보여주지 않는다.
`` 먼저 이 command를 실행한 후 결과를 연결해준다. (파이프 개념)  (stdout -> stdin)
 export를 이용하여 변수에 할당 및 변수값 조정을 했었는데, 이부분은 지금 힘든것 같다.
(다른방법으로 찾아보자, 가능하면 user mode에서 해결하자 )

 $ vi Makefile 
test:
        @echo     finished sub direcory 
clean:
        @rm -rf *.o
install:
        install -d $(DESTDIR)   ## 존재하지 않는 디렉토리 생성 
        install ./MainTest $(DESTDIR) ##생성 후 이를 이 디렉토리에 복사 

test-version-recur1:
        @echo    Building other program1      ##다른 Make program 관리
        @cd test-rec; cd `find . -name "*test*"`; make   CROSS_COMPILE=$(CROSS_COMPILE)

test-version-recur2:
        @echo    Building other program2      ##다른 Make program 관리
        make -J 2 -C $(TEST_PATH)/test-*   ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE)  


##   @을 사용하면, 사용하는 command 실행되는 과정을 안보이게 할수 있다. 


3.2  Make의 recipe 에 script 적용

Shell Script의 기능을 Make 안에 적용하는 것이지만 변수 사용법 이부분은 아래의 변수사용법 참조

이를 이용하면 좀 더 복잡하고 정교한 Make를 만들 것이 가능하다.
이 뿐만 아니라, sed , awk 등 다양한 것을 적용가능하다.
기본 Script를 익혀서 이를 Makefile 내부에 적용을 해보자

  • 주의 및 확인사항 ( 반드시 확인 )
  1. 보통 line이 길어지는 '\'을 사용하여 다음줄로 한다. 이때 '\' 다음에  \n로 하고 빈공간을 없애자.
  2. Script 내부에 세미콜론(;)을 주의하자 
  3. Makefile은 탭이 중요하기때문에 다시 확인하자
  4. Script 실행되는 것을 되는것을 보이지 않게하려면 @사용하여 전체를 가리자. 
  5. Loop 문의 구분자, 즉 변수들을 구분하는 구분자는 스페이스이다. 
아래의 예제들은 Makefile 안에 반드시 탭과 함께 넣어야 해야 동작을 한다.
그리고 위에서 설명했듯이 Loop의 List 변수의 구분자는 스페이스 이다.

  • Recipe 안의 변수 사용법
  receipe 안에서 변수 사용할경우, 반드시 $$로 사용해야한다
  https://www.gnu.org/software/make/manual/html_node/Variables-in-Recipes.html


A. For Loop 예제-1   (recipe 안에서  $$사용) 


  1. $SUB_DIRS에서 하나씩 가져와서 dir 변수를 사용하여 반복되는 Loop문 구조
  2. @을 한번사용하여 전체적용  

@for dir in $(SUB_DIRS); 
do \
     $(MAKE) -C $$dir clean; \
done

B. For Loop 예제-2

아래와 같이 1 2 3의 구분자는 스페이스이다.
  1. 상위와 같지만, 1,2,3을 직접넣어 변수 i에 작동하는 Loop 구조
  2. @을 하나를 사용하여 전체 적용하여, 변수는 $${i} or $$i로 사용

@for i in 1 2 3; \
do \
      echo TEST$${i}_$$i ; \
done

C. For Loop 예제-3

  1. 상위와 기본구조가 같지만 이중루프로 동작
  2. @을 한번사용하여 전체적용

@for i in 1 2 3; \
do \
    for j in 1 2 3 4 5; \
        do \
          echo TEST$${i}_$$j ; \
       done; \
done

D. IF문 및 EXIT 예제-1  (File 및 Directory 존재 여부 확인)

  1. if 문 File 및 Diretory으로 점검하는 구조
  2. exit 1는 에러로 표시하여 추후 재귀호출시 에러점검.

@if [ -f test]; then \
    echo "You have test file \                   
else \
    echo You have no test file \
    exit 1; 
fi

@if [ ! -d $(DIR) ]; then \
    echo "You have no test directory  \                   
fi

@if [ -e $(FILE) ]; then \
    echo "You have test file \                   
fi

@if [ -d test]; then \
    echo "You have test directory \                   
else \
    echo You have no test directory \
    exit 1; 
fi


  • IF/LOOP및 기타 기본예제 
  http://linuxcommand.org/wss0090.php
  http://egaoneko.github.io/os/2015/05/24/linux-starter-guide-8.html#fnref:1

  • IF/CASE 예제
  http://atcamp.co.kr/cosmos/solar/read.php?board=class&uid=13&cafeid=23

  • Bash Script 여러예제
  http://www.thegeekstuff.com/tag/bash-tutorial/

  • Bash Loop/awk 활용 
  http://www.thegeekstuff.com/2011/07/bash-for-loop-examples/?utm_source=feedburner


4. GCC의 다양한 Option 설정 

이부분은 상위 Implicit-Variables 와 연관이 있으며 Make 내부에 GCC의 옵션 변경을 할때 아래와 같이 GCC Manual을 이용하여 설정을 변경하자

아래의 GCC 옵션들은 GCC-5.4.0 기준이며, 본인이 사용하는 GCC version에 맞추어
문서를 찾아 Index를 찾으면 될 것이다.

  • Index 주의사항 
   '-' 이 제거가 되어있기에  ABCD 순서대로만 찾으면 된다.
  https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Option-Index.html#Option-Index

  • ARM 용 별도의 option 
  https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/ARM-Options.html#ARM-Options


5. Make의 관련 다양한 예제

Make의 기본 기능은 Target과 그의 종속된 Target 을 연결하여 실행하는 것이다.
그리고, Make를 재귀적용법으로도 실행을 하여 다른 Makefile을 제어도 가능하다.

  • Simple Makefile 
  http://korea.gnu.org/manual/4check/make-3.77/ko/make_2.html#SEC6

  • 사용목적과 기본사용방법
  최근에 찾은사이트로 기본사용법이 잘나와 있어 좋다.
  https://www.joinc.co.kr/w/Site/C/Documents/minzkn_make
  http://developinghappiness.com/?p=28


  • Make의 재귀적 용법사용
  1.  일반적으로는 make -C 옵션을 주어 내부에서 재귀적으로 호출한다. 
  2.  다른방법은 cd로 직접 해당주소로 가서 make를 실행을 한다. 
  https://www.gnu.org/software/make/manual/html_node/Recursion.html#Recursion



6. GNU Make Manual 관련사항 

  • GNU Make의 전체 Manual  
      https://www.gnu.org/software/make/manual/html_node/index.html#SEC_Contents
      http://korea.gnu.org/manual/4check/make-3.77/ko/make_toc.html
      https://wiki.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make.html#toc8

    GNU Make의 Manual 내용이 많다보니, 상위 Manual에서도 아래와 같이 읽어보라고 권하고 있다.

    • GNU Make의 Manual 
    1. Introduction  : 소개 
    2. Features : 기본 기능 
    3. Incompatibilities and Missing Features : 비호환성과 빠진 기능 
    4. Special Target : 내부에 암묵적으로 사용가능한 Target 
    5. Quick-Reference:  Make에 익숙하다면, 많이 도움이되는 Page 
    6. Options-Summary: Make의 재귀를 사용시 Make의 option 기능을 알수 있다. 
      https://www.gnu.org/software/make/manual/html_node/Introduction.html#Introduction
      https://www.gnu.org/software/make/manual/html_node/Features.html#Features
      https://www.gnu.org/software/make/manual/html_node/Missing.html#Missing
      https://www.gnu.org/software/make/manual/html_node/Special-Targets.html#Special-Targets
      https://www.gnu.org/software/make/manual/html_node/Quick-Reference.html#Quick-Reference
      https://www.gnu.org/software/make/manual/html_node/Options-Summary.html#Options-Summary

    • GNU Make Index
      https://www.gnu.org/software/make/manual/html_node/Concept-Index.html#Concept-Index


    7. Kernel Makefile 관련내용 

    Kernel Makefile을 보면 상위부분은 복잡하지만, 밑으로 갈수록으로 모듈화가 잘되어있어
    사용하기가 편하다.
    U-Boot Makefile 역시 비슷하지만 다르게 구성이 되어있지만, 유사한 부분이 많이 있다.

    솔직히 Kernel Makefile과 U-Boot Makefile은 수정할 일이 거의 없으며 간단한 Config 규칙과 Makefile 추가하는 식을 알면된다.

    • obj-y obj-n -obj-m 설정 

      https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt

    댓글 없음 :