gcc/g++ 컴파일러 사용을 위한 개발 환경
데비안(Debian) 계열 리눅스에서 C/C++ 개발을 하고자 할 때 build-essential 패키지를 설치한다. 이 패키지는 여러 필수 패키지를 의존성으로 묶어 한 번에 설치할 수 있게 돕는 메타 패키지이다. 메타 패키지는 실제 실행 파일은 포함하지 않고 의존성 정보만 가지고 있어 "빈 패키지(empty package)"라고도 부른다. 다음 명령어로 의존성을 확인할 수 있다. apt는 apt-get과 apt-cache 기능을 통합한 것이므로 apt depends 대신 apt-cache depends를 사용할 수도 있다.
$ apt depends build-essential
build-essential
|의존: libc6-dev
의존: <libc-dev>
libc6-dev
의존: gcc (>= 4:10.2)
의존: g++ (>= 4:10.2)
의존: make
make-guile
의존: dpkg-dev (>= 1.17.11)
gcc와 g++은 각각 C와 C++ 컴파일러이며, make는 빌드 자동화 도구이다. build-essential 패키지는 이러한 핵심 도구들에 의존하므로, 이를 설치하면 관련 도구들이 일괄 설치된다. 물론 필요에 따라 이들을 개별적으로 설치하는 것도 가능하다.
추가로 gdb와 cmake 패키지를 함께 설치하는 것이 좋다. gdb는 디버거이며, cmake는 현대적인 C/C++ 프로젝트 관리를 위한 표준 빌드 시스템 생성기이다. 특히 VS Code의 ‘CMake Tools’ 확장을 제대로 활용하기 위해서는 cmake 설치가 필수적이다.
패키지 설치 전에는 다음과 같이 패키지 목록 업데이트와 시스템 업그레이드를 먼저 수행한다.
$ sudo apt update && sudo apt upgrade
참고로 논리 AND(&&)는 앞선 명령이 성공했을 때만 뒷 명령을 실행하라는 의미이다. 이와 반대로 논리 OR(||)은 앞선 명령이 실패했을 때만 뒷 명령을 실행한다. (예: 명령이 실패할 경우 에러 메시지를 출력할 때 주로 사용한다.)
C/C++ 개발에 필요한 패키지들을 다음과 같이 설치한다.
$ sudo apt install build-essential gdb cmake
이미 설치된 패키지가 있다면 자동으로 제외하고 중복 설치하지 않는다. 다만, 새 패키지를 설치하는 과정에서 의존성 해결을 위해 기존 패키지가 업그레이드될 수는 있다.
dpkg -l 명령은 시스템에 설치된 모든 패키지 목록을 보여준다. 다음과 같이 파이프(|)와 grep을 연결하여 특정 패키지의 설치 여부를 확인할 수 있다.
$ dpkg -l | grep cmake
ii cmake 3.25.1-1 amd64 cross-platform, open-source make system
ii cmake-data 3.25.1-1 all CMake data files (modules, templates and documentation)
또는 다음과 같이 버전 정보를 출력하여 설치 여부를 확인하거나, 실행 파일의 위치를 알려주는 which 명령어를 사용할 수 있다.
$ gcc --version
$ g++ --version
$ make --version
$ cmake --version
$ which g++
Clang 컴파일러 사용을 위한 개발 환경
GCC 대신 애플(Apple)이 주도하여 개발한 Clang 컴파일러를 사용할 수도 있다. Clang은 에러 메시지가 매우 명확하고 친절하며, Apache 2.0 라이선스를 채택해 기업 친화적이고 유연하다. 반면 GCC는 대부분의 리눅스 배포판 커널과 기본 시스템을 빌드하는 검증된 컴파일러이며, 런타임 성능 최적화가 매우 정교하다. 또한 GCC는 GPL 라이선스를 따르므로 소스 코드 공개 의무와 전염성이 강하다는 특징이 있다.
일반적으로 학습용이나 개발 단계에서는 친절한 에러 메시지 덕분에 오타나 문법 실수를 빠르게 교정할 수 있는 Clang을 사용하고, 실제 배포용 빌드에는 GCC를 사용하는 하이브리드 전략을 취하기도 한다. C++ 표준은 계속 발전하고 있지만, GCC와 Clang 모두 최신 표준을 빠르게 도입하고 있으므로 어떤 것을 선택해도 최신 문법 활용에는 지장이 없다.
Clang으로 개발 환경을 구축할 때는 가급적 Clang 기반의 전체 도구 세트(Toolchain)를 갖추는 것이 좋다. 주요 패키지 목록은 다음과 같다.
- clang: 컴파일러
- lldb: 디버거
- valgrind: 메모리 분석 도구
- clang-format: 코드 스타일 정리 도구
- clang-tidy: 정적 분석 도구
위 패키지들을 다음 명령으로 한 번에 설치한다.
$ sudo apt install clang lldb valgrind clang-format clang-tidy
Ninja 빌드 시스템 설치
현대적인 C++ 개발 환경에서 Ninja는 이미 make를 대체하는 표준으로 자리 잡았다. 대규모 오픈 소스 프로젝트를 분석할 때 ‘컴파일 데이터베이스(compile_commands.json)’ 파일을 생성해야 VS Code의 ‘C/C++’ 확장이나 ‘clangd’ 확장에서 코드 바로가기, 자동 완성 기능을 정확히 제공할 수 있다.
이 파일을 생성하기 위해 기존의 make와 bear를 조합하는 방법과 Ninja 빌드 시스템을 사용하는 두 가지 방법이 있다. 리눅스 커널이나 고전적인 C 라이브러리 등 오래된 프로젝트를 분석한다면 make + bear 조합이 적합하며, 현대적인 프로젝트라면 Ninja를 사용하는 것이 효율적이다.
다음과 같이 bear와 ninja-build 패키지를 설치하고, 가급적 make 대신 Ninja를 주력으로 사용하도록 한다.
$ sudo apt install bear ninja-build
예제 작성과 컴파일 테스트
VS Code의 C/C++ 관련 확장을 설치하기 전, 간단한 예제를 작성하여 명령줄(CLI)에서 설치한 컴파일러가 잘 작동하는지 확인해 본다. 윈도우 터미널을 통해 WSL에 접속한 후 프로젝트 디렉터리를 생성하고 이동한다.
$ mkdir wsl_cpp
$ cd wsl_cpp
vi나 VS Code를 사용하여 다음 내용을 입력하고 hello.cpp라는 이름으로 저장한다. 해당 디렉터리에서 code . 명령을 실행하면 현재 폴더를 작업 영역으로 하여 VS Code가 열린다.
#include <iostream>
int main() {
std::cout << "Hello, WSL!" << std::endl;
return 0;
}
터미널에서 g++을 사용하여 컴파일하고 실행해 본다.
$ g++ hello.cpp
$ ./a.out
Hello, WSL!
별도의 옵션 없이 컴파일하면 실행 파일 이름은 기본적으로 a.out으로 생성된다. 실행 시 ./와 같이 현재 디렉터리를 명시해야 하는데, 이는 유닉스 계열 시스템이 보안상의 이유로 현재 디렉터리를 실행 경로(PATH)에 포함하지 않기 때문이다. 반면 윈도우의 명령 프롬프트(CMD)는 현재 디렉터리를 실행 경로에 포함한다는 차이가 있다. 현재 실행 경로는 echo $PATH 명령어로 확인할 수 있다.
다음과 같이 -o 옵션을 사용하면 생성될 실행 파일의 이름을 직접 지정할 수 있다.
$ g++ -o hello hello.cpp
$ ./hello
Hello, WSL!
Clang의 경우 C 컴파일러는 clang, C++ 컴파일러는 clang++ 명령어를 사용한다. 다음과 같이 여러 옵션을 붙여 컴파일해 본다. (이 옵션들은 g++에서도 동일하게 사용할 수 있다.)
$ clang++ -Wall -std=c++17 -g -o hello hello.cpp
$ ./hello
Hello, WSL!
- -Wall: 모든 경고(Warning) 메시지를 출력한다.
- -std=c++17: 사용할 C++ 표준 버전을 명시한다.
- -g: 디버깅 정보를 포함하여 gdb나 lldb에서 사용할 수 있게 한다. (최종 제품 배포 시에는 이 옵션을 제외한다.)
이외에도 최적화 단계를 지정하는 옵션 등이 있으며, 이러한 설정값들은 주로 Makefile이나 Ninja 빌드 파일에서 정의하여 사용한다.
과거에는 유닉스나 리눅스 환경에서 vi, Emacs 같은 에디터만으로 작업해야 했고, 파이썬이 보급되기 전에는 복잡한 빌드 과정을 관리하기 위해 쉘 스크립트를 직접 작성해야 했다.
이제는 VS Code 덕분에 훨씬 편리한 환경에서 개발이 가능해졌다. VS Code에서 C/C++ 개발 관련 확장을 설치하고, 보다 효율적으로 개발 환경을 구축하는 방법으로 넘어가 보자.