===================================================================================================
[gcc 컴파일]
1) gcc 파일명(*.c) : Default로 out 파일이 생성된다. (ex a.out)
2) gcc -c 파일명(*.c) : 오브젝트 파일을 생성한다.
3) gcc -c 오브젝트_파일명(*.o) 파일명(*.c)
gcc -o 실행파일명(*.out) 오브젝트_파일명(*.o)
4) gcc -o 실행파일 파일명(*.c) : 실행 파일을 만든다. (3번을 한줄로...)
===================================================================================================
[gcc 옵션]
1. -Wall 옵션 : 모든 모호한 코딩에 대해서 경고를 보내는 옵션
2. -W 옵션 : 합법적이지만 모호한 코딩에 대해서 경고를 보내는 옵션
3. -W -Wall 옵션 : 아주 사소한 모호성에 대해서도 경고가 발생
4. O2 옵션 : 최적화 레벨 2로 설정. (대부분의 최적화를 시도)
5. -E 옵션 : 전처리 과정의 결과를 화면에 보이는 옵션
(전처리과정 중 발생한 오류를 검증)
※ enhanced Tip: --save-temps 옵션
6. -S 옵션 : cc1으로 전처리된 파일을 어셈블리 파일로
컴파일까지만 수행하고 멈춘다. (*.s)
7. -c 옵션 : as에 의한 어셈블까지만 수행하고 링크는 수행하지 않는다.
8. -v 옵션 : gcc가 컴파일을 어떤 식으로 수행하는지를 화면에 출력한다.
9. --save-temps 옵션 : 컴파일 과정에서 생성되는 중간 파일인
전처리 파일(*.i)과 어셈블리 파일(*.s)을 지우지 않고,
현재 디렉토리에 저장한다. (오류 분석에 사용)
===================================================================================================
[cpp0 옵션]
: 소스내에서 사용된 헤더 파일과 define 매크로와 관련된 옵션들이다.
전처리 과정에서 오류가 발생한다면 cpp0 옵션들을 점검해야 한다.
1) -l 옵션 : 전처리 과정에서 헤더 파일을 탐색하는 기본 디렉토리를 추가할 때 사용하는 옵션
2) -include 옵션 : 헤더 파일을 소스내에 추가할 때 사용한다.
3) -D[매크로] 옵션 : 매크로를 외부에서 define 할 때 사용한다.
4) -D[매크로]=[매크로 값] 옵션 : 소스 내에 #define [매크로] [매크로 값] 옵션을 추가한 것과
동일하다.
5) -U[매크로] 옵션 : -D와 반대로 소스 파일 내에 #undef[매크로] 옵션을 추가한 것과
동일하다.
6) -M / -MM 옵션 : -M 옵션 - make를 위한 소스 파일의 모든 종속 항목을 출력
-MM 옵션 - 기본 include 디렉토리에 있는 헤더 파일은 빼고 종속 항목을
출력한다.
7) -nostdinc 옵션 : 디폴트 include 디렉토리(usr/include)에서 헤더 파일을 탐색하지 않고,
-l 옵션으로 추가한 디렉토리에서만 헤더 파일을 찾는다.
8) -C 옵션 : -E 옵션과 함께 사용하며, 전처리 과정에서 주석을 제거하지 않는다.
9) -Wp,[옵션들] 옵션 : 만약 cpp0와 gcc의 옵션이 같은 것으로 중복되면 gcc 옵션으로
해석되므로... gcc의 해석을 거치지 않고 바로 cpp0 옵션으로 전달하고
싶을 때 사용한다.
===================================================================================================
[cc1 옵션]
: "C언어 옵션, 경고 옵션, 최적화 옵션, 디버깅 옵션"의 4가지 종류
"경고 수위 조절 or 최적화 수위 조절"을 하고 싶을 때 사용한다.
1. C언어 옵션 : C언어 종류와 표준에 관련된 옵션
1) -ansi 옵션 : ANSI C 표준에 부합하는 소스를 작성하고자 할 때 사용하는 옵션
2) -std=[C 표준들] 옵션 : 기타 다른 표준들을 지정하고자 할 때 사용한다.
3) -traditional 옵션 : 오래된 Traditional C 문법으로 문법을 검사한다.
4) -fno -asm 옵션 : gnu89 문법을 바탕으로 asm, inline, typeof 키워드를 사용하지 않기를
원할 때 사용한다.
2. 경고 옵션 : cc1의 옵션을 조정하여 경고 수위를 조절할 수 있다.
1) -W / -Wall 옵션 (gcc 옵션 참고)
2) -w(소문자) 옵션 : 모든 경고 메시지를 제거한다.
3) -Werror 옵션 : 모든 경고를 컴파일을 중단하는 오류로 취급한다.
(경고가 하나만 나와도 컴파일이 중단된다.)
4) -pedantic 옵션 : ANSI C89 표준에서 요구하는 모든 경고 메시지를 표시한다.
5) -pedantic-errors 옵션 : ANSI C89 표준에서 요구하는 모든 오류 메시지를 표시한다.
6) -Wtraditional 옵션 : 소스가 ANSI C와 K&R C 간에 서로 다른 결과를 가져올 수 있는
부분이 있다면 경고한다.
3. 최적화 옵션 : ⓐ 실행 파일의 크기를 줄여 메모리와 하드디스크의 사이즈를 절약 (큰 의미 X)
ⓑ 실행 파일의 크기를 줄여 실행 속도를 향상시키는 것.
1) -O0 옵션 : 최적화를 수행하지 않는다.
2) -O1 옵션 : -O0보다는 조금 낫다.
3) -O2 옵션 : 가장 많이 사용하는 옵션. 일반 응용 프로그램이나 커널을 컴파일 할 때 사용
(거의 대부분의 최적화를 수행한다.)
4) -O3 옵션 : 가장 높은 레벨의 최적화. 모든 함수를 인라인 함수와 같이 취급한다.
(Call 인스트럭션은 사용 X. but, 되도록이면 사용하지 않는 것이 좋다.
→ 너무나 많은 소스의 변경이 가해지기 때문에 왜곡이 발생할 위험이 있다.)
5) -O5 옵션 : 사이즈 최적화를 실행한다. (공간이 협소한 곳에서 사용 - 임베디드 시스템)
4. 디버깅 옵션
1) -g 옵션 : gdb에게 제공하는 정보를 바이너리에 삽입한다.
(-g 옵션을 사용하지 않고 gdb로 디버깅하면, 역어셈 → 어셈블리 코드로만 디버깅 가능)
2) -pg 옵션 : 프로파일을 위한 코드를 삽입한다.
(-pg 옵션으로 컴파일 → gmon.out(프로파일 정보) → gprof로 gmon.out 파일 분석)
===================================================================================================
[as의 옵션]
: gcc는 as의 옵션에 대해서는 알지 못한다. -Wa,[as 옵션들] 형식으로 gcc를 거치지 않고
바로 전달해야 한다. -Wa, -al, -as와 같은 형식으로 사용하면 as에게 -al -as 옵션이 같이 전해진다.
-Wa,[as 옵션들]
1) -al 옵션 : 어셈블된 인스트럭션을 보인다.
2) -as 옵션 : 정의된 심볼을 보인다.
3) -l[패스] 옵션 : include 디렉토리를 지정한다. 어셈블리 소스 내에서 사용된 include 지정자가
지정하는 헤더파일을 찾고자 할 때 사용한다.
4) -W / --no-warn : 경고 메시지를 출력하지 않는다.
5) -march=[아키텍처 문자열] : 해당 어셈블리
===================================================================================================
[collect2 / ld 옵션]
: 링크 옵션
1) -L[라이브러리 디렉토리] 옵션 : 라이브러리를 찾을 디렉토리를 지정한다.
2) -l 옵션 : 같이 링크할 라이브러리를 지정한다.
3) -shared 옵션 : 공유 라이브러리와 정적 라이브러리가 같이 있을 경우, 공유 라이브러리를 우선하여
링크한다. (아무 옵션을 주지 않아도 공유 라이브러리를 우선으로 링크한다.)
4) -static 옵션 : 정적 라이브러리와 공유 라이브러리가 같이 있다면, 정적 라이브러리를 우선하여
링크한다. (속도는 빠르지만 파일 사이즈가 커진다는 점 고려할 것!)
5) -nostdlib 옵션 : 링크시에 표준 C 라이브러리를 사용하지 않는다.
(OS, 부트로더와 같은 프로그램을 컴파일 할 때 사용)
6) -nostartfiles 옵션 : crt1.o 등과 같은 start up 파일을 링크하지 않는다.
(OS, 부트로더와 같은 프로그램을 컴파일 할 때 사용)
7) -Wl,[링크 옵션들] 옵션 : gcc를 거치지 않고 바로 링크에게 옵션을 정해주고자 할 때 사용한다.
(사용법은 -Wa와 동일한다.)
< 유용한 링크 옵션들 >
① -s 옵션 : 실행 파일에서 심볼 테이블을 제거
② -x 옵션 : 출력 파일에서 로컬 심볼 제거
③ -n 옵션 : 텍스트 영역을 읽기 전용으로 만듬
④ -r 옵션 : 추후 링크가 가능하게 오브젝트를 만듬
⑤ -e [name] 옵션 : 시작 심볼을 name 심볼로 사용 (default 시작심볼 : _start 심볼)
⑥ -M 옵션 : 심볼들의 정보를 자세하게 출력
⑦ oformat [format] 옵션 : 주어진 형식의 오브젝트 파일을 생성
===================================================================================================
출처 : http://lvzuufx.blogspot.kr/2014/09/gnu-make-gcc-auto-dependency-makefile.html
Autotools, CMake, qmake 같은 빌드 도구나 Open Watcom 의 wmake 같은 경우에는 자체적으로 auto-dependency 를 지원하거나 이를 위한 지시자를 제공한다. 하지만 GNU Make 의 경우에는 이러한 기능을 자체적으로 지원하지도 않고, 이를 위한 지시자를 제공하지도 않는다.
하지만, 다행히도 gcc 를 이용해서 이 기능을 구현할 수 있다. 우선 이에 필요한 gcc 의 기능부터 살펴보자.
-M 옵션 : 해당 소스 파일의 의존성을 GNU Make 의존성 규칙에 맞추어 출력한다. 시스템 헤더 파일들도 포함된다.
예를 들어, 다음과 같은 hello.c 를 생각해보자.
1 2 3 4 5 6 7 8 9 | // hello.c #include <stdio.h> int main( void ) { printf("Hello, world\n"); return 0; } |
- gcc -M hello.c
이를 실행한 결과는 이렇다.
- hello.o: hello.c f:/lang/gcc/usr/include/stdio.h \
- f:/lang/gcc/usr/include/sys/cdefs.h \
- f:/lang/gcc/usr/include/sys/gnu/cdefs.h \
- f:/lang/gcc/usr/include/features.h f:/lang/gcc/usr/include/sys/_types.h \
- f:/lang/gcc/usr/include/machine/_types.h \
- f:/lang/gcc/usr/include/386/_types.h
-MM 옵션 : -M 옵션과 비슷한데, 시스템 헤더 파일은 제외한다.
- gcc -MM hello.c
다음은 실행 결과이다.
- hello.o: hello.c
-MP 옵션 : -M 또는 -MM 옵션과 함께 쓰일 때, 헤더 파일들에 대한 phony 타겟을 만든다. 이는 헤더 파일이 지워졌을 때, 오류가 발생하는 것을 방지한다.
- gcc -M -MP hello.c
다음은 실행 결과이다.
- hello.o: hello.c f:/lang/gcc/usr/include/stdio.h \
- f:/lang/gcc/usr/include/sys/cdefs.h \
- f:/lang/gcc/usr/include/sys/gnu/cdefs.h \
- f:/lang/gcc/usr/include/features.h f:/lang/gcc/usr/include/sys/_types.h \
- f:/lang/gcc/usr/include/machine/_types.h \
- f:/lang/gcc/usr/include/386/_types.h
- f:/lang/gcc/usr/include/stdio.h:
- f:/lang/gcc/usr/include/sys/cdefs.h:
- f:/lang/gcc/usr/include/sys/gnu/cdefs.h:
- f:/lang/gcc/usr/include/features.h:
- f:/lang/gcc/usr/include/sys/_types.h:
- f:/lang/gcc/usr/include/machine/_types.h:
- f:/lang/gcc/usr/include/386/_types.h:
-MT 옵션 : -M 또는 -MM 옵션과 함께 쓰일 때, 타겟의 이름을 바꾼다.
- gcc -M -MT hello_MT.o hello.c
다음은 실행 결과이다.
- hello_MT.o: hello.c f:/lang/gcc/usr/include/stdio.h \
- f:/lang/gcc/usr/include/sys/cdefs.h \
- f:/lang/gcc/usr/include/sys/gnu/cdefs.h \
- f:/lang/gcc/usr/include/features.h f:/lang/gcc/usr/include/sys/_types.h \
- f:/lang/gcc/usr/include/machine/_types.h \
- f:/lang/gcc/usr/include/386/_types.h
-MF 옵션 : -M 또는 -MM 옵션과 함께 쓰일 때, 출력 결과를 해당 장치 또는 파일로 보낸다.
- gcc -M -MF hello.d hello.c
이 때 실행 결과는 표준 출력 장치에 나타나는 것이 아니라 hello.d 라는 파일에 저장된다.
바로 이 옵션들을 통해서 auto-dependecy Makefile 파일을 만들 수 있다. 문제는 이 내용을 Makefile 파일에서 어떻게 활용할 것인가이다.
의존성 파일부터 만들어 보자. 의존성 파일을 만들기 위한 기본적인 형태는 다음과 같다.
- hello.d : hello.c
- gcc -M -MP -MF $@ $<
이제는 이렇게 만들어진 의존성 파일을 포함시켜야 한다. GNU Make는 이를 위해 include 지시자를 지원한다.
- include hello.d
이때 hello.d 가 없다면 주어진 생성 규칙을 통해서 새로이 만들거나 갱신한다. 그럼에도 불구하고 hello.d 를 만들 수 없다면, GNU Make 는 오류를 보고하고, 중지한다.
마지막으로 실행 파일의 의존성 규칙을 추가해주면 된다.
- hello.exe : hello.o
물론 다음과 같은 기본적인 내용이 Makefile 앞 부분에 더 추가되어야 한다.
- .SUFFIXES : .c .o .exe
- .PHONY : all
- all : hello.exe
완성된 Makefile 의 모습은 이렇다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | .SUFFIXES : .c .o .exe .PHONY : all all : hello.exe hello.d : hello.c gcc -M -MP -MF $@ $< include hello.d hello.exe : hello.o gcc -o $@ $< |
이제 make 를 실행하면, hello.c 자체가 수정되거나 hello.c 가 포함하는 헤더 파일이 수정되면 새롭게 빌드된다.
그런데 문제는 hello.d 파일 자체는 한 번 만들어지면 hello.c 가 수정되지 않는 이상, 다시 갱신되지 않는다. 왜냐하면 hello.d 파일에 대한 의존성은 hello.c 밖에 없기 때문이다. 이 문제는 hello.d 의 의존성을 hello.o 의 의존성과 동일하게 만들어주면 된다. 위 8번째 줄을 아래와 같이 바꾸자.
- gcc -M -MP -MT "$(@:.d=.o) $@" -MF $@ $<
GNU Make 의 다중 대상 기능을 이용한 것으로. 타겟이 "hello.o" 에서 "hello.o hello.d" 로 된다.
GNU Make 를 실행하다보면 hello.d 가 지워진 경우에, 다음과 같은 오류 메세지가 출력된다.
- Makefile:10: hello.d: No such file or directory
이 오류 메세지가 나오더라도 중단되지는 않지만, 뭔가 꺼림칙하다. 어떻게 없앨 수 없을까 ? 바로 -include 를 쓰면 된다.
이 예제의 경우에는 소스 파일이 하나에 불과해서 이 정도이지만, 소스 파일이 여러 개일 경우에는 해당 소스 파일마다 일일이 추가해줘야 한다. 그리고 다른 프로젝트에 쓰기에도 수정해야 할 것이 많다. 이 얼마나 불편한가 ? 이를 개선해 보자.
우리가 고려할 것은 실행 파일과, 소스 파일이다. 다른 모든 것들은 이들로부터 변형될 수 있다. 이들을 위한 변수를 도입하자. PROGNAME 과 SRCS 에 실행파일과 소스 파일을 담자.
- PROGNAME := hello.exe
- SRCS := hello.c
이제 의존성 파일 변수와 실행 파일을 만들기 위한 목적 파일 변수를 도입하자.
- PROGNAME_DEPS := $(SRCS:.c=.o)
- DEPS := $(SRCS:.c=.d)
이 내용들을 반영하면 위 Makefile 은 다음과 같이 쓰여질 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | PROGRAM := hello.exe SRCS := hello.c PROGRAM_DEPS := $(SRCS:.c=.o) DEPS := $(SRCS:.c=.d) .SUFFIXES : .c .o .exe .PHONY : all all : $(PROGRAM) $(PROGRAM) : $(PROGRAM_DEPS) gcc -o $@ $^ %.d : %.c gcc -M -MP -MT "$(@:.d=.o) $@" -MF $@ $< -include $(DEPS) |
위 내용을 보면 특정 이름 대신에 변수를 사용한 것 외에도 몇 가지 달라진 점이 있는데, 하나는 16번째 줄에 있는 암묵적 규칙이고, 14번째 줄에 있는 $^ 이다. 암묵적 규칙은 여러개의 파일에 대해 동일한 규칙을 적용하기 위한 것이고, $^ 은 모든 의존 파일들을 뜻한다. 반면에 $< 은 첫번째 의존 파일을 뜻한다.
이렇게 해 두면, 다른 프로젝트에 적용을 한다든지 또는 소스가 변경되었다든지 할 때, PROGRAM 과 SRCS 만 바꾸어주면 된다. 소스 파일을 더 추가하고 싶다면 빈 칸을 사이에 두고 파일 이름을 적어주면 된다. 예를 들어 hello.c 외에 world.c 가 추가되었다면 2번째 줄을 이렇게 바꾸면 된다.
- SRCS := hello.c world.c
이외에도 여러가지 컴파일 플래그나 링커 플래그, 컴파일러나 링커를 위한 여러 변수들을 도입하면 훨씬 더 유연한 Makefile 이 될 것이다.
그리고 기회가 된다면 추후에 여러 개의 실행 파일과 DLL 등을 한꺼번에 빌드할 수 있는 Makefile 에 대해서도 글을 쓰도록 하겠다.
'Coding Tip > C/C++' 카테고리의 다른 글
new 연산자를 사용한 동적 메모리 할당 실패시 예외 처리 (0) | 2015.06.18 |
---|---|
[System Call] 메모리 맵 - mmap(), munmap(), msync(), CreateFileMapping, MapViewOfFile, UnmapViewOfFile (0) | 2015.05.14 |
C/C++ Socket에서 비동기 connect 처리하기 (0) | 2014.05.16 |
WSAGetLastError 함수 에러코드 종류 (0) | 2014.05.15 |
C/C++에서 NULL 포인터를 delete 하면 어떻게 되는가? (0) | 2013.10.15 |