Files
BepInEx/build.cake
T
Dusk Banks ab5d8829e9 Normalize target triples (platform names)
The short commit hashes follow semver syntax now.
2022-08-14 17:18:57 +03:00

334 lines
13 KiB
C#

#nullable enable
#addin nuget:?package=Cake.FileHelpers&version=4.0.1
#addin nuget:?package=SharpZipLib&version=1.3.1
#addin nuget:?package=Cake.Compression&version=0.2.6
#addin nuget:?package=Cake.Json&version=6.0.1
#addin nuget:?package=Newtonsoft.Json&version=13.0.1
#addin nuget:?package=Cake.Http&version=1.3.0
var task = Argument("task", "Build");
var isBleedingEdge = Argument("bleeding_edge", false);
var buildId = Argument("build_id", 0);
var cleanDependencyCache = Argument("clean_build_cache", false);
var lastBuildCommit = Argument("last_build_commit", "");
var nugetPushSource = Argument("nuget_push_source", "https://nuget.bepinex.dev/v3/index.json");
var nugetPushKey = Argument("nuget_push_key", "");
var customBuildVersion = Argument("build_version", "");
var buildVersion = "";
var currentCommit = RunGit("rev-parse HEAD");
var currentCommitShort = RunGit("log -n 1 --pretty=\"format:%h\"").Trim();
var currentBranch = RunGit("rev-parse --abbrev-ref HEAD");
var latestTag = RunGit("describe --tags --abbrev=0");
string RunGit(string command, string separator = "")
{
using var process = StartAndReturnProcess("git", new() { Arguments = command, RedirectStandardOutput = true });
process.WaitForExit();
return string.Join(separator, process.GetStandardOutput());
}
readonly record struct Target(string Abi, string Os, string Cpu) {
public string Abi { get; } = Abi.Contains('-') ? throw new FormatException($"Target ABI cannot be hyphenated: {Abi}") : Abi;
public string Os { get; } = Os.Contains('-') ? throw new FormatException($"Target OS cannot be hyphenated: {Os}") : Os;
public string Cpu { get; } = Cpu.Contains('-') ? throw new FormatException($"Target CPU cannot be hyphenated: {Cpu}") : Cpu;
public override string ToString() => $"{Abi}-{Os}-{Cpu}";
public bool IsUnix() => Os is "unix";
}
var targets = new Target[] {
new("UnityMono", "windows", "x86"),
new("UnityMono", "windows", "x64"),
new("UnityMono", "unix", "any"),
new("UnityIL2CPP", "windows", "x86"),
new("UnityIL2CPP", "windows", "x64"),
new("NetLauncher", "windows", "any"),
};
const string DEP_CACHE_NAME = ".dep_cache";
Task("Cleanup")
.Does(() =>
{
Information("Removing old binaries");
CreateDirectory("./bin");
CleanDirectory("./bin", fi => {
var cachePath = fi.Path.FullPath.Contains(DEP_CACHE_NAME);
return !cachePath || cleanDependencyCache && cachePath;
});
Information("Cleaning up old build objects");
CleanDirectories(GetDirectories("./BepInEx.*/**/bin/"));
CleanDirectories(GetDirectories("./BepInEx.*/**/obj/"));
});
Task("Build")
.IsDependentOn("Cleanup")
.Does(() =>
{
var bepinExProperties = Directory("./BepInEx.Shared");
buildVersion = FindRegexMatchGroupInFile(bepinExProperties + File("BepInEx.Shared.projitems"), @"\<Version\>([0-9]+\.[0-9]+\.[0-9]+)\<\/Version\>", 1, System.Text.RegularExpressions.RegexOptions.None).Value;
var buildSettings = new DotNetBuildSettings {
Configuration = "Release",
MSBuildSettings = new() // Apparently needed in some versions of CakeBuild
};
if (isBleedingEdge)
{
buildSettings.MSBuildSettings.Properties["BuildInfo"] = new[] {
TransformText("BLEEDING EDGE Build #<%buildNumber%> from <%shortCommit%> at <%branchName%>")
.WithToken("buildNumber", buildId)
.WithToken("shortCommit", currentCommit)
.WithToken("branchName", currentBranch)
.ToString()
};
buildSettings.MSBuildSettings.Properties["AssemblyVersion"] = new[] { buildVersion + "." + buildId };
buildVersion += "-be." + buildId;
buildSettings.MSBuildSettings.Properties["Version"] = new[] { buildVersion };
} else if (!string.IsNullOrEmpty(customBuildVersion))
{
buildVersion = customBuildVersion;
buildSettings.MSBuildSettings.Properties["Version"] = new[] { buildVersion };
}
foreach (var file in GetFiles("./BepInEx.*/*.csproj"))
{
DotNetBuild(file.FullPath, buildSettings);
}
});
const string DOORSTOP_VER = "4.0.0-alpha.1";
const string MONO_VER = "2021.6.24";
const string DOORSTOP_PROXY_DLL = "winhttp.dll";
var depCachePath = Directory($"./bin/{DEP_CACHE_NAME}");
var doorstopPath = depCachePath + Directory("doorstop");
var monoPath = depCachePath + Directory("mono");
Task("DownloadDependencies")
.Does(() =>
{
var cacheFile = depCachePath + File("cache.json");
var cache = FileExists(cacheFile) ? DeserializeJsonFromFile<Dictionary<string, string>>(cacheFile) : new Dictionary<string, string>();
bool NeedsRedownload(string repo, string curVer)
{
if (!cache.TryGetValue(repo, out var ver) || ver != curVer)
{
cache[repo] = curVer;
return true;
}
return false;
}
CreateDirectory(doorstopPath);
CreateDirectory(monoPath);
if (NeedsRedownload("NeighTools/UnityDoorstop", DOORSTOP_VER))
{
Information("Updating Doorstop");
var doorstopZipPathWin = doorstopPath + File("doorstop_win.zip");
var doorstopZipPathLinux = doorstopPath + File("doorstop_linux.zip");
var doorstopZipPathMacOs = doorstopPath + File("doorstop_macos.zip");
// TODO: Fix URLs once Doorstop 4 is released
DownloadFile($"https://nightly.link/NeighTools/UnityDoorstop/workflows/build-be/wip-rewrite/Doorstop_WIN-RELEASE.zip", doorstopZipPathWin);
DownloadFile($"https://nightly.link/NeighTools/UnityDoorstop/workflows/build-be/wip-rewrite/Doorstop_LINUX-RELEASE.zip", doorstopZipPathLinux);
DownloadFile($"https://nightly.link/NeighTools/UnityDoorstop/workflows/build-be/wip-rewrite/Doorstop_MACOS-RELEASE.zip", doorstopZipPathMacOs);
ZipUncompress(doorstopZipPathWin, doorstopPath + Directory("windows"));
ZipUncompress(doorstopZipPathLinux, doorstopPath + Directory("unix"));
ZipUncompress(doorstopZipPathMacOs, doorstopPath + Directory("unix"));
}
if (NeedsRedownload("BepInEx/mono", MONO_VER))
{
// TODO: Instead download dotnet + BCL
Information("Updating Mono");
var monoX64Path = doorstopPath + File("mono_x64.zip");
var monoX86Path = doorstopPath + File("mono_x86.zip");
DownloadFile($"https://github.com/BepInEx/mono/releases/download/{MONO_VER}/mono-x64.zip", monoX64Path);
DownloadFile($"https://github.com/BepInEx/mono/releases/download/{MONO_VER}/mono-x86.zip", monoX86Path);
ZipUncompress(monoX64Path, monoPath + Directory("x64"));
ZipUncompress(monoX86Path, monoPath + Directory("x86"));
}
SerializeJsonToFile(cacheFile, cache);
});
Task("PushNuGet")
.IsDependentOn("Build")
.Does(() =>
{
if (string.IsNullOrEmpty(nugetPushSource))
{
Information("NuGet push source is missing");
return;
}
if (string.IsNullOrEmpty(nugetPushKey))
{
Information("NuGet push key is missing");
return;
}
foreach (var file in GetFiles("./bin/NuGet/*.nupkg"))
{
Information($"Pushing {file}");
DotNetNuGetPush(file.FullPath, new() {
Source = nugetPushSource,
ApiKey = nugetPushKey,
});
}
});
Task("MakeDist")
.IsDependentOn("Build")
.IsDependentOn("DownloadDependencies")
.Does(() =>
{
var distDir = Directory("./bin/dist");
CreateDirectory(distDir);
var changelog = TransformText("<%commit_count%> commits since <%last_tag%>\r\n\r\nChangelog (excluding merges):\r\n<%commit_log%>")
.WithToken("commit_count", RunGit($"rev-list --count {latestTag}..HEAD"))
.WithToken("last_tag", latestTag)
.WithToken("commit_log", RunGit($"--no-pager log --no-merges --pretty=\"format:* (%h) [%an] %s\" {latestTag}..HEAD", "\r\n"))
.ToString();
void PackageBepin(Target target, string originDir, string? doorstopConfigFile = null, bool copyMono = false)
{
Information($"Creating distributions for target \"{target}\"...");
string? doorstopTargetPath = null;
ConvertableDirectoryPath? doorstopTargetDir = null;
if (doorstopConfigFile is not null)
{
doorstopTargetDir = doorstopPath + Directory(target.Os) + Directory(target.IsUnix() ? "libdoorstop" : target.Cpu);
doorstopTargetPath = doorstopTargetDir + File(target.IsUnix() ? "*.*" : DOORSTOP_PROXY_DLL);
}
var distTargetDir = distDir + Directory($"{target}");
var distBepinDir = distTargetDir + Directory("BepInEx");
var distDoorstopDir = distTargetDir;
if (target.IsUnix()) distDoorstopDir += Directory("doorstop_libs");
CreateDirectory(distTargetDir);
CreateDirectory(distDoorstopDir);
CreateDirectory(distBepinDir + Directory("core"));
CreateDirectory(distBepinDir + Directory("plugins"));
CreateDirectory(distBepinDir + Directory("patchers"));
if (doorstopConfigFile is not null)
{
CopyFile("./doorstop/" + doorstopConfigFile, Directory(distTargetDir) + File(target.IsUnix() ? "run_bepinex.sh" : "doorstop_config.ini"));
if (target.IsUnix())
{
CopyFiles(doorstopTargetPath, distDoorstopDir);
MoveFile(distDoorstopDir + File(".doorstop_version"), distTargetDir + File(".doorstop_version"));
}
else
{
CopyFile(doorstopTargetPath, Directory(distDoorstopDir) + File(DOORSTOP_PROXY_DLL));
CopyFile(doorstopTargetDir + File(".doorstop_version"), distDoorstopDir + File(".doorstop_version"));
}
if (target.IsUnix())
{
ReplaceTextInFiles($"{distTargetDir}/run_bepinex.sh", "\r\n", "\n");
}
if (copyMono)
{
CopyDirectory(monoPath + Directory(target.Cpu) + Directory("mono"), Directory(distTargetDir) + Directory("mono"));
}
}
CopyFiles($"./bin/{originDir}/*.*", Directory(distBepinDir) + Directory("core"));
FileWriteText(distTargetDir + File("changelog.txt"), changelog);
if (target.Abi is "NetLauncher")
{
DeleteFile(Directory(distBepinDir) + Directory("core") + File("BepInEx.NetLauncher.exe.config"));
MoveFiles((string)(Directory(distBepinDir) + Directory("core") + File("BepInEx.NetLauncher.*")), Directory(distTargetDir));
}
}
foreach (Target target in targets) {
(string originDir, string? doorstopConfigFile, bool copyMono) = (target.Abi, null, false);
if (target.Abi is var abi and ("UnityIL2CPP" or "UnityMono")) {
originDir = abi is "UnityIL2CPP" ? "IL2CPP" : "Unity";
doorstopConfigFile = target.IsUnix() ? "run_bepinex.sh" :
abi is "UnityIL2CPP" ? "doorstop_config_il2cpp.ini" : "doorstop_config_mono.ini";
}
PackageBepin(target, originDir, doorstopConfigFile, copyMono);
}
});
Task("Pack")
.IsDependentOn("MakeDist")
.IsDependentOn("PushNuGet")
.Does(() =>
{
var distDir = Directory("./bin/dist");
var commitSuffix = isBleedingEdge ? $"+{currentCommitShort}" : "";
Information("Packing BepInEx");
foreach (Target target in targets) {
ZipCompress(distDir + Directory($"{target}"), distDir + File($"BepInEx-{target}-{buildVersion}{commitSuffix}.zip"));
}
if(isBleedingEdge)
{
var changelog = "";
if (!string.IsNullOrEmpty(lastBuildCommit)) {
changelog = TransformText("<ul><%changelog%></ul>")
.WithToken("changelog", RunGit($"--no-pager log --no-merges --pretty=\"format:<li>(<code>%h</code>) [%an] %s</li>\" {lastBuildCommit}..HEAD"))
.ToString();
}
FileWriteText(distDir + File("info.json"),
SerializeJsonPretty(new Dictionary<string, object> {
["id"] = buildId.ToString(),
["date"] = DateTime.Now.ToString("o"),
["changelog"] = changelog,
["hash"] = currentCommit,
["short_hash"] = currentCommitShort,
["artifacts"] = targets.Select(target => {
(string abiName, string abiGamesPrefix) = target.Abi switch {
"NetLauncher" => (".NET Launcher", ".NET Framework/XNA "),
"UnityIL2CPP" => ("Unity IL2CPP", ""),
"UnityMono" => ("Unity Mono", ""),
var abi => (abi, $"{abi} "),
};
(string osName, string osGamesSuffix) = target.Os switch {
"unix" => ("Linux & macOS", " using GCC"),
"windows" => ("Windows", ""),
var os => (os, ""),
};
string cpuOsNameSuffix = target.Cpu switch {
"any" => "",
var cpu => $" {cpu}",
};
return new Dictionary<string, object>() {
["file"] = $"BepInEx-{target}-{buildVersion}{commitSuffix}.zip",
["description"] = $"BepInEx {abiName} for {osName}{cpuOsNameSuffix} {abiGamesPrefix}games{osGamesSuffix}",
};
}).ToArray(),
}));
}
});
RunTarget(task);