ServerPackaging.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. using System.Diagnostics;
  2. using System.IO.Compression;
  3. using Robust.Packaging;
  4. using Robust.Packaging.AssetProcessing;
  5. using Robust.Packaging.AssetProcessing.Passes;
  6. using Robust.Packaging.Utility;
  7. using Robust.Shared.Timing;
  8. namespace Content.Packaging;
  9. public static class ServerPackaging
  10. {
  11. private static readonly List<PlatformReg> Platforms = new()
  12. {
  13. new PlatformReg("win-x64", "Windows", true),
  14. new PlatformReg("win-arm64", "Windows", true),
  15. new PlatformReg("linux-x64", "Linux", true),
  16. new PlatformReg("linux-arm64", "Linux", true),
  17. new PlatformReg("osx-x64", "MacOS", true),
  18. new PlatformReg("osx-arm64", "MacOS", true),
  19. // Non-default platforms (i.e. for Watchdog Git)
  20. new PlatformReg("win-x86", "Windows", false),
  21. new PlatformReg("linux-x86", "Linux", false),
  22. new PlatformReg("linux-arm", "Linux", false),
  23. new PlatformReg("freebsd-x64", "FreeBSD", false),
  24. };
  25. private static List<string> PlatformRids => Platforms
  26. .Select(o => o.Rid)
  27. .ToList();
  28. private static List<string> PlatformRidsDefault => Platforms
  29. .Where(o => o.BuildByDefault)
  30. .Select(o => o.Rid)
  31. .ToList();
  32. private static readonly List<string> ServerContentAssemblies = new()
  33. {
  34. "Content.Server.Database",
  35. "Content.Server",
  36. "Content.Shared",
  37. "Content.Shared.Database",
  38. };
  39. private static readonly List<string> ServerExtraAssemblies = new()
  40. {
  41. // Python script had Npgsql. though we want Npgsql.dll as well soooo
  42. "Npgsql",
  43. "Microsoft",
  44. };
  45. private static readonly List<string> ServerNotExtraAssemblies = new()
  46. {
  47. "Microsoft.CodeAnalysis",
  48. };
  49. private static readonly HashSet<string> BinSkipFolders = new()
  50. {
  51. // Roslyn localization files, screw em.
  52. "cs",
  53. "de",
  54. "es",
  55. "fr",
  56. "it",
  57. "ja",
  58. "ko",
  59. "pl",
  60. "pt-BR",
  61. "ru",
  62. "tr",
  63. "zh-Hans",
  64. "zh-Hant"
  65. };
  66. public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageLogger logger, string configuration, List<string>? platforms = null)
  67. {
  68. if (platforms == null)
  69. {
  70. platforms ??= PlatformRidsDefault;
  71. }
  72. if (hybridAcz)
  73. {
  74. // Hybrid ACZ involves a file "Content.Client.zip" in the server executable directory.
  75. // Rather than hosting the client ZIP on the watchdog or on a separate server,
  76. // Hybrid ACZ uses the ACZ hosting functionality to host it as part of the status host,
  77. // which means that features such as automatic UPnP forwarding still work properly.
  78. await ClientPackaging.PackageClient(skipBuild, configuration, logger);
  79. }
  80. // Good variable naming right here.
  81. foreach (var platform in Platforms)
  82. {
  83. if (!platforms.Contains(platform.Rid))
  84. continue;
  85. await BuildPlatform(platform, skipBuild, hybridAcz, configuration, logger);
  86. }
  87. }
  88. private static async Task BuildPlatform(PlatformReg platform, bool skipBuild, bool hybridAcz, string configuration, IPackageLogger logger)
  89. {
  90. logger.Info($"Building project for {platform.TargetOs}...");
  91. if (!skipBuild)
  92. {
  93. await ProcessHelpers.RunCheck(new ProcessStartInfo
  94. {
  95. FileName = "dotnet",
  96. ArgumentList =
  97. {
  98. "build",
  99. Path.Combine("Content.Server", "Content.Server.csproj"),
  100. "-c", configuration,
  101. "--nologo",
  102. "/v:m",
  103. $"/p:TargetOs={platform.TargetOs}",
  104. "/t:Rebuild",
  105. "/p:FullRelease=true",
  106. "/m"
  107. }
  108. });
  109. await PublishClientServer(platform.Rid, platform.TargetOs, configuration);
  110. }
  111. logger.Info($"Packaging {platform.Rid} server...");
  112. var sw = RStopwatch.StartNew();
  113. {
  114. await using var zipFile =
  115. File.Open(Path.Combine("release", $"SS14.Server_{platform.Rid}.zip"), FileMode.Create, FileAccess.ReadWrite);
  116. using var zip = new ZipArchive(zipFile, ZipArchiveMode.Update);
  117. var writer = new AssetPassZipWriter(zip);
  118. await WriteServerResources(platform, "", writer, logger, hybridAcz, default);
  119. await writer.FinishedTask;
  120. }
  121. logger.Info($"Finished packaging server in {sw.Elapsed}");
  122. }
  123. private static async Task PublishClientServer(string runtime, string targetOs, string configuration)
  124. {
  125. await ProcessHelpers.RunCheck(new ProcessStartInfo
  126. {
  127. FileName = "dotnet",
  128. ArgumentList =
  129. {
  130. "publish",
  131. "--runtime", runtime,
  132. "--no-self-contained",
  133. "-c", configuration,
  134. $"/p:TargetOs={targetOs}",
  135. "/p:FullRelease=True",
  136. "/m",
  137. "RobustToolbox/Robust.Server/Robust.Server.csproj"
  138. }
  139. });
  140. }
  141. private static async Task WriteServerResources(
  142. PlatformReg platform,
  143. string contentDir,
  144. AssetPass pass,
  145. IPackageLogger logger,
  146. bool hybridAcz,
  147. CancellationToken cancel)
  148. {
  149. var graph = new RobustServerAssetGraph();
  150. var passes = graph.AllPasses.ToList();
  151. pass.Dependencies.Add(new AssetPassDependency(graph.Output.Name));
  152. passes.Add(pass);
  153. AssetGraph.CalculateGraph(passes, logger);
  154. var inputPassCore = graph.InputCore;
  155. var inputPassResources = graph.InputResources;
  156. var contentAssemblies = new List<string>(ServerContentAssemblies);
  157. // Additional assemblies that need to be copied such as EFCore.
  158. var sourcePath = Path.Combine(contentDir, "bin", "Content.Server");
  159. // Should this be an asset pass?
  160. // For future archaeologists I just want audio rework to work and need the audio pass so
  161. // just porting this as is from python.
  162. foreach (var fullPath in Directory.EnumerateFiles(sourcePath, "*.*", SearchOption.AllDirectories))
  163. {
  164. var fileName = Path.GetFileNameWithoutExtension(fullPath);
  165. if (!ServerNotExtraAssemblies.Any(o => fileName.StartsWith(o)) && ServerExtraAssemblies.Any(o => fileName.StartsWith(o)))
  166. {
  167. contentAssemblies.Add(fileName);
  168. }
  169. }
  170. await RobustSharedPackaging.DoResourceCopy(
  171. Path.Combine("RobustToolbox", "bin", "Server",
  172. platform.Rid,
  173. "publish"),
  174. inputPassCore,
  175. BinSkipFolders,
  176. cancel: cancel);
  177. await RobustSharedPackaging.WriteContentAssemblies(
  178. inputPassResources,
  179. contentDir,
  180. "Content.Server",
  181. contentAssemblies,
  182. cancel: cancel);
  183. await RobustServerPackaging.WriteServerResources(contentDir, inputPassResources, cancel);
  184. if (hybridAcz)
  185. {
  186. inputPassCore.InjectFileFromDisk("Content.Client.zip", Path.Combine("release", "SS14.Client.zip"));
  187. }
  188. inputPassCore.InjectFinished();
  189. inputPassResources.InjectFinished();
  190. }
  191. private readonly record struct PlatformReg(string Rid, string TargetOs, bool BuildByDefault);
  192. }