프로그래밍 일반 2014. 10. 24. 11:28

통합 빌드 시스템(Unity Build) 허드슨에 구축하기

예전에 통합 빌드 시스템(Unity Build)에 관한 이야기를 한적이 있었다.


링크 : 통합 빌드 시스템(Unity Build)


이번에는 통합 빌드 시스템을 허드슨 msbuild 에서 사용하는 방법에 대한 이야기해보려고 한다.


Pre-Build Event 명령어

$(SolutionDir)externals\UnityMaker\UnityMaker.exe $(ProjectPath) IndonesiaRelease UnityBuild 2


우선 빌드전 이벤트 명령어가 달라졌는데 개발 환경에 따라 유연하게 대처하기 위한 매개변수들이 늘어났을 뿐이다.


로컬에서 개발하는 경우에는 해당 명령어가 잘 동작하지만 허드슨에서는 빌드 환경 설정에 따라 문제가 발생할 수도 있는데


허드슨 MSBuild 를 사용하는 경우 VS와 매크로가 다르기 때문이다.


로컬 빌드에서는 VS Pre-Build Event 에 실행 명령어를 작업하면 되고 라이브 배포용 빌드에서는 해당 이벤트 명령어를 빌드에서 제외 시킨다.





허드슨 빌드 환경에서 프로젝트 빌드 전에 Execute windows batch command를 하나 추가한다.


build step를 하나 추가하고 'Build a Visual Studio project or solution using MSBuild' 위로 추가한 build step을 드래그해서 올린다.


@rem UnityMaker 실행

SET SOLUTION_DIR=%CD%\

SET PROJECT_XML=%CD%\src\Client.vcproj

%SOLUTION_DIR%externals\UnityMaker\UnityMaker.exe %PROJECT_XML% %_JOBS_PROJECTBUILD% UnityBuild 2

SET ERR_LEVEL=%errorlevel%


@rem UnityMaker.exe 처리 성공한다면 .vcproj 변경 커밋

IF "%ERR_LEVEL%"=="0" (

 svn commit %PROJECT_XML% -m "hudson unity build"

)


exit /b %ERR_LEVEL%


build step에 로컬빌드와 같은 의미의 명령어를 환경에 맞도록 작성해주면 허드슨 셋팅은 끝이다.


이 단계에서 고려할 사항이 혹시라도 기존에 작성한 통합 빌드 시스템 구축하기에 첨부한 소스를 참고해서 Unitymaker를 만들었다면 허드슨 빌드 환경에서 빌드에 실패할 것이다.


그 이유는 UnityMaker의 리턴 값 때문인데..기본적으로 명령 프롬프트 상에서 어떤 프로그램이나 명령어를 실행시킬 때, 프로그램이 성공적으로 끝나면 정수 0(제로)을 OS에 반환하며 끝낸다는 룰 때문이다. 


이것을 Error Level 이라고 하는데 %ERRORLEVEL% 이라는 환경 변수 안에 최근에 종료된 프로그램/명령어가 돌려준 값이 들어 있다.


BuildLog.htm 파일을 확인하면 아래와 같다.





이런 문제는 UnityMaker 프로그램이 정상적으로 종료될 때 0을 리턴하도록 수정하고 실패 리턴값을 세분화하면 된다.





프로그래밍 일반 2014. 9. 15. 11:56

통합 빌드 시스템 구축하기 (Unity Build)


코드 종속성를 해결하기 위한 작업들은 너무 기본적인 부분이기 때문에 여기에선 논외로 치고..


프로젝트 규모가 커지면 왜 이렇게 빌드 시간이 오래 걸리는가..


빌드 명령어를 실행했을 때 컴파일러가 하는 일을 한번 들여다보자..


VS C/C++ 명령어에 /E 옵션을 추가하면 컴파일러의 전처리 과정을 확인 할 수 있는데 해당 옵션을 적용 시킨 후 간단한 hello world 프로그램을 빌드해보자.


결과를 보면..



3444 라인..


???


여기에 간단한 클래스를 추가해보자..

#pragma once


struct SBossSkill;

class CBoardProcess;


namespace skill

{

class CSkill

{

public :

CSkill(const SBossSkill* pSkillInfo = NULL) : m_pReferSkillBaseInfo(pSkillInfo) 

{

}

virtual ~CSkill() {}


// 스킬을 실행 합니다.

BOOL IsExecuteSkill(int iTargetPnum, CBoardProcess* pBoardPorcess) const;


protected:

virtual BOOL IsExecute(int iTargetPnum, CBoardProcess* pBoardPorcess, const std::vector<int>& myBlockList) const = 0;


protected:

const SBossSkill* m_pReferSkillBaseInfo;

};

}


스킬을 상속받는 모래폭풍을 추가하고..

#pragma once


#include <vector>


#include "skill.h"



namespace skill

{


class CSandStorm

: public CSkill

{

public :

CSandStorm(const  SBossSkill* pSkillInfo = NULL) : CSkill(pSkillInfo) {}

virtual ~CSandStorm() {}


protected:

virtual BOOL IsExecute(int iTargetPnum, CBoardProcess* pBoardPorcess, const std::vector<int>& myBlockList) const override;

};

}


심플하게 관리자를 하나 둔 후..

#pragma once


#include <map>

#include "noncopyable.h"


namespace EBossSkill { enum Type; }

struct SBossSkill;



class CSkillManager

: private virtual INonCopyable

{

private:

CSkillManager() {}

virtual ~CSkillManager();


public :

static CSkillManager& GetInstance() {


static CSkillManager instance;

return instance;

}


BOOL LoadData();


const SBossSkill* GetSkillInfo(EBossSkill::Type find);

const skill::CSkill* GetSkillObject(EBossSkill::Type find);


private:

typedef std::map<EBossSkill::Type, SBossSkill*> SKILL_INFO;

typedef std::map<EBossSkill::Type, skill::CSkill*> SKILL_EXE;


SKILL_INFO m_bossSkillInfo; // 스킬 정보

SKILL_EXE m_bossSkillExe; // 스킬 실행

};


빌드를 하면..



151039 라인...


스킬을 하나 더 추가하면..


164116 라인...


모듈이 헤더파일을 포함하면 포함한 만큼 컴파일러에 전처리 과정이 추가된다..


이러한 이유 때문에 프로젝트의 크기가 커져서 모듈의 수가 많아지면 많아질 수록 빌드시간은 늘어나게 되는 것이다..


모듈에서 호출되는 기본 라이브러리들은 미리컴파일된 헤더에 포함하고.. 코드 종속성도 고려해서 작업하면 어느정도 효과를 볼 수 있다.


담당중인 프로젝트에서도 기본 라이브러리들은 미리컴파일된 헤더에 포함시키고 실타래처럼 역인 코드 종속성을 완화시키는 작업을 통해 재컴파일되는 파일 수량은 기존대비 90%감소하고 리빌드 시간은 22% 감소했다.


대단히 빨라진 것 같지만.. 아직도 3분 40초대.. 프로젝트가 작아서 이정도지 만약 규모가 큰 프로젝트라면 10분에서 30분도 걸리는게 빌드 시간이라는걸 생각하면 부족한게 사실..


그러면 이쯤에서 Unity Build에 대해 알아보도록 하자..


유니티 빌드의 개념은 단순하다.


위에서 모듈이 늘어날 수록 동일한 전처리 과정이 늘어나서 빌드 시간이 길어진다는 사실을 알았기 때문에 소스가 하나라면 불필요한 전처리 과정은 사라지지 않을까..


테스트..

cpp 파일을 빌드에서 제외 시킨 후


cpp를 하나로 통합하고..

// HelloWorld.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"


#include "SandHell.h"


BOOL skill::CSandHell::IsExecute( int iTargetPnum, CBoardProcess* pBoardPorcess, const std::vector<int>& myBlockList ) const 

{

return TRUE;

}



#include "SandStorm.h"


BOOL skill::CSandStorm::IsExecute( int iTargetPnum, CBoardProcess* pBoardPorcess, const std::vector<int>& myBlockList ) const 

{

return TRUE;

}



#include "skillManager.h"

#include "SandStorm.h"

#include "SandHell.h"


BOOL CSkillManager::LoadData()

{

return TRUE;

}


const SBossSkill* CSkillManager::GetSkillInfo( EBossSkill::Type find )

{

return NULL;

}


const skill::CSkill* CSkillManager::GetSkillObject( EBossSkill::Type find )

{

return NULL;

}



int _tmain(int argc, _TCHAR* argv[])

{

printf("hello world\n");

return 0;

}

빌드하면..

70838 라인...

불필요한 전처리가 많이 줄었다..


그럼 작업 중인 프로젝트의 소스파일을 어떻게 통합하느냐...


// HelloWorld.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"


#include "SandHell.cpp"

#include "SandStorm.cpp"

#include "skillManager.cpp"



int _tmain(int argc, _TCHAR* argv[])

{

printf("hello world\n");

return 0;

}

cpp를 직접 포함하면 된다.


이쯤에서 중간 정리를 해보면..


수작업

  • 기존 솔루션에 통합 cpp 추가
  • 통합 cpp 빌드에서 제외
  • 통합 빌드 환경 추가 
  • 통합 빌드 환경은 미리 컴파일된 헤더를 사용하지 않는다.

 자동화

  •  stdafx.cpp 와 통합 cpp 파일을 제외한 cpp 파일들을 빌드에서 제외
  • 빌드에서 제외한 cpp 파일을 포함한 통합 cpp 파일 갱신
  • 빌드 전 이벤트로 실행



이제 vc 프로젝트 파일을 메모장으로 열어보면..


.vcproj 파일이 xml 문서라는 사실을 알 수 있는데..

자동화하는 방법은 프로젝트 파일을 파싱해서 대상 파일들을 빌드에서 제외시키고 제외된 목록을 알아오는 것으로 간단히 처리할 수 있다.


xml 파서 오픈 소스 라이브러리 : 

Markup115.zip



파일을 두개로 통합하는 예)

1. 기존 솔루션에 통합빌드 소스 추가 후 빌드에서 제외 시키기.




2. 통합 빌드 환경 추가




3. 통합 빌드 환경은 미리 컴파일된 헤더를 사용하지 않는다.




4. 대상 파일들을 빌드에서 제외하고 통합 빌드 파일에서 include 한다.






5. 자동화 프로그램 UnityMaker


// stdafx.h 

// CMakeUp 클래스가 stl을 사용하도록 셋팅

#define MARKUP_STL


메인 로직




함수들..



UnityMaker.exe 실행 배치 파일 Base.bat

@rem 경로 단순화

CD..


@rem 실행파일, 프로젝트 xml, 솔루션, 통합작업에서 제외할 필터 이름, 통합 소스 수

%CD%\externals\UnityMaker\UnityMaker.exe %CD%\src\Client.vcproj Base UnityBuild 2


pause



6. 빌드 이벤트 등록




7. 적용 결과

디버그 빌드 37초

릴리즈 빌드 30초


대략 80 ~ 90% 정도 감소를 보인다.


빌드 시간 확인하는 방법

툴 -> 옵션 -> 프로젝트 & 솔루션 -> 프로젝트 셋팅 -> Build Timing (Yes)



8. 참고 사항

- define, 지역 변수, enum 등 이름 충돌 문제는 이번 기회에 수정하자.

- 디버깅도 가능 잘된다.

- include 누락은 기존 빌드환경에서 테스트 가능하다.


9. 링크 결과물




10. 소스


UnityMaker.zip




11. 통합 빌드 시스템 허드슨에 구축하기

링크 : 통합 빌드 허드슨에 구축하기