Makefile 빌드 변수 CFLAGS와 LDFLAGS 활용법

개요

Makefile에서 CFLAGSLDFLAGS는 각각 컴파일 단계와 링크 단계에 필요한 옵션을 전달하는 관례적인 변수입니다. 이 두 변수를 적절히 활용하면 빌드 과정을 체계적으로 제어할 수 있습니다.

CFLAGS 상세

CFLAGS(Compiler Flags)는 소스 파일(.c)을 목적 파일(.o)로 변환하는 컴파일 단계에서 사용되는 옵션들을 담당합니다.

주요 옵션

옵션설명
-I<경로>헤더 파일 검색 경로 지정
-Wall주요 경고 메시지 활성화
-Werror경고를 오류로 처리하여 빌드 중단
-O2중간 수준 최적화 적용
-g디버깅 정보 포함
-c컴파일만 수행하고 링크는 생략
-D<매크로>전처리기 매크로 정의

-I: 헤더 경로 지정

다음과 같은 프로젝트 구조를 가정합니다:

demo/
├── main.c
└── header/
    └── config.h

main.c에서 config.h를 포함하려면 헤더 경로를 명시해야 합니다:

// main.c
#include <stdio.h>
#include "config.h"

int main(void)
{
    printf("VERSION: %d\n", BUILD_VER);
    return 0;
}
// header/config.h
#ifndef CONFIG_H
#define CONFIG_H
#define BUILD_VER 42
#endif

Makefile 예시:

TOOLCHAIN := gcc
SOURCE := main.c
OBJECT := $(SOURCE:.c=.o)
BUILDFLAGS := -I./header   # 헤더 검색 경로 추가

.PHONY: build tidy

build: app
	@echo "빌드 완료"

app: $(OBJECT)
	$(TOOLCHAIN) -o $@ $^

%.o: %.c
	$(TOOLCHAIN) $(BUILDFLAGS) -c $< -o $@

tidy:
	rm -f $(OBJECT) app

-Wall과 -Werror

-Wall은 코드의 잠재적 문제를 탐지하여 경고로 출력합니다. 단, 경고만으로는 빌드를 중단하지 않습니다.

// warn.c
#include <stdio>

int global_val;  // 미사용 전역 변수

int main(void)
{
    printf("warning test\n");
    return 0;
}

위 코드를 -Wall 옵션으로 컴파일하면 미사용 변수에 대한 경고가 출력됩니다. -Werror를 추가하면 이 경고가 오류로 처리되어 빌드가 중단됩니다:

BUILDFLAGS := -Wall -Werror   # 경고 발생 시 빌드 실패

-O2 최적화

-O2는 컴파일 시간과 실행 성능 사이의 균형을 맞추는 중간 수준 최적화입니다. 적용되는 주요 기법은 다음과 같습니다:

  • 상수 폴딩: 컴파일 시점에 계산 가능한 표현식을 상수로 대체
  • 루프 최적화: 루프 전개, 불필요한 반복 제거
  • 함수 인라인화: 작은 함수를 호출 지점에 직접 삽입
  • 죽은 코드 제거: 도달 불가능한 코드 록 제거
최적화 레벨특징
-O0최적화 없음, 디버깅에 유리
-O1기본 최적화, 빠른 컴파일
-O2권장 레벨, 성능과 안정성 균형
-O3공격적 최적화, 잠재적 부작용
-Os코드 크기 최소화, 임베디드용

-g 디버깅 정보

-g 옵션은 소스 코드의 줄 번호, 변수명, 자료형 등의 정보를 바이너리에 포함시켜 gdb 등의 디버거에서 소스 레벨 디버깅이 가능하도록 합니다. 디버깅 시에는 -O0 -g 조합을 권장합니다.

-c: 객체 파일 생성

링크 과정 없이 목적 파일만 생성할 때 사용합니다. Makefile의 패턴 규칙에서 필수적으로 활용됩니다.

-D: 조건보 컴파일

소스 코드를 수정하지 않고 빌드 시점에 매크로를 정의할 수 있습니다:

// feature.c
#include <stdio.h>

int main(void)
{
    printf("mode: ");
    
#if defined(PROFILE)
    printf("profiling enabled\n");
#elif defined(PRODUCTION)
    printf("production release\n");
#else
    printf("development\n");
#endif

    return 0;
}
# Makefile
BUILDFLAGS := -DPROFILE     # 또는 -DPRODUCTION

%.o: %.c
	$(TOOLCHAIN) $(BUILDFLAGS) -c $< -o $@

LDFLAGS 상세

LDFLAGS(Linker Flags)는 목적 파일들을 최종 실행 파일이나 라이브러리로 결합하는 링크 단계에서 사용됩니다.

주요 션

옵션설명
-L<경로>라이브러리 파일 검색 경로 지정
-l<이름>링크할 라이브러리 지정
-Wl,-rpath=<경로>실행 시 동적 라이브러리 검색 경로
-static정적 링크 강제

-L과 -l의 협력

사용자 정의 라이브러리를 링크할 때는 경로와 라이브러리 이름을 함께 지정합니다:

# 프로젝트 구조
# myapp/
#   ├── calc.c
#   └── lib/
#       └── libcalc.a

LINK_PATHS := -L./lib
LINK_LIBS := -lcalc

app: calc.o
	$(TOOLCHAIN) -o $@ $^ $(LINK_PATHS) $(LINK_LIBS)

-lcalclibcalc.so 또는 libcalc.a를 찾아 링크합니다. 동적 라이브러리를 우선 시도하며, 없으면 정적 라이브러리를 사용합니다.

-Wl: 링커 직접 제어

GCC를 통해 링커(ld)에 전용 옵션을 전달할 때 사용합니다:

RUNTIME_PATH := -Wl,-rpath,'$$ORIGIN/lib'

app: main.o
	$(TOOLCHAIN) -o $@ $^ -L./lib -lhelper $(RUNTIME_PATH)

위 예시는 실행 파일이 자신의 위치 기준 lib 디렉토리에서 동적 라이브러리를 검색하도록 설정합니다.

-static

모든 의존성을 실행 파일에 내장하여 외부 동적 라이브러리 없이도 실행 가능한 독립 바이너리를 생성합니다:

LINKFLAGS := -static

app: $(OBJECTS)
	$(TOOLCHAIN) $(LINKFLAGS) -o $@ $^ -lm

CFLAGS와 LDFLAGS 비교

구분CFLAGSLDFLAGS
적용 단계컴파일 (.c → .o)링크 (.o → 실행 파일)
처리 대상소스 파일, 헤더 파일목적 파일, 라이브러리
대표 옵션-I, -Wall, -O2, -g-L, -l, -Wl
관련 도구컴파일러 (gcc, clang)링커 (ld)

태그: Makefile CFLAGS LDFLAGS GCC 빌드시스템

7월 4일 16:42에 게시됨