premakeExample
1 簡介
本文講解如何基於premake5創建一個典型的C++解決方案,該解決方案包含三個項目:
- 一個依賴GLFW動態庫的動態庫項目ExampleDll
- 一個依賴ExampleDll動態庫的窗口程序APP
- 一個基於Catch的單元測試程序UnitTest
所有代碼已上傳Github,鏈接。
2 你需要準備
- premake5.exe程序
- 一個文本編輯器
- 一個支持C++11的編譯器,因爲源代碼使用了C++11特性
- 一個支持OpenGL 1.1的顯卡
3 文件的組織結構
首先需要確定整個解決方案中源代碼、三方庫、二進制等文件的組織結構,本文創建的文件結構如下:
premakeExample/
|–premake5.lua (premake腳本文件)
|–prj/ (生成的解決方案與各項目配置文件路徑)
|–build/
| |–target/ (目標生成路徑)
| |–obj/ (中間文件生成路徑)
|–bin/ (最終發佈的二進制文件路徑)
|–3rd/ (第三方庫文件路徑)
| |–Catch (Catch單元測試框架頭文件路徑)
| |–glfw (glfw三方庫相關文件路徑)
4 下載所需文件
本文以64位系統爲例,需下載64位二進制庫,也可以下載32位,但要記得在後面lua腳本中改變architecture配置。
將下載解壓後的include和
lib-vc*
文件夾放在premakeExample/3rd/glfw文件夾下,*
指代不同的vs版本,需與後續生成配置文件的vs版本一致。
將lib-vc*路徑下的glfw3.dll拷貝紙premakeExample/bin路徑下
- catch是一個只有頭文件的依賴庫,將Catch.hpp(下載)放在premakeExample/3rd/Catch/include路徑下即可。
5 添加代碼
5.1 ExampleDll
ExampleDll庫只是對glfw中的GLFWwindow進行了簡單封裝,隱藏了window的內部實現細節。Window類通過_declspec(dllexport)
導出。
- ExampleDll.hpp代碼如下:
#ifndef EXAMPLE_DLL_HPP
#define EXAMPLE_DLL_HPP 1
#include <string>
#include <memory>
struct GLFWwindow;
namespace ExDLL
{
class _declspec(dllexport) Window
{
public:
Window(int width, int height, const std::string& title);
~Window();
bool shouldClose() const noexcept;
void pollEvents() const noexcept;
void swapBuffers() const noexcept;
std::pair<int, int> getWindowSize() const noexcept;
private:
GLFWwindow* wnd;
};
}
#endif
- ExampleDll.cpp代碼如下
#include "ExampleDll.hpp"
#include <GLFW/glfw3.h>
namespace ExDLL
{
Window::Window(int width, int height, const std::string& title)
{
glfwInit();
wnd = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
glfwMakeContextCurrent(wnd);
}
Window::~Window()
{
glfwDestroyWindow(wnd);
glfwTerminate();
}
bool Window::shouldClose() const noexcept
{
return glfwWindowShouldClose(wnd) != 0;
}
void Window::pollEvents() const noexcept
{
glfwPollEvents();
}
void Window::swapBuffers() const noexcept
{
glfwSwapBuffers(wnd);
}
std::pair<int, int> Window::getWindowSize() const noexcept
{
std::pair<int, int> sz{};
glfwGetWindowSize(wnd, &sz.first, &sz.second);
return sz;
}
}
5.2 App
應用程序依賴Exampledll,創建一個窗口應用程序,並運用固定管線繪製一個紅色的三角形。當然本教程不講解OpenGL,如果想要學習OpenGL的使用,應該學習更先進的可編程管線。
- main.cpp代碼如下:
#include <ExampleDll.hpp>
#if defined _WIN32
//Windows平臺使用OpenGL需要包含Windows.h
#include <Windows.h>
//消除窗口程序的控制檯界面
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#endif
#include <gl/GL.h>
//導入ExampleDll中的Window類
class _declspec(dllimport) ExDLL::Window;
int main()
{
ExDLL::Window window{ 800, 600, "Hello World!" };
while (!window.shouldClose())
{
window.pollEvents();
//爲簡單起見,使用了古老的固定管線
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_TRIANGLES);
glVertex2f(-0.5f, -0.5f);
glVertex2f(0.5f, -0.5f);
glVertex2f(0, 0.5f);
glEnd();
window.swapBuffers();
}
return 0;
}
5.3 UnitTest
單元測試程序檢查創建的窗口大小是否與參數要求的大小一致。
- Test.cpp代碼如下:
#define CATCH_CONFIG_MAIN
#include <Catch.hpp>
#include <ExampleDll.hpp>
TEST_CASE("Window tests", "[ExampleDll]")
{
using namespace ExDLL;
Window w{ 600, 400, "Test Window" };
auto size = w.getWindowSize();
REQUIRE(size.first == 600);
REQUIRE(size.second == 400);
}
6 編寫premake5.lua腳本文件
premake5.lua是生成工程配置文件的核心。
- premake5.lua代碼如下:
-- 最終解決方案的名稱
workspace "premakeExample"
-- 解決方案與各項目配置文件生成路徑
location "prj"
-- 指定語言
language "C++"
-- 指定架構 x64 或 x86 或 x86_64
architecture "x64"
-- 配置類型
configurations {"Debug","Release"}
-- 針對Debug配置類型的參數設置
filter {"configurations:Debug"}
symbols "On"
-- 針對Release配置類型的參數設置
filter {"configurations:Release"}
optimize "On"
-- 重置過濾器的其他設定
filter {}
-- 目標文件生成路徑 如%{prj.name}爲內置的宏,指項目的名稱,如後面的ExampleDll,App等
targetdir ("build/target/%{prj.name}/%{cfg.longname}")
-- 中間文件生成路徑
objdir ("build/obj/%{prj.name}/%{cfg.longname}")
-- 編譯後命令行,將目標文件拷貝至bin文件夾,注意../bin/是prj文件夾的相對路徑
postbuildcommands{
("{COPY} %{cfg.buildtarget.relpath} \"../bin/\"")
}
-- 定義函數,包含glfw三方庫頭文件,可被其他工程調用
function includeGLFW()
includedirs "3rd/glfw/include"
end
-- 定義函數,鏈接glfw三方庫
function linkGLFW()
-- 指定lib的文件路徑
libdirs "3rd/glfw/lib-vc2019"
-- 指定lib文件名,即glfw3dll.lib,此處使用的是動態庫
links "glfw3dll"
end
-- ExampleDll項目
project "ExampleDll"
-- 類型爲動態庫項目
kind "SharedLib"
-- 代碼文件,即ExampleDll文件夾下的所有文件
files "src/ExampleDll/**"
-- 包含glfw頭文件
includeGLFW()
-- 鏈接glfw三方庫
linkGLFW()
-- 定義函數,鏈接ExampleDll動態庫
function useExampleDLL()
includedirs "src/ExampleDll"
links "ExampleDll"
end
-- App應用程序
project "App"
-- 類型爲控制檯程序
kind "ConsoleApp"
-- 代碼文件
files "src/App/**"
-- 鏈接ExampleDll動態庫
useExampleDLL()
-- windows平臺使用OpenGL需鏈接OpenGL32
filter "system:windows"
links {"OpenGL32"}
-- 定義函數,包含Catch
function includeCatch()
includedirs "3rd/Catch/Include"
-- 預定義宏,C++版本爲C++11及以上
defines "CATCH_CPP11_OR_GREATER"
end
-- UnitTests單元測試項目
project "UnitTests"
-- 類型爲控制檯程序
kind "ConsoleApp"
-- 代碼文件
files "src/UnitTests/**"
-- 包含Catch
includeCatch()
-- 鏈接ExamleDll
useExampleDLL()
7 生成工程文件並編譯
premake5.exe運行時時會尋找調用命令路徑下的premake5.lua文件,如果想生成工程配置文件,需在premakeExample路徑下調用premake5.exe <action>
,其中action
可指定爲vs2019
或gmake
,本文以vs2019爲例。
執行命令後在prj文件夾下,生成的工程配置文件如下:
打開premakeExample.sln編譯解決方案,在bin路徑下運行app.exe,效果如下:
命令行打開單元測試程序UnitTests.exe,效果如下: