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;
}
}
}