forked from Unity-Technologies/UnityDataTools
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBuildReport.cs
More file actions
213 lines (186 loc) · 7.96 KB
/
Copy pathBuildReport.cs
File metadata and controls
213 lines (186 loc) · 7.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
using System;
using System.Collections.Generic;
using System.IO;
using UnityDataTools.BinaryFormat;
using UnityDataTools.FileSystem.TypeTreeReaders;
namespace UnityDataTools.Analyzer.SerializedObjects;
public class BuildReport
{
public string Name { get; init; }
public string BuildGuid { get; init; }
public string PlatformName { get; init; }
public int Subtarget { get; init; }
public string StartTime { get; init; }
public string EndTime { get; init; }
public int Options { get; init; }
public int AssetBundleOptions { get; init; }
public string OutputPath { get; init; }
public uint Crc { get; init; }
public ulong TotalSize { get; init; }
public int TotalTimeSeconds { get; init; }
public int TotalErrors { get; init; }
public int TotalWarnings { get; init; }
public int BuildType { get; init; }
public string BuildResult { get; init; }
public List<BuildFile> Files { get; init; }
public FileListAssetBundleHelper fileListAssetBundleHelper { get; init; }
private BuildReport() { }
public static BuildReport Read(RandomAccessReader reader)
{
var summary = reader["m_Summary"];
// Read the GUID (4 unsigned ints)
// Unity's GUID format reverses nibbles within each uint32
var guidData = summary["buildGUID"];
var guid0 = guidData["data[0]"].GetValue<uint>();
var guid1 = guidData["data[1]"].GetValue<uint>();
var guid2 = guidData["data[2]"].GetValue<uint>();
var guid3 = guidData["data[3]"].GetValue<uint>();
var guidString = GuidHelper.FormatUnityGuid(guid0, guid1, guid2, guid3);
// Convert build start time from ticks to ISO 8601 UTC format
var startTimeTicks = summary["buildStartTime"]["ticks"].GetValue<long>();
var startTime = new DateTime(startTimeTicks, DateTimeKind.Utc).ToString("o");
// Convert ticks to seconds (TimeSpan.TicksPerSecond = 10,000,000)
var totalTimeTicks = summary["totalTimeTicks"].GetValue<ulong>();
var totalTimeSeconds = (int)Math.Round(totalTimeTicks / 10000000.0);
var endTime = new DateTime(startTimeTicks + (long)totalTimeTicks, DateTimeKind.Utc).ToString("o");
// Read the files array
var filesList = new List<BuildFile>(reader["m_Files"].GetArraySize());
foreach (var fileElement in reader["m_Files"])
{
filesList.Add(new BuildFile
{
Id = fileElement["id"].GetValue<uint>(),
Path = fileElement["path"].GetValue<string>(),
Role = fileElement["role"].GetValue<string>(),
Size = fileElement["totalSize"].GetValue<ulong>()
});
}
TrimCommonPathPrefix(filesList);
return new BuildReport()
{
Name = reader["m_Name"].GetValue<string>(),
BuildGuid = guidString,
PlatformName = summary["platformName"].GetValue<string>(),
Subtarget = summary["subtarget"].GetValue<int>(),
StartTime = startTime,
EndTime = endTime,
Options = summary["options"].GetValue<int>(),
AssetBundleOptions = summary["assetBundleOptions"].GetValue<int>(),
OutputPath = summary["outputPath"].GetValue<string>(),
Crc = summary["crc"].GetValue<uint>(),
TotalSize = summary["totalSize"].GetValue<ulong>(),
TotalTimeSeconds = totalTimeSeconds,
TotalErrors = summary["totalErrors"].GetValue<int>(),
TotalWarnings = summary["totalWarnings"].GetValue<int>(),
BuildType = summary["buildType"].GetValue<int>(),
BuildResult = GetBuildResultString(summary["buildResult"].GetValue<int>()),
Files = filesList,
fileListAssetBundleHelper = new FileListAssetBundleHelper(filesList)
};
}
// Currently the file list has the absolute paths of the build output, but what we really want is the relative path.
// This code reuses the approach taken in the build report inspector of automatically stripping off whatever part of the path
// is repeated as the prefix on each file, which effectively finds the relative output path.
static void TrimCommonPathPrefix(List<BuildFile> files)
{
if (files.Count == 0)
return;
string longestCommonRoot = files[0].Path;
foreach (var file in files)
{
for (var i = 0; i < longestCommonRoot.Length && i < file.Path.Length; i++)
{
if (longestCommonRoot[i] == file.Path[i])
continue;
longestCommonRoot = longestCommonRoot.Substring(0, i);
}
}
foreach (var file in files)
{
file.Path = file.Path.Substring(longestCommonRoot.Length);
}
}
public static string GetBuildTypeString(int buildType)
{
return buildType switch
{
1 => "Player",
2 => "AssetBundle",
3 => "ContentDirectory",
_ => buildType.ToString()
};
}
public static string GetBuildResultString(int buildResult)
{
return buildResult switch
{
0 => "Unknown",
1 => "Succeeded",
2 => "Failed",
3 => "Cancelled",
4 => "Pending",
_ => buildResult.ToString()
};
}
}
public class BuildFile
{
public uint Id { get; init; }
public string Path { get; set; }
public string Role { get; init; }
public ulong Size { get; init; }
}
/// Helper for matching files that are inside an Unity Archive file to the file containing it.
// Currently this only applies to AssetBundle builds, which can have many output files and which use hard to understand internal file names
// like "CAB-76a378bdc9304bd3c3a82de8dd97981a".
// For compressed Player builds the PackedAssets reports the internal files, but the file list does not report the unity3d content,
// so this code will not pick up the mapping. However because there is only a single unity3d file on most platforms this is less important
public class FileListAssetBundleHelper
{
public Dictionary<string, string> internalNameToArchiveMapping = new Dictionary<string, string>();
public FileListAssetBundleHelper(List<BuildFile> files)
{
CalculateAssetBundleMapping(files);
}
/// <summary>
// Map between the internal file names inside Archive files back to the Archive filename.
/*
Example input:
- path: C:/Src/TestProject/Build/AssetBundles/audio.bundle/CAB-76a378bdc9304bd3c3a82de8dd97981a.resource
role: StreamingResourceFile
...
- path: C:/Src/TestProject/Build/AssetBundles/audio.bundle
role: AssetBundle
...
Result:
CAB-76a378bdc9304bd3c3a82de8dd97981a.resource -> audio.bundle
*/
/// </summary>
private void CalculateAssetBundleMapping(List<BuildFile> files)
{
internalNameToArchiveMapping.Clear();
// Track archive paths and their base filenames for AssetBundle or manifest files
var archivePathToFileName = new Dictionary<string, string>();
foreach (var file in files)
{
if (file.Role == "AssetBundle" || file.Role == "ManifestAssetBundle")
{
var justFileName = Path.GetFileName(file.Path);
archivePathToFileName[file.Path] = justFileName;
}
}
if (archivePathToFileName.Count == 0)
return;
// Map internal file names to their corresponding archive filenames
foreach (var file in files)
{
// Assumes internal files are not in subdirectories inside the archive
var justPath = Path.GetDirectoryName(file.Path)?.Replace('\\', '/');
var justFileName = Path.GetFileName(file.Path);
if (!string.IsNullOrEmpty(justPath) && archivePathToFileName.ContainsKey(justPath))
{
internalNameToArchiveMapping[justFileName] = archivePathToFileName[justPath];
}
}
}
}