Premake

引言

这个系列我们将从零开始开发一个自己的游戏引擎。上一次我们讨论日志记录时添加了一个名为spdlog的日志记录库。今天我们将以premake为基础重构项目文件。

必要性

如果我们仅仅是用Visual Studio制作我们的项目,在目前的Windows平台上看来是ok的,但最大的问题是当我们不得不处理不同的平台例如MacLinux甚至AndroidIOS,我们需要为特定的IDE生成不同的项目文件。

我们要使用的工具叫做Premake。这里不使用CMake的原因是我认为非常复杂,虽然使用效果好但它有很多格式化的东西,这种复杂对于我们而言不是必要的;而Premake在编写预生成文件时使用Lua生成项目文件,功能强大并且易于初学者理解。一旦我们定义好Premake的格式,事实上我们就可以定义Infinite是什么,例如C++我们需要包含这些头文件、这些是包含目录、这些是我们想要链接的库……在Lua中定义好我们想要设置的配置,我们就可以只需运行Premake,它将为项目生成Visual SutdioXcode的项目文件。

这就是我们为什么要使用这种项目生成器的原因而不是手动处理所有事情。可以想象手动维护所有内容,每次添加新文件时相关文件都会同时添加到MacWindows,但是我们必须手动进行所有操作,这很痛苦。

premake-core

在Github中下好发布版本,我选择的是Premake 5.0 alpha 15

下载好之后复制该.exe文件,在最初始的Infinite目录中新建vendor文件夹,这个文件夹与Infinite\Infinite\vendor那个存储spdlog的文件夹稍有不同,此文件夹适用于项目解决方案中的一切。继续在vendor中创建文件夹bin,在bin中创建premake文件夹,复制粘贴刚才的premake5.exe文件以及许可证:

Infinite
|__ vendor
|__ bin
|__ premake
|__ premake5.exe
|__ LICENSE.txt

premake5.lua

回到最初始的Infinite目录,新建premake5.lua,编写代码:

workspace "Infinite"
architecture "x64"
startproject "Sandbox"

configurations
{
"Debug",
"Release",
"Dist"
}

outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"

  • workspace可以当作解决方案;架构就是我们实际上想要处理的平台,我们选择x64
  • 配置我们设置了三种:调试Debug、发布Release和发行Dist
  • outputdir输出目录:%{cfg.buildcfg}即上面的配置;%{cfg.system}为系统;{cfg.architecture}是刚才的平台

Infinite

此部分用于构建Infinit/Infinite

project "Infinite"
location "Infinite"
kind "SharedLib"
language "C++"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"%{prj.name}/vendor/spdlog/include"
}

filter "system:windows"
cppdialect "C++17"
staticruntime "On"
systemversion "latest"

defines
{
"IFN_PLATFORM_WINDOWS",
"IFN_BUILD_DLL"
}

postbuildcommands
{
("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")
}

filter "configurations:Debug"
defines "IFN_DEBUG"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
optimize "On"
  • location项目位置;kind项目类型;language语言为C++
  • files包含文件,列出我们现在想要的文件即.h.cpp
  • includedirs引入spdlog
  • filter过滤器表示具有某种配置或程序属性

Sandbox

沙盒基本与Infinite一致,只需在一些地方稍作修改即可:

project "Sandbox"
location "Sandbox"
kind "ConsoleApp"

language "C++"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"Infinite/vendor/spdlog/include",
"Infinite/src/Infinite"
}

links
{
"Infinite"
}

filter "system:windows"
cppdialect "C++17"
staticruntime "On"
systemversion "latest"

defines
{
"IFN_PLATFORM_WINDOWS"
}

filter "configurations:Debug"
defines "IFN_DEBUG"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
optimize "On"
  • kind项目类型变为ConsleApp控制台程序
  • includedirs引入spdlogInfinite
  • links链接器与Infinite相连

完整源码

workspace "Infinite"
architecture "x64"
startproject "Sandbox"

configurations
{
"Debug",
"Release",
"Dist"
}

outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"

project "Infinite"
location "Infinite"
kind "SharedLib"
language "C++"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"%{prj.name}/vendor/spdlog/include"
}

filter "system:windows"
cppdialect "C++17"
staticruntime "On"
systemversion "latest"

defines
{
"IFN_PLATFORM_WINDOWS",
"IFN_BUILD_DLL"
}

postbuildcommands
{
("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")
}

filter "configurations:Debug"
defines "IFN_DEBUG"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
optimize "On"



project "Sandbox"
location "Sandbox"
kind "ConsoleApp"

language "C++"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"Infinite/vendor/spdlog/include",
"Infinite/src/Infinite"
}

links
{
"Infinite"
}

filter "system:windows"
cppdialect "C++17"
staticruntime "On"
systemversion "latest"

defines
{
"IFN_PLATFORM_WINDOWS"
}

filter "configurations:Debug"
defines "IFN_DEBUG"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
optimize "On"

启动

Infinite目录中新建GenerateProject.bat

call vendor\bin\premake\premake5.exe vs2019
PAUSE

保存退出后我们只需点击此文件便可生成相应的项目,但在此之前我们需要把以下文件全部删除以更新:

Infinite
|__ bin
|__ bin-int
|__ Infinite
| |__ Infinite.vcxproj
| |__ Infinite.vcxproj.filters
| |__ Infinite.vcxproj.user
|__ Sandbox
| |__ Sandbox.vcxproj
| |__ Sandbox.vcxproj.user
|__ Infinite.sln

这些文件premake都会帮你生成,所以不必担心。

调试

理论上上述步骤完成后再次运行就成功了,但实际操作的时候遇到了以下两个问题;

  • 生成解决方案的目录混乱
  • dll文件无法正常生成导致沙盒项目无法构建

针对第二点我目前的参考了网络的一种解决方案,在Infinite属性中的生成后事件添加:

xcopy /y "$(OutDir)Infinite.dll" "$(SolutionDir)bin\$(Configuration)-$(Platform)\Sandbox\"

系统报错就会停止,但生成的dll文件在另一个平行的文件夹中,原因是$(Configuration)-$(Platform)%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}还是不一致,而我浏览了宏也没有找到合适的,最后只能用拖拽的方式暂时将Infinite.dll拖回Sandbox',勉强运行成功。

后再次研究,将问题锁定到生成前事件的指令,我认为是指令不明确的原因,更改为xcopy /Y ...也可以解决问题。

IF EXIST ..\bin\Debug-windows-x86_64\Infinite\Infinite.dll\ (xcopy /Y  ..\bin\Debug-windows-x86_64\Infinite\Infinite.dll ..\bin\Debug-windows-x86_64\Sandbox > nul) ELSE (xcopy /Y ..\bin\Debug-windows-x86_64\Infinite\Infinite.dll ..\bin\Debug-windows-x86_64\Sandbox > nul)