Linux/UNIX Programming Compile & Makefile 문양세 강원대학교 IT대학 컴퓨터과학전공
C 프로그램 컴파일 (1/9) Compile & Makefile 대부분의 유닉스 유틸리티와 상용 프로그램들은 C(또는 C++)로 작성되어 있음 (Linix/Unix 자체가 C언어로 작성되어 있음) 일부 Linix/Unix 시스템에는 C 컴파일러가 기본적으로 내장되어 있으며, C 언어를 알지 못하고서는 시스템을 깊이 있게 이해하기 어려움 최근 C 컴파일러를 Linix/Unix와 별도로 판매하는 것이 일반적임 (cc) 공개된 C 컴파일러로 gcc (GNU cc) 컴파일러가 널리 사용됨 C 컴파일러 사용법 $ cc [-options] C-files $ gcc [-options] C-files
C 프로그램 컴파일 (2/9) 기본적인 사용법 $ cc hello.c Compile & Makefile 기본적인 사용법 $ cc hello.c hello.c를 컴파일하여 object file인 hello.o를 만듦 또한, 실행 가능한 파일(executable file)로서 a.out을 만듦
C 프로그램 컴파일 (3/9) 주요 옵션(-c) $ cc –c hello.c Compile & Makefile 주요 옵션(-c) $ cc –c hello.c Object code(기계어 코드)만 생성하며, 실행 파일은 만들지 않음 상기 예의 경우, hello.c를 컴파일하여 object code인 hello.o를 만듦
C 프로그램 컴파일 (4/9) Compile & Makefile 주요 옵션(-o) $ cc –o hello hello.c ($ cc –o hello hello.o) C 컴파일러는 실행 파일의 default 이름으로 a.out을 생성하며, 상기 –o 옵션을 사용하여 원하는 파일명으로 실행파일을 바꿀 수 있음 여러 C 파일(혹은 object file)을 묶어서 하나의 실행 파일을 생성할 수도 있음
C 프로그램 컴파일 (5/9) 주요 옵션(-g) $ cc –g –o hello hello.c Compile & Makefile 주요 옵션(-g) $ cc –g –o hello hello.c 컴파일 시 object file 내에 디버깅 정보를 포함시킴 추후 디버거(dbx, gdb)를 사용하여 디버깅을 수행할 수 있음
C 프로그램 컴파일 (5/9) 주요 옵션(-g) $ cc –g –o hello hello.c Compile & Makefile 주요 옵션(-g) $ cc –g –o hello hello.c 컴파일 시 object file 내에 디버깅 정보를 포함시킴 추후 디버거(dbx, gdb)를 사용하여 디버깅을 수행할 수 있음 Debugger는 프로그래밍을 하는데 있어서 매우 유용한 tool이므로, 개인적으로 반드시 숙지할 것
C 프로그램 컴파일 (6/9) 주요 옵션(-p (gcc의 경우 –pg)) $ cc –p –o hello hello.c Compile & Makefile 주요 옵션(-p (gcc의 경우 –pg)) $ cc –p –o hello hello.c Profiler를 사용하기 위한 정보를 포함시킴 Profiler를 사용하면, 함수의 반복 횟수 및 수행 시간을 파악할 수 있음
C 프로그램 컴파일 (6/9) 주요 옵션(-p (gcc의 경우 –pg)) $ cc –p –o hello hello.c Compile & Makefile 주요 옵션(-p (gcc의 경우 –pg)) $ cc –p –o hello hello.c Profiler를 사용하기 위한 정보를 포함시킴 Profiler를 사용하면, 함수의 반복 횟수 및 수행 시간을 파악할 수 있음
C 프로그램 컴파일 (6/9) 주요 옵션(-p (gcc의 경우 –pg)) $ cc –p –o hello hello.c Compile & Makefile 주요 옵션(-p (gcc의 경우 –pg)) $ cc –p –o hello hello.c Profiler를 사용하기 위한 정보를 포함시킴 Profiler를 사용하면, 함수의 반복 횟수 및 수행 시간을 파악할 수 있음
C 프로그램 컴파일 (7/9) 주요 옵션(-O) $ cc –O –o hello hello.c Compile & Makefile 주요 옵션(-O) $ cc –O –o hello hello.c 최적화(optimization) 컴파일을 수행함 컴파일러에 따라 -O이외에 –O3, -O4 등의 다양한 최적화 옵션을 제공함
C 프로그램 컴파일 (8/9) 주요 옵션(-S) $ cc –S hello.c Compile & Makefile 주요 옵션(-S) $ cc –S hello.c 어셈블리 언어(assembly language)로 컴파일 함 상기 예의 경우 hello.s의 어셈블리 파일이 생성됨
C 프로그램 컴파일 (8/9) 주요 옵션(-S) $ cc –S hello.c Compile & Makefile 주요 옵션(-S) $ cc –S hello.c 어셈블리 언어(assembly language)로 컴파일 함 상기 예의 경우 hello.s의 어셈블리 파일이 생성됨
C 프로그램 컴파일 (9/9) 주요 옵션(-l) $ cc –o math_hello math_hello.c -lm Compile & Makefile 주요 옵션(-l) $ cc –o math_hello math_hello.c -lm 라이브러리(대개 /usr/lib 디렉토리에 존재함)를 링크하기 위하여 사용함 -lxyz는 libxyz.a를 링크하라는 의미임(예를 들어, -lm은 libm.a의 링크를 의미함) 최근 math 라이브러니는 default로 포함시키는 경우가 일반적임 (-lm 불필요)
C 프로그램 컴파일 (9/9) 주요 옵션(-l) $ cc –o math_hello math_hello.c -lm Compile & Makefile 주요 옵션(-l) $ cc –o math_hello math_hello.c -lm 라이브러리(대개 /usr/lib 디렉토리에 존재함)를 링크하기 위하여 사용함 -lxyz는 libxyz.a를 링크하라는 의미임(예를 들어, -lm은 libm.a의 링크를 의미함)
단일 모듈 프로그램 (1/2) 하나의 C 프로그램 파일로 프로그래밍 된 경우 Compile & Makefile 하나의 C 프로그램 파일로 프로그래밍 된 경우 매우 간단한 프로그램인 경우에 단일 모듈 프로그램이 유리함
단일 모듈 프로그램 (2/2) Compile & Makefile
다중 모듈 프로그램 (1/8) 여러 개의 C 프로그램 파일들로 프로그래밍 된 경우 Compile & Makefile 여러 개의 C 프로그램 파일들로 프로그래밍 된 경우 복잡하며 대단위 프로그램인 경우에 다중 모듈 프로그램을 사용함 (일반적으로, 모든 프로그램은 다중 모듈로 구성된다고 할 수 있음) 단일 모듈 프로그램의 문제점 코드의 재사용(reuse)이 어렵고, 여러 사람이 참여하는 프로그래밍이 불가능함 예를 들어, 앞서 “main.c” 프로그램을 작성하는데 있어서의 문제점은 다른 프로그램에서 “operation_plus” 함수와 “operation_minus”함수를 사용할 수 없다는 점임 즉, 다른 프로그램에서 operation_plus 함수를 사용하고자 할 경우, 이 부분을 자르고 붙여서 원하는 프로그램에 삽입하여 사용하여야 하는 어려움이 있음
다중 모듈 프로그램 (2/8) Compile & Makefile 함수의 재사용 앞의 예에서 “operation_plus” 함수를 공유하는 방법은 “main” 프로그램에서 해당 함수를 따로 분리하여 별도 파일로 작성한 후, 해당 파일을 컴파일한 후 원하는 프로그램에 링크하여 사용하는 것임 이러한 기법은 동시에 많은 다른 프로그램들이 이 함수를 사용할 수 있게 하며 이러한 특성을 가진 함수를 재사용(reusable) 함수라 함
다중 모듈 프로그램 (3/8) Compile & Makefile operation_main.c의 소스 코드
다중 모듈 프로그램 (4/8) Compile & Makefile 재사용 함수의 준비 재사용할 수 있는 함수를 준비하기 위해서는, 1) 함수의 소스 코드를 포함하는 소스 코드 파일과 2) 함수의 prototype을 포함하는 헤더 파일을 함께 작성해야 하며, 헤더 파일은 접미어 “.h”를 갖도록 이름 지어야 한다. 그 후에 cc(gcc)의 –c 옵션을 사용하여 소스 코드 모듈을 컴파일하여 “.o” 형태의 object file을 생성한다. Object file은 실행 파일이 생성될 때 다른 object file과 결합이 가능하게 해주는 symbol table 정보와 함께 machine code를 포함한다.
다중 모듈 프로그램 (5/8) operation_plus.c와 operation_minus.c 소스 코드 Compile & Makefile operation_plus.c와 operation_minus.c 소스 코드
다중 모듈 프로그램 (6/8) operation_plus.h와 operation_minus.h 소스 코드 Compile & Makefile operation_plus.h와 operation_minus.h 소스 코드 헤더 파일에 포함되는 일반적인 내용 함수의 prototype (상기 파일이 예제에 해당함) 상수에 대한 정의 (#define MY_PI 3.141592) 전역 변수에 대한 정의 (extern int common_pi)
다중 모듈 프로그램 (7/8) 개별 파일을 각기 컴파일하고 링크하기 Compile & Makefile 개별 파일을 각기 컴파일하고 링크하기 각 소스 코드 파일을 개별적으로 번역하기 위해서는 cc(gcc)의 –c 옵션을 사용 $ gcc –c operation_main.c 각 소스 코드 파일은 “.o”를 갖는 각기 다른 object file을 생성하게 됨 각각의 object file을 링크하여 실행 파일을 생성함
다중 모듈 프로그램 (8/8) 다중 모듈 프로그램의 효과적인 컴파일 방법 Compile & Makefile 다중 모듈 프로그램의 효과적인 컴파일 방법 변경된 파일에 대해서만 컴파일을 수행하고, 파일들의 연관 관계(dependency)에 따라서 필요한 파일만 다시 컴파일하며, 복잡한 형태의 대단위 프로그램의 컴파일을 용이하게 하기 위하여, Makefile을 작성하고 make 명령어를 사용하여 컴파일을 수행함
다중 모듈 프로그램 (8/8) 다중 모듈 프로그램의 효과적인 컴파일 방법 Compile & Makefile 다중 모듈 프로그램의 효과적인 컴파일 방법 변경된 파일에 대해서만 컴파일을 수행하고, 파일들의 연관 관계(dependency)에 따라서 필요한 파일만 다시 컴파일하며, 복잡한 형태의 대단위 프로그램의 컴파일을 용이하게 하기 위하여, Makefile을 작성하고 make 명령어를 사용하여 컴파일을 수행함
Makefile (1/10) Makefile의 필요성 Compile & Makefile Makefile의 필요성 다중 모듈 프로그램은 재사용성과 디스크 공간의 개념에서는 효과적이지만, 유지하는데 신중성이 필요 예를 들어, “operation_plus.c” 소스 코드를 수정한다면, 이 함수를 사용했던 모든 함수와 다시 링크 작업을 거쳐 실행파일을 생성해야 함 비록 이러한 것이 큰 문제는 아닌 것처럼 보여도, 수천 개의 object file과 수백 개의 실행 파일을 갖는 시스템에서는 헤더, 소스 코드 파일, object file, 실행 파일의 모든 관계를 기억한다는 것은 매우 힘든 작업임 Makefile과 make 유틸리티를 사용하여 효과적인 관리가 가능함
Makefile (2/10) “make” 유틸리티 Compile & Makefile “make” 유틸리티 Make 유틸리티는 실행 파일에 대해 파일의 상호 의존 관계의 목록을 갖는 Makefile을 생성하도록 허용함 일단 Makefile 파일이 생성되면, 실행 파일을 다시 만드는 것은 매우 쉬운 작업이 됨 $ make [-f makefile_name] make는 “Makefile”이라는 특수한 형식에 저장되어 있는 일련의 의존 규칙들에 근거하여 파일을 최신 버전으로 개정하는 유틸리티임 “-f” 옵션은 make의 대상(입력)이 되는 Makefile 이름을 명시할 수 있게 하며, 파일 이름이 명시하지 않으면 default로 “Makefile”을 입력으로 간주
Makefile (3/10) Makefile 구성 Compile & Makefile Makefile 구성 “make”를 사용하여 실행 파일을 관리하기 위해서는 우선 Makefile을 만들어야 함 이 파일은 실행 파일을 만들기 위해서 사용되는 파일들 사이에 존재하는 상호 의존 관계의 목록을 포함해야 함 Makefile은 어떠한 이름이라도 가질 수 있지만 (일반적으로) 실행 파일의 이름에 “.make”(혹은 “.mk”)라는 확장자를 붙여 다른 파일들과 구분함 Makefile의 일반적인 구성형식은 다음과 같음 targetList: dependencyList commandList targetList은 object file(or 실행 파일)의 목록이고, dependencyList는 targetList에 있는 파일들이 의존하는 파일의 목록이며, commandList는 명령어의 목록으로 의존 파일로부터 object 파일을 재구성함 명령어 리스트 내에 있는 각각의 줄은 탭(tab) 문자에 의해서 시작되어야 함
Makefile (4/10) Makefile 구성 (계속) Compile & Makefile Makefile 구성 (계속) 예를 들어, 실행파일 “main”과 관련된 파일 상호 의존에 대하여 살펴보면 이 파일은 operation_plus.o와 operation_minus.o 와 operation_main.o 등의 object file로 구성 만일 세 개의 파일 중 어느 하나의 파일이 변경되었다면, 컴파일러를 사용하여 이들 파일을 링크함으로써, main은 재구성될 수 있음 그러므로 “main.mk”내의 하나의 규칙은 다음과 같다. main: operation_main.o operation_plus.o operation_minus.o gcc –o main operation_main.o operation_plus.o operation_minus.o 이제 세 개의 목적파일에 대해서도 동일한 과정을 전개하여야 함
Makefile (5/10) Makefile 구성 (계속) Compile & Makefile Makefile 구성 (계속) 파일 operation_main.o는 세 개의 파일 “operation_main.c”, “operation_plus.h”, “operation_minus.h”에 관계됨 즉, 이 세 개의 파일 중 하나라도 변경되면 “operation_main.c”를 컴파일하여 “operation_main.o”가 재구성되어야 함 다음은 main.mk내의 나머지 규칙을 나타낸다. operation_main.o: operatoin_main.c operation_plus.h operation_minus.h gcc –c operation_main.c operation_plus.o: operatoin_plus.c operation_plus.h gcc –c operation_plus.c operation_minus.o: operatoin_minus.c operation_minus.h gcc –c operation_minus.c
Makefile (6/10) Make 규칙의 순서 main operation_main.o operation_plus.o Compile & Makefile Make 규칙의 순서 make 유틸리티는 첫 번째 규칙을 초기에 조사함으로써, 상호 의존을 나타내는 트리를 생성한다. 첫 번째 규칙에 있는 각 object file은 의존 트리의 루트가 되고, 그것들의 의존 리스트에 있는 파일은 각 루트 노드의 리프 노드로서 추가 됨 main operation_main.o operation_plus.o operation_minus.o 초기의 make 의존 트리
Makefile (7/10) Make 규칙의 순서 (계속) main operation_main.o Compile & Makefile Make 규칙의 순서 (계속) make 유틸리티는 의존 리스트 내의 각 파일과 연관된 각 규칙에 대해서도, 동일한 동작을 반복한다. 앞의 예에 대한 최종적인 트리는 다음과 같다. main operation_main.o operation_plus.o operation_minus.o “_main.c “_plus.h “_minus.h “_plus.c “_plus.h “_minus.c “_minus.h 최종 make 의존 트리
Makefile (8/10) Make 규칙의 순서 (계속) main operation_main.o Compile & Makefile Make 규칙의 순서 (계속) make 유틸리티는 리프 노드로부터 루트 노드까지 진행(traverse)하며, 부모 노드의 마지막 수정 시간보다 자식 노드의 마지막 수정 시간이 더 최근인지를 살펴본다. 이와 같이 수정시간을 비교하면서 명령어(컴파일)를 수행하여 최근 버전을 생성한다. main 11 8 9 10 operation_main.o operation_plus.o operation_minus.o “_main.c “_plus.h “_minus.h “_plus.c “_plus.h “_minus.c “_minus.h 1 2 3 4 5 6 7 make의 실행순서 정하기
Makefile (9/10) Compile & Makefile Makefile 예제: main.mk
Makefile (10/10) Makefile의 실행 ($ make [-f makefile]) Compile & Makefile Makefile의 실행 ($ make [-f makefile])
Homework#6