C++ 코드 정리 자동화 - 1. 불필요한 #include 찾기 上

지워도 되는 헤더 인클루드를 색출하고 싶다

매우 느리게 찔끔찔끔 진행하는 토이 프로젝트가 있는데, 오늘 처음으로 무언가 그럴싸한 아웃풋이 나오게 되어 스냅샷을 하는 느낌으로 간단히 포스팅.

cpp 프로젝트 규모가 점점 커지게 되면 빌드 시간 때문에 많은 고통을 겪는다. 이때문에 increadi build 같은 분산 빌드 솔루션도 쓰는거고 unity build 같은 꼼수도 사용하게 되는거다.

하지만 저런 솔루션들을 사용하기 이전에, 코드를 정리하는 것이 먼저 선행될 필요가 있다. cpp는 특성상 작업하다보면 소스파일에 불필요한 헤더파일의 #include가 남게되고, 이것들이 불필요한 dependency를 만들어내면서 늘어지는 빌드 시간을 무시할 수 없기 때문이다.

그런데 문제는 그렇게 생긴 불필요 인클루드 구문이 무엇인지를 골라내기가 힘들다는 점이다. 프로젝트 규모가 커질수록 더욱 힘들다. c#같은 경우 불필요 using 구문을 아예 visual studio IDE가 자체적으로 정리해주기까지 하지만, cpp는 색출조차 힘들다 보니 이런 기능을 제공하는 3rd party tool도 없어 보인다. Whole Tomato의 Spaghetti 처럼 인클루드간의 관계를 그래프로 보여주는 툴은 몇 번 본 적 있다. 조낸 멋지게 그래프까지 보여주었지만 정작 불필요한 놈이 무언지 콕 짚어주는 녀석은 없음. 참으로 척박한 현실이다.

그래서 한 번 직접 만들어보기로 했다.

프로젝트 내의 cpp 파일을 개별 컴파일 하기

일단은 만들려는 툴에서, 입력으로 받은 vc 프로젝트에 포함된 cpp 파일을 개별로 컴파일 할 수 있어야 한다.
그렇게 되면 cpp 파일마다 돌면서 코드 안에 있는 #include를 직접 하나씩 제거해보면서 컴파일이 성공하는지를 확인할거다. 그러면 불필요할 것이라 예상되는 #include의 후보를 만들 수 있다.

무식한 방법이다. cpu를 많이 먹을거고 시간도 오래 걸릴거다. 하지만 저렇게라도 알 수 있다면 새벽에 실행해서 리포트 뽑아놓도록 CI에 물려놓으면 그만이다.

무식하기도 하지만 또한 불완전한 방법이기도 하다. 위의 동작으로 불필요 #include 후보 리스트를 만들었다고 해도,
헤더파일 끼리의 상호 참조관계, 내부 포함 관계등이 여러 복잡한 상황을 연출하기 때문에
후보로 지목된 헤더가 실은 필요한 녀석일 수도 있다.

하지만 일단은 후보 리스트 색출까지 먼저 진행해 보기로 한다.
사실 정말 확실한 불필요 #include가 색출 가능하다면 tool이 아예 코드를 코치는 것까지 자동으로 처리해 줄 수도 있을 것 같지만… 일단 나중에 생각하기로.

프로젝트에 포함된 cpp 파일의 리스트를 구하는 것은 일도 아니다. vcxproj파일은 xml 형태로 되어 있으므로, /Project/ItemGroup/ClCompile 경로의 xml element를 얻어와 파일 경로를 읽어내면 끝이다.

그다음은 이 파일을 각각 컴파일 할 수 있어야 하는데… 이것은 생각보다 만만치가 않다. cl.exe를 실행해서 컴파일 하면 되지만, cl.exe의 커맨드라인 옵션으로 들어가야 하는 인자가 엄청나게 많고, 이 옵션을 vcxproj 파일에서 일일이 파싱하고 다시 조합하기란 상당히 귀찮고 짜증나는 작업이다.

이 귀찮은 작업을 MSBuild에 맡겨버릴 수 있다. MSBuild에 /t:BuildCompile 옵션과 /p:SelectedFiles=xxx을 쓰면 vcxproj를 알아서 파싱해서 cl.exe의 커맨드라인 인자를 직접 만들어준다.

이렇게 해서 일단 프로젝트 파일에 있는 cpp를 개별 컴파일 하는 것까지 성공.

여기까지 하고 나니 cpp 파일당 컴파일 시간까지 덤으로 얻게 됨.
앗싸.