using System; using System.Collections.Generic; using System.Text.RegularExpressions; using Newtonsoft.Json; using Unity.Pipeline.Commands; namespace Unity.Pipeline.Editor.Commands.Build { /// /// One entry of list_build_targets (CLI-204): a BuildTarget enum value with the /// information an agent needs to choose a valid target without guessing — its group and whether the /// platform's build support is actually installed in this editor. /// [Serializable] public class BuildTargetInfo { /// BuildTarget enum name, e.g. "StandaloneWindows64". [JsonProperty("name")] public string Name { get; set; } /// Friendly label, e.g. "Windows 64-bit". [JsonProperty("displayName")] public string DisplayName { get; set; } /// BuildTargetGroup enum name, e.g. "Standalone". [JsonProperty("targetGroup")] public string TargetGroup { get; set; } /// BuildPipeline.IsBuildTargetSupported(group, target) — module installed and usable. [JsonProperty("isInstalled")] public bool IsInstalled { get; set; } } /// One scene in the Build Settings scene list, projected for get_build_settings. [Serializable] public class BuildSceneEntry { [JsonProperty("path")] public string Path { get; set; } [JsonProperty("guid")] public string Guid { get; set; } [JsonProperty("enabled")] public bool Enabled { get; set; } } /// /// Result of get_build_settings (CLI-204): the current build configuration read from /// EditorUserBuildSettings / EditorBuildSettings plus the active target's IL2CPP code /// generation mode. Scene-list management lives in add_scene_to_build / /// remove_scene_from_build (CLI-189); this only reports the list. /// [Serializable] public class BuildSettingsResult { [JsonProperty("activeBuildTarget")] public string ActiveBuildTarget { get; set; } [JsonProperty("activeBuildTargetGroup")] public string ActiveBuildTargetGroup { get; set; } [JsonProperty("developmentBuild")] public bool DevelopmentBuild { get; set; } [JsonProperty("allowDebugging")] public bool AllowDebugging { get; set; } [JsonProperty("connectWithProfiler")] public bool ConnectWithProfiler { get; set; } [JsonProperty("buildScriptsOnly")] public bool BuildScriptsOnly { get; set; } [JsonProperty("symlinkSources")] public bool SymlinkSources { get; set; } /// "OptimizeSpeed" | "OptimizeSize" for the active build target. [JsonProperty("il2CppCodeGeneration")] public string Il2CppCodeGeneration { get; set; } [JsonProperty("scenes")] public List Scenes { get; set; } } /// /// Mutable build-settings fields for set_build_settings (CLI-204). Every field is nullable; /// only the ones the caller supplies are changed. Note that and /// only take effect when is also on. /// public class SetBuildSettingsInput : IStructuredCommandInput { [CliArg("developmentBuild", "Build a Development Player (enables the debugger/profiler).")] public bool? DevelopmentBuild { get; set; } [CliArg("allowDebugging", "Allow script debugging (only effective with developmentBuild=true).")] public bool? AllowDebugging { get; set; } [CliArg("connectWithProfiler", "Auto-connect the Profiler (only effective with developmentBuild=true).")] public bool? ConnectWithProfiler { get; set; } [CliArg("buildScriptsOnly", "Build only the scripts (skip data) for faster iteration.")] public bool? BuildScriptsOnly { get; set; } [CliArg("symlinkSources", "Symlink runtime/plugin sources instead of copying (where supported).")] public bool? SymlinkSources { get; set; } [CliArg("il2CppCodeGeneration", "IL2CPP code generation for the active target: OptimizeSpeed | OptimizeSize.")] public string Il2CppCodeGeneration { get; set; } } /// /// Result of set_build_settings (CLI-204): the fields that were actually changed /// () versus the supplied fields that already had the requested value /// (). A dry run reports the same set it would /// write, without changing anything. /// [Serializable] public class SetBuildSettingsResult { [JsonProperty("success")] public bool Success { get; set; } [JsonProperty("dryRun")] public bool DryRun { get; set; } /// Field name → new value, for each field that changed (or would change on a dry run). [JsonProperty("applied")] public Dictionary Applied { get; set; } = new Dictionary(); /// Field name → current value, for supplied fields that already matched (no-ops). [JsonProperty("skipped")] public Dictionary Skipped { get; set; } = new Dictionary(); [JsonProperty("message")] public string Message { get; set; } public static SetBuildSettingsResult Fail(string message) => new SetBuildSettingsResult { Success = false, Message = message }; } /// One Build Profile asset, projected for list_build_profiles (Unity 6 only). [Serializable] public class BuildProfileInfo { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("guid")] public string Guid { get; set; } /// The profile's build target name (best-effort; may be empty if unreadable). [JsonProperty("platform")] public string Platform { get; set; } [JsonProperty("isActive")] public bool IsActive { get; set; } } /// One output file produced by the build (a row of BuildReport.GetFiles()). [Serializable] public class BuildFileEntry { [JsonProperty("path")] public string Path { get; set; } /// BuildFile.role verbatim, e.g. "MainData", "AssetBundle", "BootConfig". [JsonProperty("role")] public string Role { get; set; } [JsonProperty("sizeBytes")] public long SizeBytes { get; set; } } /// One asset packed into a build bundle/file (a row of a PackedAssets entry). [Serializable] public class PackedAssetContent { [JsonProperty("assetPath")] public string AssetPath { get; set; } [JsonProperty("type")] public string Type { get; set; } [JsonProperty("guid")] public string Guid { get; set; } [JsonProperty("sizeBytes")] public long SizeBytes { get; set; } } /// A packed bundle/file with its per-asset size breakdown (requires DetailedBuildReport). [Serializable] public class PackedAssetEntry { [JsonProperty("bundlePath")] public string BundlePath { get; set; } [JsonProperty("overheadBytes")] public long OverheadBytes { get; set; } [JsonProperty("contents")] public List Contents { get; set; } } /// One message emitted during a build step. [Serializable] public class BuildStepMessageEntry { /// "Log" | "Warning" | "Error" (mapped from LogType). [JsonProperty("type")] public string Type { get; set; } [JsonProperty("content")] public string Content { get; set; } } /// One step of the build (a row of BuildReport.steps). [Serializable] public class BuildStepEntry { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("durationMs")] public long DurationMs { get; set; } [JsonProperty("depth")] public int Depth { get; set; } [JsonProperty("messages")] public List Messages { get; set; } } /// A build error/warning surfaced in build_status, with best-effort source file. [Serializable] public class BuildIssue { [JsonProperty("message")] public string Message { get; set; } /// Source file when the message carries a "File.cs(line,col): ..." prefix; omitted otherwise. [JsonProperty("file", NullValueHandling = NullValueHandling.Ignore)] public string File { get; set; } /// Build a , reusing for file extraction. public static BuildIssue From(string content) { var parsed = BuildMessage.Parse("info", content); return new BuildIssue { Message = parsed.Message, File = string.IsNullOrEmpty(parsed.File) ? null : parsed.File }; } } /// /// Full build_status result for a finished build (CLI-204) — a JSON-friendly projection of /// UnityEditor.Build.Reporting.BuildReport. Optional collections are omitted when null /// (), so a failed build can carry errors/steps without empty /// file/asset arrays. Transient states (queued/building/idle/dry_run) are reported as small, /// purpose-built payloads, not this type. /// [Serializable] public class BuildReportResult { [JsonProperty("status")] public string Status { get; set; } [JsonProperty("buildId")] public string BuildId { get; set; } /// "Succeeded" | "Failed" | "Cancelled" | "Unknown". [JsonProperty("result")] public string Result { get; set; } [JsonProperty("platform")] public string Platform { get; set; } [JsonProperty("outputPath")] public string OutputPath { get; set; } [JsonProperty("totalSizeBytes")] public long TotalSizeBytes { get; set; } [JsonProperty("buildTimeMs")] public long BuildTimeMs { get; set; } [JsonProperty("buildStartedAt", NullValueHandling = NullValueHandling.Ignore)] public string BuildStartedAt { get; set; } [JsonProperty("buildEndedAt", NullValueHandling = NullValueHandling.Ignore)] public string BuildEndedAt { get; set; } [JsonProperty("totalWarnings")] public int TotalWarnings { get; set; } [JsonProperty("totalErrors")] public int TotalErrors { get; set; } [JsonProperty("files", NullValueHandling = NullValueHandling.Ignore)] public List Files { get; set; } [JsonProperty("packedAssets", NullValueHandling = NullValueHandling.Ignore)] public List PackedAssets { get; set; } [JsonProperty("buildSteps", NullValueHandling = NullValueHandling.Ignore)] public List BuildSteps { get; set; } [JsonProperty("errors")] public List Errors { get; set; } = new List(); [JsonProperty("warnings")] public List Warnings { get; set; } = new List(); } /// /// A single build error/warning with best-effort file/line parsed from the message content. /// Retained from the original build stub (CLI-127): it is the shared parser used to extract a /// source location from a raw build-step message and is exercised directly by the test suite. /// [Serializable] public class BuildMessage { [JsonProperty("severity")] public string Severity { get; set; } [JsonProperty("file")] public string File { get; set; } [JsonProperty("line")] public int Line { get; set; } [JsonProperty("message")] public string Message { get; set; } // Matches the standard compiler/build message prefix "Path/To/File.cs(line,col): rest". static readonly Regex s_LocationPattern = new Regex(@"^(?.*?)\((?\d+),\d+\):\s*(?.*)$", RegexOptions.Singleline); /// /// Build a from a raw build-step message, extracting file/line when /// the content carries the standard "File.cs(line,col): ..." prefix; otherwise the whole content /// is kept as the message with no file/line. /// public static BuildMessage Parse(string severity, string content) { var msg = new BuildMessage { Severity = severity, Message = content ?? string.Empty }; if (!string.IsNullOrEmpty(content)) { var match = s_LocationPattern.Match(content); if (match.Success) { msg.File = match.Groups["file"].Value.Trim(); if (int.TryParse(match.Groups["line"].Value, out var line)) msg.Line = line; msg.Message = match.Groups["rest"].Value.Trim(); } } return msg; } } }