이 블로그는 Web 환경을 이용한 원격 제어 기술에 필요한 지식을 공유 하기 위한 블로그 입니다.
실제 개발과 프로그램 예를 위하여 Raspberry Pi와 Raspberry Pi Pico, ATmega128 보드, Arduino Mega 보드(ATmega2560), WiFi 모듈을 사용 합니다.

raspberry-linux-gcc-make

Raspberry - Linux - gcc - make
Raspberry - Linux - gcc - make


  • gcc c 컴파일러(Compiler)
    • gcc 개요
    • gcc(GNU Compiler Collection)는 자선 단체인 자유 소프트웨어 재단(FSF: Free Software Foundation)의 지원으로 GNU(GNU's Not UNIX) 프로젝트에 의해 만들어진 c 컴파일러(Compiler) 이기 때문에 누구나 무료로 자유롭게 사용 할 수 있다. 원래 C 언어만 지원하여 GNU C 컴파일러 였지만 현재는 C(gcc), C++(g++), Java(gcj), Fortran(gfortran), Ada(GNAT) 등 여러 언어를 컴파일 할 수 있게 확장되었다.

      • gcc의 실행 과정
      • gcc는 소스파일을 이용하여 실행파일을 만들때 필요한 프로그램을 아래 예와 같이 차례로 실행하여 실행 파일을 생성한다.

          소스파일(.c/, .c++) --> 전처리기(preprocessor)(*.i) --> 컴파일러(*.s) --> 어셈블러(*.o) --> 링커(a.out)

        • 전처리 과정: 전처리 부분에 명시된 헤더 파일을 삽입, 실행 문장의 메크로를 상수(문자열)로 변환한다.
        • 컴파일 단계: 전처리 된 파일 *.i를 이용하여 컴파일러가 어셈블리어 파일 *.s로 변환한다.
        • 어셈블 단계: 어셈블리어로 된 *.s파일을 이용하여 이진 코드로 이루어진 *.o 파일 을 생성한다.
        • 링크 단계: 링커는 라이브러리 함수와 오브젝트 파일들을 연결해 실행 가능한 파일(a.out)을 생성한다.
    • gcc 옵션
      • GCC 전역 옵션
        • gcc -v : gcc의 버전을 확인 한다.
        • -o : 출력(output) 화일명을 지정한다.
          • 예: gcc -o hello hello.c

            위 명령을 실행하면 hello.c를 컴파일하고 링크하여 hello 라는 실행 파일이 생성(출력) 된다. hello 파일을 아래 명령으로 실행 할 수 있다.

            ./hello

        • -c : 오직 컴파일(compile) 작업만 실행한다. 여러 개의 소스 파일(c or cpp)을 각각 컴파일하여 링크하는 경우에 사용한다.
          • 예: gcc -c hello.c   or   gcc -c -o hello.o hello.c

      • 전처리기(cpp0) 옵션
        • -l : 헤더 파일을 탐색할 디렉토리를 지정 (예: -l/dir/include)
        • -include [Header file path] : 해당 헤더 파일을 모든 소스에 추가 (예: -include /dir/my.h)
        • -D[macro] : 프로그램 외부에서 매크로(프로그램 내의 #define 과 동일한 효과)를 지정 (예: -DDEBUG)
        • -D[macro]=[macro value] : 프로그램 외부에서 해당 매크로를 정의하고 값을 지정 (예: -DDEBUG=1)
        • -C : 전처리 과정에서 주석을 제거하지 않는다.
        • -Wp,[Option list] : 옵션 리스트를 전처리기(preprocessor)에 바로 전달한다.
      • C컴파일러(cc1) 옵션
        • 최적화 옵션
          • -O0 : 최적화를 수행하지 않는다.
          • -O1 : 최적화 레벨 1을(-O0보다는 조금 낮은 수중)의 최적화를 수행한다.
          • -O2 : 가장 많이 사용하는 옵션. 일반 응용 프로그램이나 커널을 컴파일 할 때 사용(거의 대부분의 최적화를 수행한다.)
          • -O3 : 가장 높은 레벨의 최적화. 모든 함수를 인라인 함수와 같이 취급한다.
          • -O5 : 메모리 사이즈 최적화를 실행한다.(메모리 공간이 작은 경우 사용 - 임베디드 시스템 등)
        • 경고 옵션 : cc1의 옵션을 이용하여 경고 수위를 조절할 수 있다.
          • -Wall : 모든 모호한 코딩에 대해서 경고를 보내는 옵션
          • -W : 합법적이지만 모호한 코딩에 대해서 경고를 보내는 옵션
          • -W -Wall : 아주 사소한 모호성에 대해서도 경고가 발생한다.
          • -Werror : 모든 경고를 오류로 취급하여 컴파일 중단 한다.
      • 어셈블리(as) 옵션
        • -Wa,[Option list] : 어셈블러에게 옵션 리스트를 바로 전달한다.
        • -Wa,-al : 어셈블된 코드와 인스트럭션을 보임
        • -Wa,-as : 정의된 심볼을 보임
      • 링크(ld) 옵션
        • -L[path] : 라이브러리 탐색 디렉토리를 지정한다. (ex_: -L/dir/lib)
        • -l[path] : 해당 라이브러리를 링크 (예: -lmylib)한다. (ex_: -L/dir/lib)
        • -shared : 공유 라이브러리를 우선하여 링크
        • -static : 정적 라이브러리를 우선하여 링크
        • -nostdlib : 표준 C 라이브러리를 사용하지 않음
        • -Wl,[Option list] : 옵션 리스트를 링커(linker)에 바로 전달한다.
      • 디버깅 옵션
        • -g : 바이너리(Binary) 파일에 디버깅 정보를 삽입한다.

  • Make 설명을 위한 c project 예
    • Make 설명을 위한 c project
      • Make 설명을 위한 c project는 아래 그림과 같이 main.c, print-msg.c, print-msg.h, function.c, function.h 3개의 c 파일과 2개의 헤더 파일로 구성되어 있다.

      • 위 예에서와 같이 main.c 파일을 컴파일하여 Object File(main.o)을 생성하기 위하여는 main.c, print-msg.h, function.h 이 필요하고,
      • print-msg.c 파일을 컴파일하여 Object File(print-msg.o)을 생성하기 위하여는 print-msg.c, print-msg.h,
      • function.c 파일을 컴파일하여 Object File(function.o)을 생성하기 위하여는 function.c, function.h 파일이 필요하다.
    • Make utility를 사용하지 않고 Build 하기
      • 위 예의 c project을 Built 하기 위하여는 아래 예와 같이 순차적으로 각 c 파일을 컴파일하여 Object 파일을 만든다.
        • gcc -c -o main.o main.c

          gcc -c -o print-msg.o print-msg.c

          gcc -c -o function.o function.c

          윗 명령에서 -c 옵션은 Link 하지 않고 컴파일만 하겠다는 의미 이고, -o 옵션은 Object 파일 이름을 지정하는 옵션이다.

      • 실행 파일(a.out)은 위 명령에 의하여 생성된 Object 파일을 아래 예와 같이 Link 하여 생성한다.
        • gcc -o a.out main.o print-msg.o function.o

      • 윗 예는 개발 환경을 제공하는 툴(IDE) 없이 콘솔 명령을 사용하여 Built 하는 예로 여러개의 파일을 포함하는 큰 과제인 경우 불편하고 오류가 발생 할 가능성이 매우 크다. 이런 문제를 해결하기 위하여 아래와 같이 make 유틸리티(Utility)를 사용한다.
    • Make utility를 사용하여 Build 하기
      • 위 예의 c project을 make 유틸리티(Utility)를 사용 하여 Built 하는 예이다.

      • 위 예의 c project을 Build 하기 위한 makefile(linux 상에서 반복적으로 발생하는 컴파일을 쉽게 하기 위해서 사용하는 make Utility 프로그램의 설정 파일) 예
      • 
        a.out: main.o print-msg.o function.o
        	gcc -o a.out main.o print-msg.o function.o
        
        main.o: print-msg.h function.h main.c
        	gcc -g -Wall -c -o main.o main.c
        	
        print-msg.o: print-msg.h print-msg.c
        	gcc -g -Wall -c -o print-msg.o print-msg.c
        
        function.o: function.h function.c
        	gcc -g -Wall -c -o function.o function.c
              
      • 아래와 같이 make 명령으로 c project을 Build 한다. 이 예에서는 모든 Project 파일이 동일한 폴더(현재 작업 폴더)에 있는 것으로 가정 한다.
      • make

        make Utility는 makefile(or Makefile)의 설정에 따라 Build 동작을 실행한다.


  • Make Utility
    • Make utility는 많은 파일로 구성된 프로그램을 유지 관리하기위한 소프트웨어 도구이다. Make utility는 큰 프로그램의 어떤 부분을 다시 컴파일 해야 하는지 자동으로 결정하고 명령을 실행하여 컴파일한다.

    • Make Utility 와 Makefile
      • Make는 별도로 지정하지 않는 경우 makefile(make descriptor file)에서 실행 할 명령을 읽는다.

      • Makefile은 프로그램의 어느 부분을 다시 컴파일 하여야 하는지 결정하고, 명령을 실행하는 규칙(Rule)의 모음이다.
      • Makefile은 소프트웨어 Build를 진행하는 절차와 종속성이 있는 작업을 자동화하는 방법이다.
      • Makefile에는 종속성 규칙(dependency rules)과 매크로(macros), suffix(or implicit) rules이 포함된다.
    • Makefile의 기본 구조와 이해
      • 종속성 규칙(dependency rules)
        • Rule은 세 가지 부분(하나 이상의 Target, 0개 이상의 종속성(Dependency), 0개 이상의 명령)으로 구성된다.

        • Target의 기본형
          • target: dependencies

            <tab> target를 구현하는 명령(command)

            <tab> 문자는 여백문자(Space)로 대치 할 수 없다.

            "target"은 일반적으로 파일 이름(예: executable or object files) 또는 작업 이름(예: clean) 이다.

            Rule의 각 “command”는 shell에 의하여 해석(Interpreted)되고 실행된다.

            "make target" 형태의 명령을 입력하면 target의 모든 종속성이 최신 상태인지 확인하고, 최신 상태가 아닌 경우 지정된 명령을 사용하여 target을 다시 생성한다.

            “make” 만 명령으로 입력하면 makefile 내의 첫번째 target이 생성된다.

        • Phony targets
        • Phony targets은 실제 파일이 아닌 종속성을 갖지 않는 command의 나열(List)이다.

          예: clean:

                    rm -rf *.o

      • Macros
        • Macro를 사용하면 반복되는 텍스트 입력을 피할 수 있으며 makefile을 쉽게 수정할 수 있다.

        • Macro의 정의는 아래와 같이 한다.
        • NAME = text string

          예: CC = gcc

        • 정의된 Macro는 아래와 같이 이름을 괄호 또는 중괄호로 묶고 앞에 $ 기호를 붙여서 사용(참조) 한다.
        • $(CC) main.o print-msg.o function.o -o a.out

          윗 예에서 "$(CC)"는 "gcc"로 대치되어 "gcc main.o print-msg.o function.o -o a.out"과 동알한 명령이 된다.

        • Internal macros: Internal macros는 make에서 미리 정의된 macros 이다.
          • 아래 명령은 현재 빌드에 적용되는 모든 macro, suffix rules, target 목록을 표시한다.
          • make -p

        • Special macros
          • @ 매크로는 현재 target의 이름을 삽입한다.
          • 예:

            target1: $(objs):

                  $(CXX) -o $@ $(objs)

            위 설정은 아래 설정과 같다.

            target1: $(objs):

                  $(CXX) -o target1 $(objs)

        • 자주 사용하는 내부 매크로(Internal macro)
          • $* : 확장자가 없는 현재의 대상(Target) 파일
            • main.o : main.c io.h

                  gcc -c $*.c

              $* 는 확장자가 없는 현재의 대상 파일이므로 $* 는 main에 해당된다.

          • $@ : 현재의 대상(Target) 파일
            • test : $(OBJECTS)

                  gcc –o $@ $*.c

              $@ 는 현재의 대상 파일이므로 $* 는 test에 해당된다.

          • $< : 현재의 대상(Target) 파일보다 더 최근에 갱신된 파일 이름(Dependency중에서 처음에 위치한 파일 이름)
            • .c .o :

                  gcc -c $< (또는 gcc -c $*.c)

              $< 는 현재의 대상 파일보다 더 최근에 갱싱된 파일 이름이므로, .o 파일보다 더 최근에 갱신된 .c 파일은 자동적으로 컴파일 된다.

          • $? : 현재의 대상(Target) 파일보다 더 최근에 갱신된 파일 이름(모든 dependency 파일의 이름)
      • Suffix rules
        • 이중 접미사와 단일 접미사를 이용하여 Default rules 또는 Implicit rules을 정의하여 프로그램을 작성하는 데 사용할 수 있다.

        • Doubles-suffix는 Source suffix와 Target suffix로 정의 된다.
        • 예:

          .cpp.o:

                $(CC) $(CFLAGS) -c $<

          위 규칙은 make에게 .cpp 파일을 사용하여 .o 파일을 생성하게 한다.

          위에서 $< 는 .o 파일을 생성하는 데 사용되는 .cpp 파일을 나타내는 특수 매크로이다.

        • Suffix rules은 현재 호환성을 위해 지원되지만 더 이상 사용이 권장되지 않는다. 가능한 패턴 규칙(규칙에 문자 '%'이 포함됨)을 사용하는 것이 좋다.
        • 아래와 같이 패턴 규칙은 위에서 Suffix rule을 사용한 경우와 같은 결과를 생성한다.
        • 예:

          %.o: %.cpp

                $(CC) $(CFLAGS) -c $<

      • 명령(Command line)에서 macros 정의하기
        • Macro는 명령에서 정의 할 수 있다.

          예:

          make DEBUG_FLAG=-g

          위 경우 DEBUG_FLAG는 -g로 정의된다.

      • Make 동작에 대한 이해
        • Make utility는 대상 파일(Target file)의 수정 시간과 종속 파일(Dependency files)의 수정 시간을 비교하여 대상 파일보다 종속 파일의 수정 시간이 최근인 모든 대상 파일을 다시 생성한다.
        • 기본적(By default)으로 첫 번째 대상 파일은 빌드된 결과 파일이다. 다른 대상은 첫 번째 대상에 대한 종속성 확인에 사용된다.
        • Make utility는 필요한 순서대로 빌드하기 때문에 첫 번째 대상을 제외하고 대상의 순서는 중요하지 않다.
      • 새로 작성된 Makefile 예
        • Macro를 사용하여 작성한(위 c project을 Build 하기 위한) makefile 예
        • 
          # This is a comment line
          CC = gcc
          # CFLAGS will be the options passed to the compiler.
          CFLAGS = -g -Wall
          OBJS =  main.o print-msg.o function.o 
          
          TARGET = a.out
           
          $(TARGET): $(OBJS)
          	$(CC) ${CFLAGS} -o $@ $(OBJS)
          
          %.o: %.c
          	$(CC) $(CFLAGS) -c $<
          
          clean:
          	-rm -f *.o
          	-rm -f $(TARGET)
          
          #Dependency check
          main.o: print-msg.h function.h main.c
          print-msg.o: print-msg.h print-msg.c
          function.o: function.h function.c
                
        • 아래 명령으로 c project가 Build 된다.
        • make

        • 아래 명령으로 clean(모든 *.o 파일과 a.out 파일이 삭제됨) 명령이 실행 된다.
        • make clean

      참고자료: http://www.gnu.org/software/make/manual/html_node/