자습

220713_기본 틀 작업, .bat파일 자동화

hyrule 2022. 8. 20. 23:01

> 오늘의 목표: 솔루션 내에서 각각 다른 기능을 하는 3개의 프로젝트를 생성하고, 연결시킨다.

새로 배운 지식들

💡
컴퓨터 메모리의 구성
  • 스택: 지역변수
  • 데이터 영역: 전역 변수 혹은 정적변수
  • 힙: 동적할당
  • 코드: 함수 등 우리가 작성한 코드가 올라가는 영역

💡
정적 라이브러리
  • 정적 라이브러리는 아예 프로그램에 라이브러리의 코드를 포함시킨다(바이너리 코드 형태)

    → 그래서 exe 파일이 만들어지면 라이브러리 파일은 필요하지 않다.

    • 정적 라이브러리 내부의 바이너리 코드를 사용하기 위해서는, 라이브러리를 포함시킨 프로그램 안에서 정적 라이브러리의 코드들에 대한 선언 파일(헤더 파일)을 반드시 가지고 있어야 한다.



< 사전 설정 >

> 가장 먼저 3개의 솔루션 폴더를 만들어 준다. 01. Client, 02. Engine, 03. Editor

> x64 모드로 전환한다.


01. Client2D 프로젝트 생성

  1. Client2D 프로젝트를 생성한다.
    • 데스크톱 애플리케이션, 빈 프로젝트로 생성.
  1. 생성한 프로젝트를 우선 제거하고, 폴더 정리를 해준다.
    • 소스코드 폴더로 들어가서, Client2D 폴더 안에 3개의 폴더를 생성한다.

    Bin, BinObj, BinObjDebug, Include 폴더 생성

    • 정리가 끝났으면, 기존 프로젝트 추가를 통해 도로 추가한다.

  1. 추가가 완료되었으면, 프로젝트 속성에 들어가 나머지 설정을 해준다.
    • 모든 구성

  • Debug

  • Release

  1. main.cpp 를 생성해주고, WinMain 함수를 작성해준다.
    #include <Windows.h>
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPWSTR    lpCmdLine,
        _In_ int       nCmdShow)
    {
    
    
        return 0;
    }


02. Engine 프로젝트 추가

1. ‘AR41Engine’라는 이름으로 프로젝트를 Windows 데스크톱 마법사를 통해 생성한다.

애플리케이션 종류: ‘정적 라이브러리(.lib)’

추가 옵션: 빈 프로젝트

2. 마찬가지로 프로젝트를 솔루션에서 제거 후 위처럼 폴더 작업을 해준 후 다시 포함시킨다.

3. API 프레임워크의 의 GameInfo.h와 같이, 공통으로 포함시키는 헤더를 모아놓는 헤더 파일을 생성한다.

  • < EngineInfo.h >
    #pragma once
    
    #include <Windows.h>
    #include <vector>
    #include <list>
    #include <unordered_map>
    #include <crtdbg.h>
    #include <typeinfo>
    #include <string>
    #include <functional>
    #include <algorithm>
    #include <stack>
    
    #define	DECLARE_SINGLE(Type)	\
    private:\
    	static Type*	m_Inst;\
    public:\
    	static Type* GetInst()\
    	{\
    		if (!m_Inst)\
    			m_Inst = new Type;\
    		return m_Inst;\
    	}\
    	static void DestroyInst()\
    	{\
    		if(m_Inst)\
    		{\
    			delete m_Inst;\
    			m_Inst = nullptr;\
    		}\
    	}\
    private:\
    	Type();\
    	~Type();
    
    #define	DEFINITION_SINGLE(Type)	Type* Type::m_Inst = nullptr;
    
    struct Resolution
    {
    	unsigned int	Width;
    	unsigned int	Height;
    };

4. class CEngine

  • < CEngine.h > 전문 보기
    #pragma once
    
    #include "EngineInfo.h"
    
    class CEngine
    {
    private:
    	HINSTANCE	m_hInst;
    	HWND		m_hWnd;
    	Resolution	m_WindowRS;
    	static bool	m_Loop;
    
    public:
    	Resolution GetWindowResolution()	const
    	{
    		return m_WindowRS;
    	}
    
    	HWND GetWindowHandle()	const
    	{
    		return m_hWnd;
    	}
    
    	HINSTANCE GetWindowInstance()	const
    	{
    		return m_hInst;
    	}
    
    public:
    	bool Init(HINSTANCE hInst, const TCHAR* Title,
    		const TCHAR* ClassName, int IconID, int SmallIconID,
    		unsigned int WindowWidth,
    		unsigned int WindowHeight, 
    		unsigned int DeviceWidth, unsigned int DeviceHeight,
    		bool WindowMode = true);
    	int Run();
    
    private:
    	void Logic();
    	void Input(float DeltaTime);
    	bool Update(float DeltaTime);
    	bool PostUpdate(float DeltaTime);
    	void Collision(float DeltaTime);
    	void Render(float DeltaTime);
    
    
    private:
    	void Register(const TCHAR* ClassName, int IconID, int SmallIconID);
    	bool Create(const TCHAR* Title, const TCHAR* ClassName);
    
    private:
    	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    
    	DECLARE_SINGLE(CEngine)
    };

  • 싱글턴 형태로 생성한다.
  • 윈도우 창을 생성하는 과정과, 메시지 루프를 처리하는 과정은 API때의 프레임워크와 동일하다.

  • 변수
    	HINSTANCE	m_hInst;
    	HWND		m_hWnd;
    	Resolution	m_WindowRS;
    	static bool	m_Loop;
  • Init()
    public:
    	bool Init(HINSTANCE hInst, const TCHAR* Title,
    		const TCHAR* ClassName, int IconID, int SmallIconID,
    		unsigned int WindowWidth,
    		unsigned int WindowHeight, 
    		unsigned int DeviceWidth, unsigned int DeviceHeight,
    		bool WindowMode = true);
    • 아래의 4가지 변수는 클라이언트 영역에서 전달받을 변수이다.
      • const TCHAR* Title
      • const TCHAR* ClassName
      • int IconID
      • int SmallIconID

      클라이언트 영역에서 창을 생성하고, 엔진에서는 해당 변수를 받아 창을 생성해준다.(WIN32API 때의 창 생성 과정과 동일

    💡
    윈도우 API에서는 특별하게 설정해주지 않으면 창 크기가 곧 해상도였지만, DX에서는 창 크기와 해상도를 따로 취급하므로 두 가지를 따로 받는다.

  • Register()
    private:
    	void Register(const TCHAR* ClassName, int IconID, int SmallIconID);
    • Init 메소드 안에서 매개변수를 전달받아 호출한다.
    • 상세 내용은 Win32API의 Register() 메소드와 동일하다. 매개변수만 받아서 호출해준다.

  • Create()
    private:
    	bool Create(const TCHAR* Title, const TCHAR* ClassName);
    • Init() 안에서 호출한다. 매개변수로 받은 Title과 ClassName을 창의 이름으로 사용한다.

  • WndProc()
    private:
    	static bool	m_Loop;
    	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    • 정적 함수로 선언. 메시지 루프를 처리한다.


03. Editor 프로젝트 추가

다른 프로젝트들과 마찬가지로, 프로젝트를 우선 제거 후 Include, Bin, BinObj, BinObjDebug 폴더를 만들어 정리 후 다시 추가한다.

  • 여기에도 main.cpp를 추가해준다. 일단 wWinMain() 함수만 생성한다.
#include <Windows.h>

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{


    return 0;
}

만들어진 라이브러리를 링크하기

💡
위에서 만든 Engine 프로젝트를 빌드하면 Engine.lib 파일이 생성될 것이다.

💡
이제 이 엔진 라이브러리를 클라이언트 영역에서 포함시켜서 사용해야 한다.
  • 사용하기 위해서는, 다른 클래스의 헤더 파일을 포함시키는 것 처럼 이 라이브러리 안의 코드들에 대한 선언이 들어있는 헤더 파일을 포함시켜야 한다.
  • 그런데 매번 엔진 영역의 헤더 파일을 건드릴 때마다 엔진 프로젝트의 헤더 파일을 수동으로 옮겨주어야 하는데, 매번 컴파일 때마다 이 작업을 하는 것은 분명 귀찮을 것이다.
  • 그러므로 엔진 영역을 컴파일할 때, 자동으로 헤더 파일을 클라이언트 영역으로 복사하도록 윈도우에 명령을 내려서 자동화를 해보자.
  • 이 작업은 윈도우 일괄 처리 명령(.batch 파일)을 통해서 해줄 것이다.

💡
batch 파일에서 해야 할 일은 다음과 같다.
  • 라이브러리 프로젝트에서 .lib 파일이 만들어지면 해당 파일을 ‘각 프로젝트/Bin’ 폴더 안으로 복사
  • 솔루션 파일이 있는 공간에 Engine 폴더를 생성한다. 추가로 하위 폴더에 Bin 폴더와 Include 폴더를 생성한다.
    • Bin 폴더에는 .lib 파일을, Include 폴더에는 .h 파일을 복사

💡
batch 파일 만들기
  • .lib 파일은 빌드가 완료되면 생성된다. 그러므로 빌드 이후에 batch 파일이 동작하도록 해 주어야 한다.
  • 솔루션 우클릭 - 추가 - 새 항목에 들어가서 텍스트파일을 생성하고, 생성된 텍스트 파일의 이름을 Copy.bat으로 바꿔 준다.

💡
프로젝트에서 빌드 후 자동 실행되도록 설정하기
  • 엔진 프로젝트 속성 → 빌드 이벤트 → 빌드 후 이벤트 순서로 들어가준다.
  • ‘모든 구성’을 선택하고, 명령줄에 다음 명령을 추가한다. call $(SolutionDir)Copy.bat
  • 이러면 프로젝트가 빌드되고 나서 자동으로 솔루션 디렉토리에 있는 ‘Copy.bat’ 파일이 실행된다.

💡
Copy.bat 파일 수정하기
  • Engine 프로젝트의 빌드가 끝난 뒤 .bat 파일이 실행되면, 프로젝트 파일이 있는 (SolutionDir)/Engine/Include 폴더가 기준 경로로 잡히게 된다.
    • 이 경로에서 다시 솔루션 폴더 기준으로 돌아올 필요가 있다. ‘cd..’ 명령을 사용하면 한 단계 상위 폴더로 이동한다. 솔루션 디렉토리까지는 2단계 위이므로 2번 해준다.
  • xcopy: 기준 폴더에 있는 특정 파일(들)을 목표 폴더로 복사하는 명령어
    🔑
    dos 명령어의 사용법이 궁금할 때는, 명령 프롬프트에서 ‘(명령어) /?’ 를 치면 사용법이 나온다. ex) xcopy /?
    • 변경된 파일들에 대해서만 복사를 하면 되므로 /d를 사용한다.
    • 모든 파일 및 폴더의 헤더 파일을 복사애야 하므로 /s를 사용한다.
    • 기존 파일이 있으면 무조건 덮어써야 하므로 /y를 사용한다.
    • Include 폴더에서 복사하는 경우 .h 파일만 복사하면 된다 → *.h만 복사
    • Bin 폴더에서는 이외의 파일을 복사해야 할 경우만 생기므로 ‘링크용 파일만 제외’ 한 모든 파일을 복사하면 된다. → “*.*”를 복사
      • .pdb, .idb 등만 제외시키면 된다. → 메모장을 열어서 제외할 확장자를 줄바꿈으로 작성한다.

        → 이 파일을 솔루션 디렉토리에 ‘Exclude.txt’ 파일로 저장한다.

      • Bin 폴더에서 복사하는 파일들은 해당 파일들을 제외시키는 명령어인 /exclude:Exclude.txt 를 추가한다.

//Copy.bat
cd..
cd..
xcopy .\AR41Engine\Include\*.h .\Engine\Include\ /d /s /y
xcopy .\AR41Engine\Bin\*.* .\Engine\Bin\ /d /s /y /exclude:Exclude.txt
xcopy .\AR41Engine\Bin\*.* .\AR41Editor\Bin\ /d /s /y /exclude:Exclude.txt
xcopy .\AR41Engine\Bin\*.* .\Client2D\Bin\ /d /s /y /exclude:Exclude.txt

  • 빌드 후에 다른 프로젝트들의 Bin 폴더에 라이브러리가 정상적으로 복사되었다면 성공한 것이다.


Uploaded by N2T