리눅스와 VSCode를 이용한 C++ 프로젝트 개발 환경 구축 및 CMake 활용

C/C++ 컴파일 과정

C++ 소스 코드가 실행 가능한 프로그램으로 변환되는 과정은 여러 단계로 나뉩니다. 각 단계는 특정 역할을 수행하며, 이해하는 것이 빌드 시스템을 다루는 데 중요합니다.

1. 전처리 (Pre-compilation)

전처리기는 소스 파일(.cpp)과 헤더 파일(.h)을 처리하여 전처리된 파일(.i)을 생성합니다. 이 과정에서 #include, #define과 같은 전처리 지시어를 처리합니다. #include는 지정된 헤더 파일의 내용을 해당 위치에 전개(expand)합니다.

g++ -E sample.cpp -o sample.i

여러 헤더 파일을 포함하는 경우, 헤더 파일의 순서가 중요할 수 있습니다.

2. 컴파일 (Compilation)

컴파일러는 전처리된 파일(.i)을 입력으로 받아, 어휘 분석, 구문 분석, 의미 분석 및 최적화를 수행한 후, 어셈블리 코드 파일(.s)을 생성합니다. 이 단계는 전체 빌드 과정의 핵심입니다.

g++ -S sample.i -o sample.s

3. 어셈블 (Assembly)

어셈블러는 어셈블리 코드(.s)를 기계어 명령어로 변환하여 목적 파일(.o)을 생성합니다. 각 어셈블리 명령어는 대부분 하나의 기계어 명령어에 해당합니다.

g++ -c sample.s -o sample.o

링킹 (Linking)

링커는 컴파일된 목적 파일과 프로그램에서 사용하는 라이브러리 파일들을 결합하여 최종 실행 파일을 생성합니다. 예를 들어, printf와 같은 함수는 라이브러리에서 제공되므로, 링킹 과정에서 해당 라이브러리와 함께 묶여야 합니다.

정적 라이브러리 vs 동적 라이브러리

  • 정적 라이브러리 (Static Library): 컴파일 시 라이브러리 코드가 실행 파일에 포함됩니다. 따라서 실행 파일의 크기가 더 크지만, 실행 시 추가 라이브러리가 필요 없습니다.
  • 동적 라이브러리 (Dynamic Library): 프로그램 실행 시에만 메모리에 로드됩니다. 컴파일 시에는 간단한 참조만 남기므로, 실행 파일의 크기가 더 작습니다.

가장 큰 차이점은 정적 라이브러리는 프로그램 코드에 직접 포함되어 재사용성과 의존성이 낮지만, 동적 라이브러리는 프로그램과 독립적으로 유지되어 코드의 재사용성과 유연성을 높입니다.

실전: 정적 라이브러리 생성

프로젝트 구조:

.
├── include
│   └── math_utils.h
├── main.cpp
└── src
    └── math_utils.cpp

명령어:

# 1. 컴파일하여 math_utils.o 파일 생성
g++ math_utils.cpp -c -I../include

# 2. 정적 라이브러리 libmath_utils.a 생성 (아카이빙)
ar rs libmath_utils.a math_utils.o

# 3. 루트 디렉터리로 이동
cd ..

# 4. 링킹하여 실행 파일 static_main 생성
g++ main.cpp -lmath_utils -Lsrc -Iinclude -o static_main

매개변수 설명:

  • -I: 헤더 파일 경로 지정
  • -l: 정적 라이브러리 지정 (파일명에서 'lib'와 '.a'를 제외한 부분 사용)
  • -L: 라이브러리 파일이 있는 디렉터리 지정

실전: 동적 라이브러리 생성

# 1. 동적 라이브러리 생성
g++ math_utils.cpp -I../include -fPIC -shared -o libmath_utils.so

# 2. 링킹
g++ main.cpp -Iinclude -Lsrc -lmath_utils -o shared_main

# 3. 실행 (라이브러리 경로 지정 필요)
LD_LIBRARY_PATH=src ./shared_main

GDB 디버거

GDB(GNU Debugger)는 C/C++ 프로그램을 디버깅하기 위한 강력한 도구입니다. VSCode는 내부적으로 GDB를 호출하여 디버깅 기능을 제공합니다.

CMake 및 개발 환경 구축

CMake는 크로스 플랫폼 빌드 시스템 생성기를 위한 오픈소스 도구입니다. 복잡한 Makefile을 직접 작성하는 대신, CMake를 사용하면 프로젝트 구조를 쉽게 관리하고 다양한 플랫폼에서 빌드할 수 있습니다.

환경 구축 시 발생한 문제 해결

리눅스 환경에서 VSCode를 처음 사용하는 경우, 컴파일러(gcc, g++, clang), 빌드 도구(make, cmake), 코드 분석기(clangd) 등의 설정에 어려움을 겪을 수 있습니다. 특히 컴파일러 버전 충돌이나 라이브러리 경로 문제는 흔히 발생합니다.

Clang 관련 문제

초기에는 clangd가 표준 라이브러리를 인식하지 못하는 문제가 발생했습니다. 이는 Clang 컴파일러가 GNU 컴파일러(GCC) 대신 사용되어야 함을 시사하는 경고 메시지를 통해 알게 되었습니다.

We recommend that you use clang-14 for developing BusTub. You're using GNU, a different version.

기본 컴파일러를 Clang으로 전환하기 위해 다음 명령을 사용했습니다.

sudo update-alternatives --config c++

Clang으로 전환 후, CMake를 실행하면 libstdc++ 라이브러리를 찾을 수 없다는 오류가 발생할 수 있습니다. 이는 Clang이 필요한 C++ 표준 라이브러리를 찾지 못하는 경우입니다. 해결 방법은 다음과 같습니다.

sudo ln -s /usr/lib/x86_64-linux-gnu/libstdc++.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so

이후 make를 실행하면, 'iostream' file not found와 같은 헤더 파일을 찾을 수 없다는 오류가 발생할 수 있습니다. 이는 Clang이 시스템에 설치되지 않은 특정 버전의 GCC(예: GCC 12)를 사용하려고 시도할 때 발생합니다. 시스템에 해당 GCC 버전을 설치하면 문제가 해결됩니다.

# GCC 12 설치 (예시)
sudo apt install g++-12

올바른 컴파일러 버전을 사용하면, 빌드 과정과 clangd의 기능이 정상적으로 작동합니다.

태그: 리눅스 vscode C++ cmake GCC

7월 3일 17:03에 게시됨