Visual C++을 위한 지속적인 통합 빌드 서버

  • Post author:
  • Post category:
  • Post comments:14 Comments
  • Post last modified:February 8, 2020

그 동안 R&D 센터에서 고민만 해오던 빌드 서버를 내가 프로젝트에 본격적으로 참여하기 전에 설치하기로 마음 먹고 어제, 오늘 대공사를 치렀다. 축복받은 관리되는 환경(.NET Framework나 Java 같은)과 달리 네이티브 C++의 세계는 항상 어둠이 가득했다. 이 참에 손전등이라도 달아줄 생각이었다. 결국 인프라스트럭처가 잘 갖춰져 있어도 사람들이 적극적으로 써야 그 값어치를 다 하는 거지만, 나 혼자 생각으로 뚝딱 만든 게 아니니 잘 되리라 생각한다. R&D 센터에는 믿음직한 사람이 가득하니까.

웹 서버 설치

상황: Windows Server 2003에 Microsoft SQL Server 2005가 설치되어 있다. IIS 웹 서버는 설치 하지 않았다. CruiseControl .NET의 대시보드(Dashboard)를 설치하려면 IIS 웹 서버부터 설치해야 한다.

  1. IIS 웹 서버를 설치한다.
    [Control Panel – Add or Remove Programs – Add/Remove Windows Components – Application Server – Internet Information Services]
  2. 대시보드(http://localhost/ccnet/)에 접속하니 오류 메시지가 뜬다.

    If IIS was installed after the .NET framework, you will need to either run aspnet_regiis.exe or open IIS manager, right-click the ccnet website, select properties and then the ASP.NET tab. Make sure the ASP.NET version has something displayed in the dropdown (eg. .NET 2.0, 2.0.50727) and click OK.

    이렇게 해결했다.

    1. ASP.NET 설치한다.
      C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\aspnet_regiis.exe -I
    2. [IIS 관리자 – Web Service Extensions]에서 ASP.NET 활성화시킨다.
    3. IIS 관리자에서 ASP.NET 버전 선택해준다. 여기선 2.0을 골랐다.

소스 코드 관리 시스템 설치

CruiseControl .NET이 서브버전 저장소에 접근하려면 svn.exe가 필요하다. 그러니 윈도우용 서브버전을 설치했다. 물론 시스템 환경변수에 서브버전 바이너리 폴더가 등록되어 있는지 확인했다.

CruiseControl .NET 돌려보기

일단 서버에 깔려 있는 VS 2005로 컴파일하기로 한다. (원래는 SDK를 따로 다운로드 받아서, 그 안에 있는 컴파일러를 쓰는 게 정석이다.) 빌드 스크립트는 나중에 도입하고, 일단 CruiseControl .NET 설정 파일(ccnet.config)만 쓰자.

<cruisecontrol>
  <project>
   <name>Project Name</name>
    <webURL>http://domain/ccnet-</webURL>
    <workingDirectory>c:\src\project_name</workingDirectory>
    <triggers>
        <intervalTrigger seconds="60"/>
    </triggers>
    <modificationDelaySeconds>2</modificationDelaySeconds>
    <sourcecontrol type="svn">
      <trunkUrl>http://domain/svn/project_name/trunk</trunkUrl>
      <username>username</username>
      <password>password</password>
    </sourcecontrol>
    <tasks>
        <devenv>
            <solutionfile>C:\src\project_name\vs2005_solution.sln</solutionfile>
            <configuration>Debug</configuration>
            <buildtype>Build</buildtype>
            <executable>C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe</executable>
        </devenv>
    </tasks>
  </project>
</cruisecontrol>

일단 빌드 잘 되고, 대시보드에 결과가 잘 나온다. CCTray 역시 훌륭히 작동하는 걸 확인했다.

빌드 스크립트는 보류한다.

NAnt를 써서 좀 더 복잡한 빌드 작업을 해 볼 때이다. 하지만 NAnt의 최신 출시 버전이 1년 전에 나온 거라 께름칙하다. Visual Studio 2005의 솔루션 파일(*.sln)을 지원 못하는 문제도 여전하고 일단 NAnt의 도입은 보류한다. 잠깐 MSBuild를 사용해볼까 고민하다가 관두기로 한다. 당장은 CruiseControl .NET을 돌아가게 만드는 일에 집중해야 한다. 빌드 스크립트가 제공하는 고급 기능은 추후에 요구 사항이 생기면 도입하자.

단위 테스트 라이브러리 선정

VC++ 단위 테스트로는 UnitTest++을 사용하기로 한다. 처음엔 CppUnit을 도입할까 했지만, 곧 생각을 바꿨다. 예전 기억을 되살려보면 CppUnit은 너무 불편하다. 세팅하고 테스트하는 과정이 무척 까다롭기 때문에 결국 테스트를 하는 사람이 없어지게 된다. 그래서 Boost Library의 단위 테스트 기능이나 여러 대안을 모색해봤다. 그러고 나서 낙점한 것이 바로 UnitTest++이다.

CruiseControl .NET과 UnitTest++의 연동

UnitTest++ 사이트에서 제공하는 Money 프로젝트를 사용해 실험해보기로 한다. 이번엔 CruiseControl .NET의 설정 파일(ccnet.config) 뿐만 아니라 단위 테스트 프로젝트의 main() 함수도 고쳐야 한다.

<cruisecontrol>
    <project>
        <name>Money</name>
        <webURL>http://domain/ccnet</webURL>
        <workingDirectory>c:\src\Money</workingDirectory>
        <triggers>
            <intervalTrigger seconds="60"/>
        </triggers>
        <modificationDelaySeconds>2</modificationDelaySeconds>
        <sourcecontrol type="nullSourceControl">
        </sourcecontrol>
        <tasks>
            <devenv>
                <solutionfile>C:\src\Money\MoneyTestApp.sln</solutionfile>
                <configuration>Debug</configuration>
                <buildtype>Build</buildtype>
                <executable>C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe</executable>
            </devenv>
            <exec>
                <executable>MoneyTestApp.exe</executable>
                <buildArgs>C:\src\Results.xml</buildArgs>
                <baseDirectory>C:\src\Money\bin\</baseDirectory>
                <buildTimeoutSeconds>10</buildTimeoutSeconds>
            </exec>
        </tasks>
        <publishers>
            <merge>
                <files>
                    <file>C:\src\Results.xml</file>
                </files>
            </merge>
            <xmllogger/>
            <statistics/>
        </publishers>
    </project>
</cruisecontrol>

테스트이니 소스 코드 관리 시스템은 쓰지 않는다고 치자. CruiseControl .NET이 UnitTest++을 지원하지 않기 때문에 exec 태스크를 사용해서 테스트를 실행시킨다. (CruiseControl .NET 사이트에 CppUnit을 연동하는 방법이 적혀 있는데, 이것을 참고 삼았다.) 이때 테스트 코드의 main() 함수는 다음과 같다. 출력 결과를 저장할 파일 이름이 인자로 들어오면 XML로 보고하고, 아니면 콘솔에 출력한다. 이렇게 하면 VS 2005와 연동하고 동시에 CruiseControl .NET과도 연동할 수 있다. (참고 자료. UnitTest++: Reports)

#include "UnitTest++.h"
#include "TestReporterStdout.h"
#include "XmlTestReporter.h"
#include <fstream>
#include <vector>
#include <string>

int main(int argc, char const ** argv)
{
    std::vector<std::string> args;
    if(argv)
    {
        while(*argv != NULL)
        {
            args.push_back(*argv);
            argv ++;
        }
    }

    if(args.size() == 1)
    {
        UnitTest::TestReporterStdout reporter;
        return UnitTest::RunAllTests(reporter, UnitTest::Test::GetTestList(), 0);
    }

    std::ofstream f(args[1].c_str());
    UnitTest::XmlTestReporter reporter(f);
    return UnitTest::RunAllTests(reporter, UnitTest::Test::GetTestList(), NULL, 0);
}

대시보드 손보기

CruiseControl .NET이 UnitTest++을 지원하지 않기 때문에 테스트 결과를 제대로 출력하려면 C:\Program Files\CruiseControl.NET\webdashboard\xsl\unittests.xml파일을 수정해야 한다. 250줄 가량 되기 때문에 파일은 첨부한다. 여기서 unittest__가 들어간 곳이 수정된 부분이다.

결과 보기

참고. 혹시나 싶어 프로젝트 이름은 스크린샷에서 지워버렸다.

CCTray
CCTray

CCTray가 빌드 오류, 정확히 말해 테스트 실패를 개발자에게 알려준다. 무슨 일인가 싶어 대시보드를 확인하러 간다.

대시보드
대시보드

Money 프로젝트의 테스트 코드 중 일부를 일부러 고쳐놓아서 오류가 발생한다. 문제가 된 프로젝트를 클릭해서 무슨 일인지 확인한다.

빌드 결과
빌드 결과

어떤 테스트가 문제인지 바로 확인할 수 있다.

참고 자료

소감. 우와, 이거 하느라 엄청 고생했는데, 글로 쓰고 나니 분량이 얼마 안 된다.

Author Details
Kubernetes, DevSecOps, AWS, 클라우드 보안, 클라우드 비용관리, SaaS 의 활용과 내재화 등 소프트웨어 개발 전반에 도움이 필요하다면 도움을 요청하세요. 지인이라면 가볍게 도와드리겠습니다. 전문적인 도움이 필요하다면 저의 현업에 방해가 되지 않는 선에서 협의가능합니다.
0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

14 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
outsight
outsight
17 years ago

고생했다 재훈아…

최재훈
17 years ago

코드 짜는 사람이 고생이죠 뭐. ㅎㅎ

kabbala
17 years ago

이제 우리나라에도 nightly build라는 말이 쓰이는건가요? (24시간 빌드 머신인건가?)

최재훈
17 years ago

아, 답변이 늦었네요. 다른 짓 하다가 잊어먹은 것 같네요. 

지속적인 통합은 일일 빌드보다 앞선 개념입니다. 설정에 따라 일일 빌드처럼 쓸 수도 있습니다. 하지만 보통 수 분에 한번씩 소스 코드 커밋이 일어났는지 확인하고, 그때마다 빌드를 해보게 됩니다. 빌드라 함은 컴파일 뿐만 아니라 단위 테스트, 코드 품질 검사 등을 모두 포함합니다. 

jessie
17 years ago

좋은 자료 보고 많은 참고가 되었습니다..
저희 프로젝트에서도 CCNet 을 이용한 CI 를 구축해보려고 하는데.. 테스트 유틸중 UnitTest++ 을 사용해 보려고 세팅을 해보았습니다.. 혹시 console application 으로 만든 테스트 프로그램에서 테스트 모듈안에 assert() 과 같은 문구가 나왔을때 어떻게 처리해야 할지 갑갑합니다.. 콘솔창으로 실행할때 assert 윈도우가 뜨면서 break retry ignore 의 선택을 물어보게 되는데 이 때 빌드및테스트를 하는 서버에서는 이후 진행이 먹통이 되게 되는군요.. 테스트 프로그램을 디버그에서 돌려야 할 것 같은데 이 문제를 어떻게 해결해야 할지 모르겠습니다..

최재훈
17 years ago

테스트 대상이 되는 코드에 assert가 있는 상황이지요? 그렇다면 assert를 #if 등을 사용해서 두 가지 정도로 나누시는 게 어떨까요? release에선 기존 그대로 assert를 가져가시고, _DEBUG나 _UNITEST가 정의되어 있는 상황이라면 assert를 호출할 때 crash를 내지 않고 exception을 던지는 겁니다. 그리고 단위 테스트 코드에선 try/catch로 받는 거지요.

사실 제가 손보고 있는 코드에도 비슷한 부분이 있어서 수정하려고 맘 먹고 있습니다. 요즘 다른 일에 집중하느라 잠시 잊고 있었네요.

jessie
17 years ago

넵~ 좋은 답변 감사드립니다~ ^^ 어제 고민을 하다가 assert 를 그냥 재정의해서 무리없이 통과시켜 버릴까 했는데.. exception 을 던지는게 좋은 방법일듯 하군요..;

최재훈
17 years ago

원하시는 답변이었다니 다행입니다. 사실 테스트 코드니까 출시용에선 성능 문제 때문에 꺼리던 예외도 그리 문제가 안 되니까요. 

황재호
황재호
15 years ago

질문드립니다.
1. Visual C++ 6.0을 쓰는 사람은 CI를 하기
  위해서는 Visual Stdio 2005이상으로 upgrade를
  해야하나요?

2. CI서버와 소스코드관리서버는 물리적으로 다른
  서버를 쓰는게 편리한지요?

3. Visual Stdio 2008을 사용해도 설명하신 것처럼
  되는지요?

이상, 겨우, SVN과 TRAC 깔아보고 질문드립니다.

최재훈
15 years ago

1. VC 6.0이라고 못할 건 없겠지만 제가 해본 적이 없으니 넘어갑니다. CI 문제는 아니지만 컴파일러나 SDK는 최신 버전을 쓰는 편이 좋습니다. 마이그레이션이 항상 쉽지는 않습니다만……

2. 아무래도 그렇습니다. 빌드하느라 CPU 사용률이 100%로 치솟으면 소스코드를 내려받기 힘들거든요.

3. 저는 2005에서 2008로 넘어왔습니다.

김정형
김정형
15 years ago

안녕하세요? 촙 개발자입니다.
이번에 빌드 자동화를 공부하면서
CruiseControl.NET + TortoiseSVN을 사용한
환경을 만들고 테스트 중인데 잘 안되네요.

에서 args를 받는 방법이 없을까요?

김정형
김정형
15 years ago

압 태그를 먹는군요..ㅡㅜ “

“에서 아규먼트를 받는 방법이 있을까요? 홈페이지에 도큐먼트를 뒤져봐도 안나오네요…

김정형
김정형
15 years ago

허미 3번째네..ㅡㅜ sourcecontrol에서..로 정정

최재훈
15 years ago

제가 쓰는 방법을 알려드리지요. 우선 sourcecontrol 에서 autoGetSource를 false로 합니다. 그리고 MSBuildCommunityTasks에 있는 svn 기능을 이용합니다. 그러니까 ccnet이 직접 소스코드를 가져오는 대신 ccnet이 MSBuild 스크립트에게 소스코드를 가져오라고 시키는 것이죠.