< 이 포스트로 해소할 수 있는 궁금증들 >
1) <stdio.h>와 같은 표준 라이브러리의 헤더 파일들은 도대체 어디서 가져오는 것이냐?
2) <windows.h>, <d3d12.h>와 같은 윈도우즈 개발 시 사용하는 헤더 파일들은 도대체 어디서 가져오냐?
3) 내가 만든 라이브러리의 헤더파일, 혹은 다른 사람이 만든 헤더 파일을 <>로 가져오고 싶다면?
- 기준 : Visual Studio Community 2022 버전.
- 그러나 내 생각에는, VS의 버전이 달라도 큰 틀에서 기능이 크게 다르진 않을 것이라 생각한다.
#1. include 꺾쇠(<>)
▷ 설정을 따로 건드리지 않았다면, #include <stdio.h> 같이 꺾쇠(<>)로 표현되는 헤더파일에 대해 VS(MSBuild)는 기본적으로 [VC++ 디렉터리] - [포함 디렉터리]에 설정된 디렉터리 경로부터 검색하며 헤더 파일을 찾아 전처리를 수행한다.
▷ 포함 디렉터리에는 이미 Visual Studio에 의해 약속된 매크로를 사용해서 기본적으로 찾는 경로가 저장되어 있다. <편집>을 눌러 직접 그 경로를 확인할 수도 있다.
▷ 꼭 실제 경로를 보지 않아도, 이미 매크로 자체의 이름이 많은 것을 설명해주고 있다. 하나는 VC 컴파일러가 기본적으로 찾는 경로로, Visual Studio 설치 폴더 내부 깊숙한 곳에 있다. 다른 하나는 Windows Kits 폴더 내부에 존재하는 Windows SDK 경로다.
▷ 아래 그림을 보자. 실제로 저 경로에 가면 우리가 흔하게 사용하는 온갖 헤더파일들이 존재한다. <stdio.h>부터 시작해서 <vector>, <memory>, 심지어 <d3d12.h>와 같이 DirectX 같은 헤더파일도 존재한다. 우리가 꺾쇠를 사용하면 Visual Studio(정확히는 MSBuild)가 이 경로를 탐색해서 전처리를 수행하는 것이다.
▷ 그렇다면 [포함 디렉터리] 경로에 내가 만든 헤더파일이 있는 프로젝트의 경로를 입력하면 따옴표("") 대신 꺾쇠(<>)로 사용할 수 있을까? 그렇다. 실제로 저 값을 조정하면 꺾쇠로 변경이 가능하다. 그러나 이 방식은 절대 추천하지 않는다. 대부분의 IT 책과 강의, 현업 팀들은 결코 이렇게 설정하지 않는다. 이걸 건드려서 외부 라이브러리의 헤더 파일을 가져오면 아마 욕 먹을 지도...?
▷ 왜냐하면 [VC++ 디렉터리]의 [포함 디렉터리] 항목은 프로젝트 단위가 아니라 Visual Studio(MSBuild) 차원의 '전역' 설정이기 때문이다.
▷ [포함 디렉터리]의 설명을 보면 이 값이 환경변수 INCLUDE에 해당한다고 나와 있다. 차근차근 설명하자면, Visual Studio(MSBuild)는 .sln 파일과 .vcxproj 파일을 참고해 해당 솔루션과 하위 프로젝트들을 빌드한다. 이 과정에서 VC++ 컴파일러(cl.exe)는 시스템의 환경변수 INCLUDE의 값을 참고해 include 파일을 탐색한다. 컴파일러의 전처리 동작은 다른 컴파일러도 비슷하게 작동한다고 들었다. 즉, 특별히 옵션을 지정해주지 않아도 저 환경변수의 경로를 탐색해 h 헤더 파일을 찾는 것이다.
▷ 실제로 시스템의 환경변수를 조작하거나 내가 설정해야 할 필요는 없다. MSBuild 도구는 vcxproj에 설정된 [포함 디렉터리]의 값을 '세션 환경 변수 INCLUDE'로 등록한다. 이것은 빌드를 할 때만 임시적으로 활성화되고, 빌드가 끝나면 제외된다. 여하튼 환경 변수 자체를 설정하는 구조이므로, 다른 모든 프로젝트의 컴파일/전처리 과정에 영향을 줄 수 있다. 이런 이유로 이 INCLUDE 값은 C/C++ 표준과 Windows SDK의 헤더 파일이 들어가는 것이 올바르다. 다른 외부 라이브러리는 전역이 아니라 프로젝트 단위로 설정하는 것이 바람직하다.
▷ 위 그림처럼 [C/C++] - [추가 포함 디렉터리]에 외부 정적 라이브러리의 헤더 파일, 혹은 다른 사람이 만든 라이브러리의 헤더 파일이 있는 경로를 기입하는 것이 좋은 습관이다.
▷ 이것은 환경변수 INCLUDE를 제어하는 것이 아니라, VC++ 컴파일러(cl.exe)에게 옵션으로 직접 명령을 실행하는 방식이다. 그래서 [추가 포함 디렉터리]에 경로를 올바르게 기입하면 꺾쇠(<>)를 사용할 수 있다.
▷ 저런 식으로 내가 [C/C++]에 설정한 것들이 컴파일러에게 옵션을 주는 방식으로 컴파일/전처리를 하는 것이다. 이것은 프로젝트마다 옵션이 다르게 구성될 수 있다.
▷ 다른 프로젝트의 컴파일 명령 옵션을 보면 포함 디렉터리에 설정한 경로가 없거나 다르다. 이것은 해당 프로젝트의 [C/C++] 설정을 따른다. 즉, 프로젝트마다 명령어 옵션이 다른 것이다. 이것이 왜 [추가 포함 디렉터리]를 사용하는 것이 바람직한지 원리를 설명해준다. 환경변수를 설정하는 전역이 아니라 프로젝트 단위로 다르게 설정하는 것이다.
▷ 물론 지금까지 설명한 것은 외부의 라이브러리 헤더 파일을 꺾쇠(<>)로 사용하기 위한 목적이므로, 만약 그 헤더 파일을 프로젝트 소스 폴더 내부로 들여온 후 따옴표로 include해서 절대 경로를 지정한다고 해도 사실 큰 문제는 없다... 그러나 헤더 파일이 있는 경로를 [추가 포함 디렉터리]에 추가해주면 꺾쇠로도 사용할 수 있으니 자주 사용하는 유용한 라이브러리의 헤더 파일이라면 이 방법을 고려해도 괜찮다.
※ 많은 오픈소스 프로젝트의 소스코드를 보면, 각종 헤더파일을 꺾쇠(<>)로 include 하는데, 이는 오픈소스를 각 개발자가 로컬에서 불러와 빌드할 상황을 고려한 것이다. 따옴표("")와 같은 절대 경로보다 꺾쇠를 이용한 것은, 소스코드를 가져와 빌드하는 개발자가 [추가 포함 디렉터리]를 조정할 것이라고 가정하는 것이 합리적인 기대이기 때문이다.
< 설명히 좀 복잡했으니 요약! >
- #include <stdio.h>와 같이 꺽쇠를 사용하면 컴파일러는 아래의 우선순위로 헤더 파일을 검색한다.
1) 컴파일러 명령줄의 옵션 \I 에 설정된 경로를 검색한다.
2) 못 찾으면 환경변수 INCLUDE에 설정된 경로를 검색한다.
- 두 방식은 아래처럼 제어한다.
1) 명령줄 옵션으로 설정하기 = [추가 포함 디렉터리]에 경로를 설정한다.
2) 빌드 시 전역으로 설정하기 = [포함 디렉터리]에 경로를 설정한다. (그러나 이 방식은 지양할 것!)
#2. 따옴표("")
▷ 따옴표는 쉽다. 상대/절대 경로다. 끝!
▷ 위 그림은 예시를 들기 위해 구성해본 소스코드다. 일단 따옴표를 쓰면 컴파일러는 1) 헤더 파일이 있는 디렉터리부터 시작하는 상대 경로로 찾거나, 혹은 2) 절대 경로로 헤더파일을 찾아 전처리를 수행한다. 같은 프로젝트에서 만든 헤더 파일이거나, 혹은 같은 솔루션 아래에 만든 다른 프로젝트의 헤더 파일일 경우 이렇게 자주 쓴다.
▷ 따옴표 방식도 만약 상대 경로나 절대 경로로 찾지 못하면 [추가 포함 디렉터리], 다음으로는 [포함 디렉터리]의 경로를 검색하긴 한다. 따라서 stdio.h도 따옴표로 쓸 수는 있다. 그러나 이건 컴파일의 성능도 떨어지고, 읽기도 불편하므로 사용을 지양해야 한다. 관습적으로 꺾쇠<>는 시스템 라이브러리, 외부 라이브러리에 사용되고, 따옴표("")는 현재 프로젝트나 솔루션의 헤더 파일에 사용되기 때문이다. "stdio.h"라고 쓰면 읽는 입장에서 당황스럽다.
< 컴파일러의 전처리 과정 총 정리 >
1) 제일 먼저 현재 소스코드의 디렉터리를 시작으로 한 상대 경로로 탐색한다.
2) 못 찾으면, "" 값이 절대 경로라고 가정하고 탐색한다.
3) 못 찾으면, 컴파일러 명령줄의 옵션에 설정된 경로를 탐색한다. (꺾쇠 탐색은 여기서부터 시작!!)
4) 못 찾으면, 환경변수 INCLUDE의 값에 설정된 경로를 탐색한다.
5) 기여코 못 찾으면 에러~~ 전처리 실패!
#. 여담..
▷ [포함 디렉터리]는 구성(Configuration)과 플랫폼(Platform)의 값과 별개로 언제나 동일한 경로를 가리키고 있다. 반대로 lib, dll 라이브러리의 위치나 실행 파일의 경로와 관련된 디렉터리는 플랫폼을 기준으로 win32(x86), x64가 서로 다른 경로를 가리킨다.
▷ 그도 그럴것이, 라이브러리(lib, dll)는 이미 컴파일된 바이너리 데이터이므로 플랫폼 환경에 맞게 32비트와 64비트 코드가 나뉘어져 있는 것이 당연하다. 32비트와 64비트는 기계어 명령어가 아무리 같은 x86 아키텍처라도 다르기 때문이다. 서로 다른 것을 링크하면 오류가 뜬다.
▷ 그러나 헤더 파일은 그냥 텍스트 파일일 뿐이며 32bit, 64bit라고 해서 달라질 이유가 없다. 그래서 플랫폼과 별개로 경로가 같은 것이다.
'IT 공부 > Visual Studio 툴 사용하기' 카테고리의 다른 글
[Visual Studio] - 문자 집합 옵션과 UNICODE 매크로 (0) | 2024.12.26 |
---|---|
[Visual Studio] - CRT 런타임 라이브러리의 빌드 방식 (0) | 2024.12.26 |
[Visual Studio] - 정적 라이브러리와 같이 빌드하기 (0) | 2024.12.24 |
[Visual Studio] - 소스파일과 빌드 산출물을 깔끔하게 구성하는 방법 (0) | 2024.12.23 |
[Visual Studio] - 소스코드를 가져오거나 컴파일할 때 한글 인코딩 문제 (0) | 2024.12.21 |