본문 바로가기

Library/C/C++

Automated Build : Visual Studio's nmake and GNU make

투박한 텍스트 위주의 화면으로 개발하는 이미지가 강한 GNU 개발툴에 비해, Visual Studio는 화려한 UI 이미지가 강하다. 데이터 스트림이라는 은유적인 개념으로 작은 프로그램들이 협력하는 유닉스 스타일의 GNU 개발툴들은, 그 논리성을 충분히 이해하고 있다면 실로 막강한 개발 환경을 구축할 수 있다. 이런 자유스러운 문화에 푹 젖은 프로그래머들은, Visual Studio를 WYSIAYG(What You See Is All You Get)이라며 비웃지만, 사실 Visual Studio는 UI 지상주의로 설계된 소프트웨어가 아니다.

Visual Studio를 마우스가 없으면 아예 동작하지 않는 것으로 생각하는 프로그래머들이 많지만, 사실 Visual Studio는 유닉스 스타일처럼 텍스트 기반의 코어 위에 UI가 덧씌워져 있다. 보이는대로만 디자인을 해야 하는 Visual Studio 내부의 개발툴도 있지만, 적어도 Visual C++은 다른 GNU 개발툴과 비슷한 방식으로 사용할 수 있다. 특히, Makefile을 통한 빌드 자동화는 큰 차이가 없다. 윈도우즈는 운영체제 전체를 관통하는 일반적인 메타포가 없어서 유닉스 스타일의 개발툴과 직접적으로 비교할 수 없지만, 빌드 자동화라는 부분에 있어서는 이들과 비슷한 환경을 제공한다.

예를 들어, 대부분의 Visual Studio 프로젝트는 솔루션 파일이나 프로젝트 파일을 제공하는데, 많은 프로그래머들은 이런 프로젝트 관리 파일이 주어지지 않는다면 몇 줄 안되는 코드임에도 다른 플랫폼에서 컴파일하거나 이식하는 작업을 포기한다. 그러나, 관리 파일은 실제로 그렇게 대단한 것이 아니다. Visual C++ 프로젝트의 경우, 가장 중요한 것은 Visual C++ 컴파일러인 cl.exe에게 넘겨주는 파라미터들이며, 파일들에게 적용되는 컴파일 명령을 알고 있다면 Visual Studio IDE의 도움 없이도 프로젝트를 컴파일 할 수 있다.

잘 만들어진 크로스 플랫폼 소프트웨어들은 다양한 플랫폼을 위한 Makefile들을 제공하는데, 특히 여기서 말하고자 하는 것은 Visual C++ nmake 유틸리티이다. 위에서 말했던, 각 파일에 적용되는 컴파일 명령들이 중요하다고 하는 것도, 사실 GNU nmake와 Visual C++ nmake의 Makefile 구성 방식은 큰 차이가 없기 때문이다. nmake 쪽이 미리 정의되어 있는 매크로가 좀 더 많고 주의해야 할 것들이 약간 더 많기는 하지만, Makefile 몇 줄 고치는 것으로도 GNU make와 nmake에 맞는 Makefile을 만들어낼 수 있다. 극단적으로 말한다면, 컴파일러를 지시하는 매크로와 컴파일 옵션만 수정하는 것으로도 충분하다.

Makefile 매뉴얼은 처음 접하는 사람이 보면 바로 덮어버릴 만큼 복잡하게 씌여있지만, Makefile 구성은 사실 간단하다. 가장 간단하게 Makefile을 구성한다면, 최소의 매크로 작성 / 의존성 정의만 해주면 된다. 이외에도 일반적인 빌드 규칙과 같은 고급 기능들이 있지만, 당장 필요한 것은 아니다. 먼저, 매크로는 다음과 같이 정의된다.


macroname = ....


이것이 전부이다. Makefile 내에서 매크로를 참조할 때는, $(marconame)과 같이 사용하면 된다. 매크로는 단순히 문자열을 치환해주는 역할을 한다. 매크로를 다루는 좀 더 복잡한 기교들이 많지만, 일단 이 정도만 알아도 간단한 Makefile을 작성하는데 아무런 문제가 없다.

다음은 의존성 정의인데, Makefile 작성에 가장 중요한 부분이다. 즉, 프로젝트를 구성하는 어떤 파일을 수정했을 때 다시 컴파일 할 필요가 있는지 결정한다. 다음과 같이 작성한다.


foo.o : foo.h foo.c
    gcc foo.c -c -g -Wall -o $@


위의 규칙을 살펴보면, foo.o 파일이 생성되기 위해서는 foo.h와 foo.c 파일이 필요하며, 밑의 줄은 커맨드 라인이다. 커맨드 라인은 반드시 탭이나 같은 줄의 세미콜론 다음에 이어져야 한다. $@는 make가 특별히 정의한 매크로인데, $@는 foo.o, 즉 의존성 정의 대상의 레이블로 치환된다. gcc의 -o 옵션은 출력되는 파일 이름을 지정하며, 커맨드 라인을 해석하면 링크 없이 컴파일만(-c), 디버깅 심벌을 포함하고(-g), 모든 경고 활성화(-Wall), 출력 파일은 foo.o로 하라는 뜻이 된다.

이제, 앞으로 make는 foo.o의 갱신 여부를 foo.h, foo.c 파일의 타임스탬프를 보고 결정한다. 만약, 두 파일이 수정되었다면 make는 자동으로 foo.o를 새로 컴파일하고, foo.o와 의존성 관계가 있는 부분을 다시 컴파일하게 된다.


이제, 실제 예를 하나 들어보자. 화면에 Hello, World!를 출력하는 프로젝트에 대해, GNU make를 사용하는 Makefile을 작성해야 한다고 하자. Hello, World!를 출력하는 모듈은 hello.h와 hello.cpp로 구성되어 있고, 화면에 글자를 출력하는 모듈은 display.o이며, display.h와 display.cpp로 구성되어 있다.


#makefile의 주석은 #으로 시작한다.
CPPC = g++
OBJ_CPPFLAGS = -c -g -Wall -I.
BIN_CPPFLAGS = -g -Wall
BIN_NAME = hello

$(BIN_NAME) : hello.o display.o
    $(CPPC) $(BIN_CPPFLAGS) hello.o display.o -o $@

hello.o : hello.h hello.cpp
    $(CPPC) hello.cpp $(OBJ_CPPFLAGS) -o $@

display.o : display.h display.cpp
    $(CPPC) display.o $(OBJ_CPPFLAGS) -o $@


아주 간단하게 구성해봤는데, 매크로를 좀 더 잘 활용한다면 더 간결하게 작성할 수 있을 것이다. (실제로 이렇게 엉터리로 구성되는 Makefile은 없다) 이렇게 Makefile을 작성하고, 그냥 간단히 make만 두들기면 make가 알아서 컴파일러에게 작업을 지시하며, 코드를 수정했다면 '영리하게' 다시 컴파일해준다. Makefile을 다룰 줄 몰라 일일이 소스를 컴파일해왔다면, 빌드가 엄청나게 편리해진 것을 느낄 수 있을 것이다. make는 기본적으로 'Makefile'을 사용하게 되며, 다른 Makefile을 이용하고 싶다면 -f 옵션으로 해당 Makefile을 지정하면 된다. 예를 들어, Makefile_Usr라면, make -f Makefile_Usr를 입력하면 된다.

그리고, 놀랍게도 Visual C++의 nmake 역시 위의 Makefile을 '거의 그대로' 사용할 수 있다. 사실, Visual C++ cl.exe는 '-' 스타일의 옵션 입력도 지원하고, gcc와 옵션, 그 의미도 대단히 흡사하기 때문에, 위의 Makefile이 수정이 필요하지는 않다. 다만, CPP라는 미리 정의된 매크로가 cl.exe를 지정하고 있고, -Wall은 너무나 많은 경고가 나오기 때문에 -W3으로 경로 레벨을 낮추는 정도면 충분할 것이다. nmake는 미리 정의된 매크로가 많기 때문에 nmake를 사용한다면 이들을 잘 알고 있어야 한다.


잘 구성된 크로스 플랫폼 소프트웨어들은, Visual Studio 솔루션 파일 외에도 커맨드 라인에서 바로 컴파일될 수 있도록 nmake용 Makefile을 제공하는데, 이것은 위에서 본 것처럼 근본적으로 대단한 마술을 부린 것이 아니다. 물론, 이런 소프트웨어들은 automake, MSBuild, 좀 더 세련된 매크로 사용으로 대단히 잘 만들어진 Makefile을 가지고 있지만 이런 Makefile을 작성하는 것은 생각보다 어려운 것이 아니다. 위의 코드처럼 차근차근 Makefile을 활용하다보면 언젠가는 높은 수준의 자동화된 빌드 프로세스를 구축할 수 있을 것이다.

nmake 활용이 당장 부담스럽다면, Visual Studio는 nmake 외에도 dsw, sln 파일을 바로 해석해서 컴파일 할 수 있는 도구(msdev.exe)를 제공하는데, 이것을 사용하면 된다. 위에서 말했듯이, Visual Studio는 결코 외관만 화려한 껍데기 개발툴이 아니다. 그리고, 이와 같은 자동화된 빌드 프로세스 구축은 실전에서는 필수적이며, 생각보다 자주 빌드 플랫폼을 변경하기 때문에 이러한 개발툴의 사용법을 잘 알고 있어야 한다.