ビルドツールpremakeの紹介

ビルドツール[premake](https://premake.github.io/index.html)についてご紹介。

- https://github.com/premake/premake-core

20161014追記
201609更新

Premakeの守備範囲はだいたいCMakeに似ていて、定義ファイルからvcソリューションやMakeFileの生成をすることができます。プロジェクトで各ライブラリのオプション(/MTとか/MTDとかDEFINEなど)をまとめて変えたい場合、生成物の出力ディレクトリをDebug, Release, Win32, Win64, VS2010, VS2013で違うディレクトリに出力したい、もしくは同じディレクトリに違う名前で出力したい場合などに設定をluaコードで記述することができます。
こういった場合、VSで開発しているとプロジェクトの設定を開いてマウスでぽちぽちしたりvcxprojのXMLを直接編集するのを強いられますし、CMakeではCMake語がよくわかりません。しかしPremakeで管理していれば次のように記述してソリューションを再生成するだけでさくっと設定完了できるかもしれません。

```lua:premake5.lua抜粋

-- PlatformとConfiguration毎に出力ディレクトリを分ける
filter {"platforms:Win32", "configurations:Debug" }
    targetdir "build/Win32/Debug"
filter {"platforms:Win32", "configurations:Release" }
    targetdir "build/Win32/Release"
filter {"platforms:Win64", "configurations:Debug" }
    targetdir "build/Win64/Debug"
filter {"platforms:Win64", "configurations:Release" }
    targetdir "build/Win64/Release"

-- すべてのPlatformとConfiigurationのvcのリンクをStaticRuntime(/MT, /MTdの方)にする
filter {} -- filterをクリア。すべてが対象になる
    flags { "StaticRuntime" }
```

#Download
https://premake.github.io/download.html
のリンクからPremake 5.0 (alpha)を取得するべし。

#HelloWorldプロジェクト
HelloWorldしてみる。
新しいディレクトリを作ってファイルを配置します。

```
+ hello_premake
  + hello.c
  + premake5.lua
  + premake5.exe
```

```c:hello.c
#include <stdio.h>

int main(void) 
{
   puts("Hello, word!");
   return 0;
}
```

各コマンドは呼び出しにスコープのような効果範囲が決まっていて
直近のprojectとfilterが呼び出しが範囲を決める。
エディタでインデントをかけるとフラットになっていしまうので
lua言語のブロックdo, endで人工的にインデントしてみた。

```lua:premake5.lua
-- premake5.lua
workspace "hello_premake"
configurations { "Debug", "Release" }

project "hello_premake"
do
    kind "ConsoleApp"
    language "C"

    files { "**.h", "**.c" }

    filter "configurations:Debug"
    do
        defines { "DEBUG" }
        flags { "Symbols" }
    end

    filter "configurations:Release"
    do
        defines { "NDEBUG" }
        optimize "On"
    end
end
```

premake5.exeを実行してプロジェクトを生成。

```bat:ソリューションを生成
> premake5.exe vs2015
Building configurations...
Running action 'vs2015'...
Generated hello_premake.sln...
Generated hello_premake.vcxproj...
Done (24ms).
```

が生成されます。
ビルド

```bat:コマンドラインでビルド
> set MSBUILD=C:\Program Files (x86)\MSBuild\14.0\Bin\msbuild.exe
> "%MSBUILD%" hello_premake.sln /p:Configuration=Release /p:Platform=Win32
Microsoft (R) Build Engine バージョン 14.0.25420.1
Copyright (C) Microsoft Corporation. All rights reserved.

このソリューション内のプロジェクトを 1 度に 1 つずつビルドします。並行ビルドを有効にするには、"/m"  スイッチを追加してください。
2016/09/25 0:10:16 にビルドを開始しました。
ノード 1 上のプロジェクト "D:\dev\hello_premake\hello_premake.sln" (既定のターゲット)。
ValidateSolutionConfiguration:
  ソリューション構成 "Release|Win32" をビルドしています。
プロジェクト "D:\dev\hello_premake\hello_premake.sln" (1) は、ノード 1 上に "D:\dev\hello_premake\hello_premake.
vcxproj" (2) をビルドしています (既定のターゲット)。
PrepareForBuild:
  ディレクトリ "obj\Release\" を作成しています。
  ディレクトリ "bin\Release\" を作成しています。
  ディレクトリ "obj\Release\hello_premake.tlog\" を作成しています。
InitializeBuildStatus:
  "AlwaysCreate" が指定されたため "obj\Release\hello_premake.tlog\unsuccessfulbuild" を作成していま す。
ClCompile:
  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\CL.exe /c /Zi /nologo /W3 /WX- /Ox /Oi
   /Oy- /D NDEBUG /D _UNICODE /D UNICODE /GF /Gm- /EHsc /MD /GS /Gy /fp:precise /Zc:wchar_t /Zc:for
  Scope /Zc:inline /Fo"obj\Release\\" /Fd"obj\Release\vc140.pdb" /Gd /TC /analyze- /errorReport:que
  ue hello.c
  hello.c
Link:
  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\link.exe /ERRORREPORT:QUEUE /OUT:"bin\
  Release\hello_premake.exe" /INCREMENTAL:NO /NOLOGO kernel32.lib user32.lib gdi32.lib winspool.lib
   comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /M
  ANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /PDB:"bin\Release\hello
  _premake.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /TLBID:1 /ENTRY:"mainCRTStartup" /DYNAMICBASE
  /NXCOMPAT /IMPLIB:"bin\Release\hello_premake.lib" /MACHINE:X86 /SAFESEH obj\Release\hello.obj
  hello_premake.vcxproj -> D:\dev\hello_premake\bin\Release\hello_premake.exe
FinalizeBuildStatus:
  ファイル "obj\Release\hello_premake.tlog\unsuccessfulbuild" を削除しています。
  "obj\Release\hello_premake.tlog\hello_premake.lastbuildstate" のタッチ タスクを実行しています。
プロジェクト "D:\dev\hello_premake\hello_premake.vcxproj" (既定のターゲット) のビルドが完了しました 。

プロジェクト "D:\dev\hello_premake\hello_premake.sln" (既定のターゲット) のビルドが完了しました。


ビルドに成功しました。
    0 個の警告
    0 エラー
```

以下のbatファイルを作っておくとわりと便利。

```bat:premake5_vs2015.bat
premake5.exe vs2015
```

## ファイル名の命名規則
カレントのpremake5.luaがデフォルトの設定として使われる。なければpremake4.luaも検索される。
個別にファイルを指定するには"--file="コマンドラインを使う。

## luaのシンタックス上の注意
関数呼び出し時に、引数が文字列かテーブルのリテラルの場合のみ括弧を省略できる。

```lua
project "project_name"

files {
  "hello.c",
}
```

リテラルじゃない場合エラーになります

```lua
local name="project_name"

project name -- x
project(name) -- o
```

# 環境、種別を切り分ける概念

## action
どの種類のプロジェクトを生成しているか。premake5.exeのコマンドライン引数。

```
vs2010, vs2013, vs2015, gmake, etc...
```

action別に切り分けできる。

```lua
filter { "action:vs*" }
  -- vs用の設定

filter { "action:gmake" }
  -- gmake用の設定
```

## configuration
バリエーション。
よく使うのはこれ。

```lua
-- 定義して
configurations { "Debug", "Release" }

-- 設定する
filter "configurations:Debug"
do
    defines { "DEBUG", "_DEBUG" }
    flags { "Symbols" }
end
filter "configurations:Release"
do
    defines { "NDEBUG" }
    optimize "On"
end
```

以外にも

```
dll_debug32, static_debug32
```

等自由に定義できる。

## platform
バリエーションその２。

- https://github.com/premake/premake-core/wiki/Configurations-and-Platforms

> Unlike build configurations, platforms are completely optional. If you don't need them, just don't call the platforms function at all and the toolset's default behavior will be used.

 x86, x86_64, etc...

```lua
-- 定義して
platforms { "x86_64", "x86" }

-- 設定する
filter { "platforms:x86" }
   architecture "x86"
filter { "platforms:x86_64" }
   architecture "x86_64"
```

#実用的な雛形
筆者がいつも使っているものです。

* sln, vcxproj等の出力ディレクトリを_build_premakeに隔離
* 32bit, 64bitの２種類
* Debug, Releaseの２種類
* 成果物(exe, dll)の出力を_build_premake/Win64_Releaseという風に指定
* 出力ディレクトリを分けたのでライブラリ名に_dといったサフィックスは付けない

実際には、Win32をばっさり削ってます。
あと、プロジェクト定義は別々のファイルに分割してincludeかdofileで読み込みます(後述)。

```lua:premake5.lua
local build_dir="_build_premake"

-- premake5.lua
location(build_dir)

workspace "workspace"
do
    configurations { "Debug", "Release" }
    platforms { "Win32", "Win64" }
end

filter "configurations:Debug"
do
    defines { "DEBUG", "_DEBUG" }
    flags { "Symbols" }
end

filter "configurations:Release"
do
    defines { "NDEBUG" }
    optimize "On"
end

filter { "platforms:Win32" }
   architecture "x86"
filter {"platforms:Win32", "configurations:Debug" }
    targetdir(build_dir.."/Win32_Debug")
filter {"platforms:Win32", "configurations:Release" }
    targetdir(build_dir.."/Win32_Release")

filter { "platforms:Win64" }
   architecture "x64"
filter {"platforms:Win64", "configurations:Debug" }
    targetdir(build_dir.."/Win64_Debug")
filter {"platforms:Win64", "configurations:Release" }
    targetdir(build_dir.."/Win64_Release")

filter { "action:vs*" }
    buildoptions {
        "/wd4996",
    }
    defines {
        "_CRT_SECURE_NO_DEPRECATE",
        "NOMINMAX",
    }
    --characterset "Unicode"
    characterset "MBCS"

filter {} -- filter clear


project "project"
do
    --kind "ConsoleApp"
    --kind "WindowedApp"
    --kind "StaticLib"
    kind "SharedLib"
    --language "C"
    language "C++"

    objdir "%{prj.name}"

    flags{
        --"WinMain" -- with WindowedApp kind
        --"StaticRuntime",
    }
    files {
        "*.cpp",
        "*.h",
    }
    includedirs { 
    }
    defines { 
    }
    buildoptions { 
    }
    libdirs { 
    }
    links { 
    }

    postbuildcommands{
        "copy $(TargetDir)$(TargetName).dll ..",
    }
    filter { "configurations:Debug" }
    do
        postbuildcommands{
            "copy $(TargetDir)$(TargetName).pdb ..",
        }
    end
end
```

premakeでプロジェクト設定を管理してgitignoireに_build_premakeを設定し、slnやvcxprojを生成物と見なして
リビジョン管理から外してます。

# howto/tips
##プロジェクト定義を外部ファイルに分ける
dofileを使う。

```lua
dofile "subproject/premake5.lua"
```

省略形として

```lua
include "subproject"
```

## 変数展開
- https://github.com/premake/premake-core/wiki/Tokens

```lua
configurations { "debug", "release" }
platforms { "x86_64", "x86" }

    libdirs {
        "$(FBXSDK_DIR)/lib/%{cfg.action}/%{cfg.platform}/$(Configuration)",
    }
    -- $(FBXSDK_DIR)\lib\vs2015\x86_64\$(Configuration)と展開される
```

# 実例

##libpng

```
+ libpng
    + premake5.lua
    + libpng-1.6.16
        + premake5.lua
    + zlib-1.2.8
        + premake5.lua
```

```premake5.lua
-- premake5.lua
location "build"

solution "libpng"
do
    configurations { "Debug", "Release" }
    platforms { "Win32", "Win64" }
end

defines {
    "WIN32",
    "_WINDOWS",
    "_USRDLL",
}
buildoptions { "/wd4996" }

filter "configurations:Debug"
do
    defines { "DEBUG", "_DEBUG" }
    flags { "Symbols" }
end

filter "configurations:Release"
do
    defines { "NDEBUG" }
    optimize "On"
end

filter { "platforms:Win32" }
   architecture "x32"
filter { "platforms:Win64" }
   architecture "x64"

filter {"platforms:Win32", "configurations:Debug" }
    targetdir "build/Win32/Debug"
filter {"platforms:Win32", "configurations:Release" }
    targetdir "build/Win32/Release"
filter {"platforms:Win64", "configurations:Debug" }
    targetdir "build/Win64/Debug"
filter {"platforms:Win64", "configurations:Release" }
    targetdir "build/Win64/Release"

include "libpng-1.6.16"
include "zlib-1.2.8"
```

```lua:zlib-1.2.8/premake5.lua
project "zlib"
do
    kind "StaticLib"
    language "C"
    --language "C++"

    files { "*.h", "*.c" }
end
```

```lua:libpng-1.6.16/premake5.lua
project "libpng"
do
    kind "SharedLib"
    language "C"
    --language "C++"

    files { "*.h", "*.c" }
    -- 依存関係を記述できる(libファイル名か他のproject名を書ける)
    links { "zlib" }
end
```

とりあえずソースをダウンロードしてlibpng.dllを生成するべく定義してみた。zlibはあっさりとビルドできてしまったが、libpngの方はさすがに通らない。修正する。
libpng-1.6.16/projects/vstudio/libpng/libpng.vcxprojを参考にlibpng-1.6.16/premake5.luaを改造する。

```lua:libpng-1.6.16/premake5.lua
project "libpng"
do
    kind "SharedLib"
    language "C"
    --language "C++"

    files { 
        "png.c",
        "pngerror.c",
        "pngget.c",
        "pngmem.c",
        "pngpread.c",
        "pngread.c",
        "pngrio.c",
        "pngrtran.c",
        "pngrutil.c",
        "pngset.c",
        "pngtrans.c",
        "pngwio.c",
        "pngwrite.c",
        "pngwtran.c",
        "pngwutil.c",
    }
    includedirs {
        "../zlib-1.2.8",
    }
    -- 基準ディレクトリはvcxprojのあるところ
    prebuildcommands {
        "copy ..\\libpng-1.6.16\\scripts\\pnglibconf.h.prebuilt ..\\libpng-1.6.16\\pnglibconf.h",
    }
    links { "zlib" }
end
```

ビルド成功。
![ss.png](https://qiita-image-store.s3.amazonaws.com/0/59664/2e4ac698-6aa6-5b5f-16be-5c7e49588640.png)

### /MT(/MTD)の件

```lua:premake5.luaに追記
-- buildoptions { "/wd4996" }の次の行あたり
flags { "StaticRuntime" }
```

![static.png](https://qiita-image-store.s3.amazonaws.com/0/59664/0c5c9cdc-5cfd-fbbd-2564-fe6c9d2f9cfd.png)

MSVCR12D.DLLへの参照を消し去った。
こんな感じでプロジェクトを見通しのいい状態で管理できる。

## SDL2
- https://github.com/ousttrue/SDLpremake5

# ドキュメント
- https://github.com/premake/premake-core/wiki