From ae03cf20f68def3cc57c139368df6ff3123aec3f Mon Sep 17 00:00:00 2001 From: Andrew Skowronski Date: Mon, 21 Sep 2026 13:18:34 -0400 Subject: [PATCH] [#131] Support ContentLayout v3 (Unity 6.7) with v2 backward compatibility The Unity 6.7 ContentLayout.json schema (v3) renames ID to StableId (no .cfid extension), replaces ContentHash with ArtifactIndex, references loadables by index instead of ObjectIdHash, and drops the loadable AssetPath/OutputLFID fields (no more remapping into clusters). UnityDataTools.Models.ContentLayout now tracks v3; the v2 schema is preserved as a reference definition in UnityDataTools.Models.V2. Analyze accepts both versions: v2 files are upgraded in memory to the v3 model (ContentLayoutV2Upgrader), so a single write path and a single database schema serve both. The content_layout tables move to the v3 shape (loadable_index key, stable_id, artifact_index, is_root_asset holding the 1-based root position); the v2-only source data (asset_path, source_lfid) lands in extra columns that exist only in databases imported from a v2 layout. PRAGMA user_version bumped to 8. The previous v2 layout of the LeadingEdge build is archived at TestCommon/Data/ContentLayoutVersions/v2 as a permanent fixture, the LeadingEdge ContentDirectory reference data is regenerated with a 6.7 editor (v3 layout), and UnityFileSystemApi.dll is updated to a local 6.7 build (debug config, Windows only - the mac/linux libraries still need a 6.7 build). --- Analyzer/Properties/Resources.Designer.cs | 12 + Analyzer/Properties/Resources.resx | 6 + .../ContentLayoutLoadableDependencies.sql | 2 +- .../ContentLayoutLoadableObjects.sql | 10 +- .../ContentLayoutLoadableObjectsV2.sql | 16 + .../ContentLayoutLoadableObjectsView.sql | 11 + .../ContentLayoutSerializedFiles.sql | 4 +- Analyzer/Resources/ContentLayoutViews.sql | 35 +- Analyzer/Resources/Init.sql | 2 +- .../AddContentLayoutLoadableDependency.cs | 2 +- .../AddContentLayoutLoadableObject.cs | 42 ++- .../AddContentLayoutSerializedFile.cs | 4 +- .../SQLite/Parsers/ContentLayoutParser.cs | 72 ++-- .../SQLite/Writers/ContentLayoutSQLWriter.cs | 101 ++++-- Analyzer/Util/BuildHistoryHelper.cs | 26 +- Analyzer/Util/ContentLayoutV2Upgrader.cs | 116 +++++++ .../analyze-examples-contentlayout.md | 6 +- Documentation/analyzer-schema.md | 1 + Documentation/contentdirectory-format.md | 95 +++--- Documentation/contentlayout-database.md | 44 ++- Documentation/contentlayout.md | 26 +- ReferenceFinder/ReferenceFinderTool.cs | 6 +- .../v2/ContentLayout.json | 1 + .../BuildContentTEP.json | 318 +++++++----------- .../BuildLog.jsonl | 81 ++--- .../BuildReportSummary.json | 14 +- .../ContentBuildId.txt | 1 + .../ContentLayout.json | 2 +- .../ContentSizeSummary.txt | 22 +- .../ScriptsOnlyCache.yaml | 2 +- .../circularrefs.json | 3 + ...96bba0b9ce4c44194edd738d64b9df.buildreport | Bin 0 -> 11604 bytes ...4157fb08bb9f645971d39c1203bd03.buildreport | Bin 11268 -> 0 bytes .../214959d6771e8c749dd41eca123456b2.cf | Bin 0 -> 2468 bytes .../2357d63c989be24891a7e85b77bce4a6.cf | Bin 0 -> 3000 bytes .../38abbace71a8a756c679a7ff3f501f02.cf | Bin 0 -> 2952 bytes .../39105176560358e0cd444e7a0a513ed3.cf | Bin 0 -> 1400 bytes .../3cfc30da76f416cbd860bd47c9e5ed95.cf | Bin 0 -> 17540 bytes .../491d099506657727cb6683c78c0e2383.cf | Bin 0 -> 4428 bytes .../4d49f1aa2acf2ecf20f51660e1bf77ea.cf | Bin 17572 -> 0 bytes .../51690f09f541d7442ce8faf26fd84696.cf | Bin 4812 -> 0 bytes .../5c43454a3823f172a2a326410a36ba6b.cf | Bin 2136 -> 0 bytes .../61fa28f6b22b739b19ece22ea0b3f29f.cf | Bin 8516 -> 0 bytes .../6a60c190881f43bb4a91a360b9e8850d.cf | Bin 96364 -> 0 bytes .../70ae0762d8f82ea593516bcc52c94bf0.cf | Bin 0 -> 1896 bytes .../730d2d641a53eeb1e633f2ff60d730e9.cf | Bin 1288 -> 0 bytes .../79bdcb3e9bc659d3519594280240cd1b.cf | Bin 0 -> 1900 bytes .../812fe00f6f62ce8504f14dfe20976ba2.cf | Bin 0 -> 10724 bytes .../86d71ff2bb38e064697257d35d6421b8.cf | Bin 2012 -> 0 bytes .../8ad4924ac5e264fde63d8e95a0dab8ab.cf | Bin 1304 -> 0 bytes .../8ec44fb05f86ef7dcb7bbef683d8a330.cf | Bin 0 -> 17540 bytes .../93bb3159569284a5c7bcceb4acb91bae.cf | Bin 1288 -> 0 bytes .../9a17f57112aa00edaa582242bc31fb71.cf | Bin 0 -> 1400 bytes .../ContentDirectory/BuildManifestHash.txt | 2 +- .../baff06b928d147276f2245dd3b19216a.json | 1 - .../bfcf18a244e45f867750e0223a9b3a43.cf | Bin 2184 -> 0 bytes .../c0152db4dd710be51b2decb997325f34.cf | Bin 1524 -> 0 bytes .../c046f78347acd69f03fa7f80362fde94.cf | Bin 2260 -> 0 bytes .../c271b85494f5e4cc35c4ec4a776324af.cf | Bin 17572 -> 0 bytes .../ccccf25fc67d2bea8415c0846c1602dc.cf | Bin 0 -> 32252 bytes .../e320fc78984f8430afa90a591fd02004.json | 1 + .../eb3abd5ab5b0d790980fe9e9df872484.cf | Bin 0 -> 2084 bytes UnityDataModels/ContentLayout.cs | 60 ++-- UnityDataModels/ContentLayoutV2.cs | 170 ++++++++++ .../AnalyzeBuildHistoryTests.cs | 48 +-- .../AnalyzeContentLayoutTests.cs | 145 ++++---- .../AnalyzeContentLayoutV2Tests.cs | 151 +++++++++ UnityDataTool.Tests/BuildReportTests.cs | 10 +- UnityDataTool.Tests/ConsoleTestHelper.cs | 53 +++ UnityDataTool.Tests/DumpTests.cs | 6 +- 70 files changed, 1139 insertions(+), 591 deletions(-) create mode 100644 Analyzer/Resources/ContentLayoutLoadableObjectsV2.sql create mode 100644 Analyzer/Resources/ContentLayoutLoadableObjectsView.sql create mode 100644 Analyzer/Util/ContentLayoutV2Upgrader.cs create mode 100644 TestCommon/Data/ContentLayoutVersions/v2/ContentLayout.json create mode 100644 TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentBuildId.txt create mode 100644 TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/circularrefs.json create mode 100644 TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/f096bba0b9ce4c44194edd738d64b9df.buildreport delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/f64157fb08bb9f645971d39c1203bd03.buildreport create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/214959d6771e8c749dd41eca123456b2.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/2357d63c989be24891a7e85b77bce4a6.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/38abbace71a8a756c679a7ff3f501f02.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/39105176560358e0cd444e7a0a513ed3.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/3cfc30da76f416cbd860bd47c9e5ed95.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/491d099506657727cb6683c78c0e2383.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/4d49f1aa2acf2ecf20f51660e1bf77ea.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/51690f09f541d7442ce8faf26fd84696.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/5c43454a3823f172a2a326410a36ba6b.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/61fa28f6b22b739b19ece22ea0b3f29f.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/6a60c190881f43bb4a91a360b9e8850d.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/70ae0762d8f82ea593516bcc52c94bf0.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/730d2d641a53eeb1e633f2ff60d730e9.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/79bdcb3e9bc659d3519594280240cd1b.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/812fe00f6f62ce8504f14dfe20976ba2.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/86d71ff2bb38e064697257d35d6421b8.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/8ad4924ac5e264fde63d8e95a0dab8ab.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/8ec44fb05f86ef7dcb7bbef683d8a330.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/93bb3159569284a5c7bcceb4acb91bae.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/9a17f57112aa00edaa582242bc31fb71.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/baff06b928d147276f2245dd3b19216a.json delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/bfcf18a244e45f867750e0223a9b3a43.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c0152db4dd710be51b2decb997325f34.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c046f78347acd69f03fa7f80362fde94.cf delete mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c271b85494f5e4cc35c4ec4a776324af.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/ccccf25fc67d2bea8415c0846c1602dc.cf create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/e320fc78984f8430afa90a591fd02004.json create mode 100644 TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/eb3abd5ab5b0d790980fe9e9df872484.cf create mode 100644 UnityDataModels/ContentLayoutV2.cs create mode 100644 UnityDataTool.Tests/AnalyzeContentLayoutV2Tests.cs create mode 100644 UnityDataTool.Tests/ConsoleTestHelper.cs diff --git a/Analyzer/Properties/Resources.Designer.cs b/Analyzer/Properties/Resources.Designer.cs index d9a101a..c05afb2 100644 --- a/Analyzer/Properties/Resources.Designer.cs +++ b/Analyzer/Properties/Resources.Designer.cs @@ -282,6 +282,18 @@ internal static string ContentLayoutLoadableObjects { } } + internal static string ContentLayoutLoadableObjectsV2 { + get { + return ResourceManager.GetString("ContentLayoutLoadableObjectsV2", resourceCulture); + } + } + + internal static string ContentLayoutLoadableObjectsView { + get { + return ResourceManager.GetString("ContentLayoutLoadableObjectsView", resourceCulture); + } + } + internal static string ContentLayoutLoadableSceneDependencies { get { return ResourceManager.GetString("ContentLayoutLoadableSceneDependencies", resourceCulture); diff --git a/Analyzer/Properties/Resources.resx b/Analyzer/Properties/Resources.resx index 27df1f5..6637926 100644 --- a/Analyzer/Properties/Resources.resx +++ b/Analyzer/Properties/Resources.resx @@ -247,6 +247,12 @@ ..\Resources\ContentLayoutLoadableObjects.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + ..\Resources\ContentLayoutLoadableObjectsV2.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + ..\Resources\ContentLayoutLoadableObjectsView.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + ..\Resources\ContentLayoutLoadableScenes.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 diff --git a/Analyzer/Resources/ContentLayoutLoadableDependencies.sql b/Analyzer/Resources/ContentLayoutLoadableDependencies.sql index 850e554..0738263 100644 --- a/Analyzer/Resources/ContentLayoutLoadableDependencies.sql +++ b/Analyzer/Resources/ContentLayoutLoadableDependencies.sql @@ -2,5 +2,5 @@ CREATE TABLE IF NOT EXISTS content_layout_loadable_dependencies ( -- The loadable objects each content file references. serialized_file_index INTEGER, -- references content_layout_serialized_files.file_index - object_id_hash TEXT -- references content_layout_loadable_objects.object_id_hash + loadable_index INTEGER -- references content_layout_loadable_objects.loadable_index ); diff --git a/Analyzer/Resources/ContentLayoutLoadableObjects.sql b/Analyzer/Resources/ContentLayoutLoadableObjects.sql index e34447b..e2ea293 100644 --- a/Analyzer/Resources/ContentLayoutLoadableObjects.sql +++ b/Analyzer/Resources/ContentLayoutLoadableObjects.sql @@ -2,13 +2,11 @@ CREATE TABLE IF NOT EXISTS content_layout_loadable_objects ( -- The objects that can be loaded on demand, identified independently of the content file that -- holds them, plus where each came from in the source project. - object_id_hash TEXT, -- hash of GUID, LFID and identifier_type + loadable_index INTEGER, -- the json array index; how the other content_layout tables name a loadable guid TEXT, -- AssetDatabase GUID of the source asset - asset_path TEXT, - lfid INTEGER, -- local file id of the object in the source asset + lfid INTEGER, -- local file id of the object in its output content file identifier_type INTEGER, serialized_file_index INTEGER, -- content_layout_serialized_files.file_index; NULL if dropped from the build - output_lfid INTEGER, -- local file id of the object in its output content file - is_root_asset INTEGER, - PRIMARY KEY (object_id_hash) + is_root_asset INTEGER, -- 1-based position in the layout's RootAssets; 0 when not a root asset + PRIMARY KEY (loadable_index) ); diff --git a/Analyzer/Resources/ContentLayoutLoadableObjectsV2.sql b/Analyzer/Resources/ContentLayoutLoadableObjectsV2.sql new file mode 100644 index 0000000..0920870 --- /dev/null +++ b/Analyzer/Resources/ContentLayoutLoadableObjectsV2.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS content_layout_loadable_objects +( + -- The objects that can be loaded on demand, identified independently of the content file that + -- holds them, plus where each came from in the source project. This is the variant created when + -- a version 2 layout (Unity 6.6) is imported; it adds source columns that newer layout versions + -- no longer record. + loadable_index INTEGER, -- the json array index; how the other content_layout tables name a loadable + guid TEXT, -- AssetDatabase GUID of the source asset + lfid INTEGER, -- local file id of the object in its output content file + identifier_type INTEGER, + serialized_file_index INTEGER, -- content_layout_serialized_files.file_index; NULL if dropped from the build + is_root_asset INTEGER, -- 1-based position in the layout's RootAssets; 0 when not a root asset + asset_path TEXT, -- v2 layouts only: path of the source asset + source_lfid INTEGER, -- v2 layouts only: local file id of the object in the source asset + PRIMARY KEY (loadable_index) +); diff --git a/Analyzer/Resources/ContentLayoutLoadableObjectsView.sql b/Analyzer/Resources/ContentLayoutLoadableObjectsView.sql new file mode 100644 index 0000000..d9930e5 --- /dev/null +++ b/Analyzer/Resources/ContentLayoutLoadableObjectsView.sql @@ -0,0 +1,11 @@ +CREATE VIEW IF NOT EXISTS content_layout_loadable_objects_view AS +-- Loadables resolved to their analyzed object. The object columns are NULL in a layout-only +-- database. Selects the loadable table's columns as-is, so the v2-only columns (asset_path, +-- source_lfid) appear when the imported layout was version 2. +SELECT l.*, + f.filename, + o.id AS object, t.name AS type, o.name, o.size +FROM content_layout_loadable_objects l +LEFT JOIN content_layout_serialized_files_view f ON f.file_index = l.serialized_file_index +LEFT JOIN objects o ON o.serialized_file = f.serialized_file AND o.object_id = l.lfid +LEFT JOIN types t ON t.id = o.type; diff --git a/Analyzer/Resources/ContentLayoutSerializedFiles.sql b/Analyzer/Resources/ContentLayoutSerializedFiles.sql index 5af0b21..4512326 100644 --- a/Analyzer/Resources/ContentLayoutSerializedFiles.sql +++ b/Analyzer/Resources/ContentLayoutSerializedFiles.sql @@ -3,9 +3,9 @@ CREATE TABLE IF NOT EXISTS content_layout_serialized_files -- The content files (.cf) the ContentDirectory build produced, one row per entry in the -- layout's SerializedFiles array. file_index INTEGER, -- the json array index; how the other content_layout tables name a file - cfid TEXT, -- symbolic .cfid reference string (for built-ins: the built-in path) + stable_id TEXT, -- identity hash used to reference this file (for built-ins: the built-in path) is_builtin INTEGER, - content_hash TEXT, -- NULL for built-ins; the filename is content_hash || '.cf' + artifact_index INTEGER, -- content_layout_binary_artifacts.artifact_index; NULL for built-ins serialized_file INTEGER, -- serialized_files.id; NULL for built-ins and for a layout-only analyze PRIMARY KEY (file_index) ); diff --git a/Analyzer/Resources/ContentLayoutViews.sql b/Analyzer/Resources/ContentLayoutViews.sql index 2db54f6..b3b2d7c 100644 --- a/Analyzer/Resources/ContentLayoutViews.sql +++ b/Analyzer/Resources/ContentLayoutViews.sql @@ -1,39 +1,31 @@ CREATE VIEW IF NOT EXISTS content_layout_serialized_files_view AS --- One row per layout content file with its derived filename, artifact size and core-table link. --- Built-in entries have no file on disk, so their path (cfid) is shown as the filename. -SELECT f.file_index, f.cfid, f.is_builtin, - CASE WHEN f.is_builtin = 1 THEN f.cfid ELSE f.content_hash || '.cf' END AS filename, +-- One row per layout content file with its content hash, derived filename, artifact size and +-- core-table link. Built-in entries have no file on disk, so their path (stable_id) is shown as +-- the filename. +SELECT f.file_index, f.stable_id, f.is_builtin, f.artifact_index, + CASE WHEN f.is_builtin = 1 THEN f.stable_id ELSE ba.content_hash || '.cf' END AS filename, + ba.content_hash, ba.size, f.serialized_file, sf.archive FROM content_layout_serialized_files f -LEFT JOIN content_layout_binary_artifacts ba ON ba.content_hash = f.content_hash AND ba.category = 'contentfile' +LEFT JOIN content_layout_binary_artifacts ba ON ba.artifact_index = f.artifact_index LEFT JOIN serialized_files sf ON sf.id = f.serialized_file; CREATE VIEW IF NOT EXISTS content_layout_source_assets_view AS -- Source asset to the content file(s) it was built into. -SELECT s.asset_path, f.file_index, f.content_hash || '.cf' AS filename, f.serialized_file +SELECT s.asset_path, f.file_index, f.filename, f.serialized_file FROM content_layout_source_assets s -INNER JOIN content_layout_serialized_files f ON f.file_index = s.serialized_file_index; +INNER JOIN content_layout_serialized_files_view f ON f.file_index = s.serialized_file_index; CREATE VIEW IF NOT EXISTS content_layout_serialized_file_dependencies_view AS -- File-to-file dependency edges with filenames resolved on both sides. SELECT d.serialized_file_index, src.filename, d.position, - d.dependency_index, dep.filename AS dependency_filename, dep.cfid AS dependency_cfid + d.dependency_index, dep.filename AS dependency_filename, dep.stable_id AS dependency_stable_id FROM content_layout_serialized_file_dependencies d INNER JOIN content_layout_serialized_files_view src ON src.file_index = d.serialized_file_index INNER JOIN content_layout_serialized_files_view dep ON dep.file_index = d.dependency_index; -CREATE VIEW IF NOT EXISTS content_layout_loadable_objects_view AS --- Loadables resolved to their analyzed object. The object columns are NULL in a layout-only database. -SELECT l.object_id_hash, l.guid, l.asset_path, l.lfid, l.is_root_asset, - f.content_hash || '.cf' AS filename, - o.id AS object, t.name AS type, o.name, o.size -FROM content_layout_loadable_objects l -LEFT JOIN content_layout_serialized_files f ON f.file_index = l.serialized_file_index -LEFT JOIN objects o ON o.serialized_file = f.serialized_file AND o.object_id = l.output_lfid -LEFT JOIN types t ON t.id = o.type; - CREATE VIEW IF NOT EXISTS content_layout_binary_artifacts_view AS -- Artifacts with their on-disk filename derived from the content hash and category. SELECT artifact_index, content_hash, category, size, @@ -48,11 +40,10 @@ FROM content_layout_binary_artifacts; CREATE VIEW IF NOT EXISTS content_layout_resource_files_view AS -- The data files (.resS/.resource) each content file uses, derived from the artifact graph. -SELECT f.file_index, f.content_hash || '.cf' AS filename, +SELECT f.file_index, f.filename, ra.category, rav.filename AS data_filename, ra.size -FROM content_layout_serialized_files f -INNER JOIN content_layout_binary_artifacts ca ON ca.content_hash = f.content_hash AND ca.category = 'contentfile' -INNER JOIN content_layout_artifact_references r ON r.artifact_index = ca.artifact_index +FROM content_layout_serialized_files_view f +INNER JOIN content_layout_artifact_references r ON r.artifact_index = f.artifact_index INNER JOIN content_layout_binary_artifacts ra ON ra.artifact_index = r.referenced_artifact_index INNER JOIN content_layout_binary_artifacts_view rav ON rav.artifact_index = ra.artifact_index WHERE ra.category IN ('texture', 'mesh', 'audio', 'video'); diff --git a/Analyzer/Resources/Init.sql b/Analyzer/Resources/Init.sql index fa45529..92fb470 100644 --- a/Analyzer/Resources/Init.sql +++ b/Analyzer/Resources/Init.sql @@ -168,7 +168,7 @@ WHERE m.type = 'Material'; INSERT INTO types (id, name) VALUES (-1, 'Scene'); -PRAGMA user_version = 8; +PRAGMA user_version = 9; PRAGMA synchronous = OFF; PRAGMA journal_mode = MEMORY; diff --git a/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutLoadableDependency.cs b/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutLoadableDependency.cs index 193587a..3f9168e 100644 --- a/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutLoadableDependency.cs +++ b/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutLoadableDependency.cs @@ -12,7 +12,7 @@ internal class AddContentLayoutLoadableDependency : AbstractCommand protected override Dictionary Fields => new() { { "serialized_file_index", SqliteType.Integer }, - { "object_id_hash", SqliteType.Text } + { "loadable_index", SqliteType.Integer } }; } } diff --git a/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutLoadableObject.cs b/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutLoadableObject.cs index 044b61b..144a484 100644 --- a/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutLoadableObject.cs +++ b/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutLoadableObject.cs @@ -5,20 +5,40 @@ namespace UnityDataTools.Analyzer.SQLite.Commands.ContentLayout { internal class AddContentLayoutLoadableObject : AbstractCommand { + // Version 2 layouts (Unity 6.6) record the source asset path and source lfid of each + // loadable; newer versions do not. The table is created with those columns only when a + // v2 layout is imported, so v3 databases are not cluttered with always-NULL columns. + // Set before CreateCommand, which reads DDLSource and Fields. + public bool V2Columns { get; set; } + protected override string TableName => "content_layout_loadable_objects"; - protected override string DDLSource => Properties.Resources.ContentLayoutLoadableObjects; + protected override string DDLSource => V2Columns + ? Properties.Resources.ContentLayoutLoadableObjectsV2 + : Properties.Resources.ContentLayoutLoadableObjects; - protected override Dictionary Fields => new() + protected override Dictionary Fields { - { "object_id_hash", SqliteType.Text }, - { "guid", SqliteType.Text }, - { "asset_path", SqliteType.Text }, - { "lfid", SqliteType.Integer }, - { "identifier_type", SqliteType.Integer }, - { "serialized_file_index", SqliteType.Integer }, - { "output_lfid", SqliteType.Integer }, - { "is_root_asset", SqliteType.Integer } - }; + get + { + var fields = new Dictionary + { + { "loadable_index", SqliteType.Integer }, + { "guid", SqliteType.Text }, + { "lfid", SqliteType.Integer }, + { "identifier_type", SqliteType.Integer }, + { "serialized_file_index", SqliteType.Integer }, + { "is_root_asset", SqliteType.Integer } + }; + + if (V2Columns) + { + fields.Add("asset_path", SqliteType.Text); + fields.Add("source_lfid", SqliteType.Integer); + } + + return fields; + } + } } } diff --git a/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutSerializedFile.cs b/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutSerializedFile.cs index c2f639c..d453b67 100644 --- a/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutSerializedFile.cs +++ b/Analyzer/SQLite/Commands/ContentLayout/AddContentLayoutSerializedFile.cs @@ -12,9 +12,9 @@ internal class AddContentLayoutSerializedFile : AbstractCommand protected override Dictionary Fields => new() { { "file_index", SqliteType.Integer }, - { "cfid", SqliteType.Text }, + { "stable_id", SqliteType.Text }, { "is_builtin", SqliteType.Integer }, - { "content_hash", SqliteType.Text }, + { "artifact_index", SqliteType.Integer }, { "serialized_file", SqliteType.Integer } }; } diff --git a/Analyzer/SQLite/Parsers/ContentLayoutParser.cs b/Analyzer/SQLite/Parsers/ContentLayoutParser.cs index e0b30f5..ecfdbb3 100644 --- a/Analyzer/SQLite/Parsers/ContentLayoutParser.cs +++ b/Analyzer/SQLite/Parsers/ContentLayoutParser.cs @@ -11,7 +11,9 @@ namespace UnityDataTools.Analyzer.SQLite.Parsers { // Imports the ContentLayout.json produced by BuildPipeline.BuildContentDirectory into the // content_layout* tables (see Documentation/contentlayout.md for the file, and - // Documentation/contentlayout-database.md for the resulting schema). + // Documentation/contentlayout-database.md for the resulting schema). Version 2 files + // (Unity 6.6) are upgraded to the current in-memory model on the way in, so a single write + // path serves both supported versions. public class ContentLayoutParser : ISQLiteFileParser { private ContentLayoutSQLWriter m_Writer; @@ -49,41 +51,73 @@ public bool CanParse(string filename) public void Parse(string filename, string rootDirectory) { - ContentLayout layout; - using (var reader = File.OpenText(filename)) - { - var serializer = new JsonSerializer(); - layout = (ContentLayout)serializer.Deserialize(reader, typeof(ContentLayout)); - } + var version = BuildHistoryHelper.TryReadLayoutVersion(filename); // The tool's failure summary only includes exception details in verbose mode, so // report the reason for these expected failures directly. - if (layout == null) + if (version == null) { - Fail($"\"{filename}\" does not contain a ContentLayout."); + throw Fail($"\"{filename}\" does not contain a ContentLayout."); } - if (layout.Version != ContentLayout.CurrentVersion) + // The upgrader and the writer throw InvalidDataException for dangling references in + // the layout (an ObjectIdHash, ContentHash or ArtifactIndex with no target). + try { - Fail($"Unsupported ContentLayout.json version {layout.Version} (this version of UnityDataTool supports version {ContentLayout.CurrentVersion})."); + ContentLayout layout; + LoadableObjectV2Data[] v2Data = null; + + switch (version) + { + case Models.V2.ContentLayout.SchemaVersion: + layout = ContentLayoutV2Upgrader.Upgrade( + Deserialize(filename), out v2Data); + break; + + case ContentLayout.CurrentVersion: + layout = Deserialize(filename); + break; + + default: + throw Fail($"Unsupported ContentLayout.json version {version} (this version of UnityDataTool supports versions {Models.V2.ContentLayout.SchemaVersion} and {ContentLayout.CurrentVersion})."); + } + + if (m_ImportedLayout != null) + { + throw Fail($"Only a single ContentLayout.json can be analyzed (already imported \"{m_ImportedLayout}\")."); + } + + // Only create the content_layout tables when a layout is actually imported. The v2 + // variant of the loadable-objects table carries extra source columns. + m_Writer.Init(v2Data != null); + m_Writer.WriteContentLayout(filename, layout, v2Data); + m_ImportedLayout = filename; } + catch (InvalidDataException e) + { + throw Fail($"\"{filename}\" is not a valid ContentLayout: {e.Message}"); + } + } - if (m_ImportedLayout != null) + private T Deserialize(string filename) where T : class + { + using var reader = File.OpenText(filename); + var serializer = new JsonSerializer(); + var layout = (T)serializer.Deserialize(reader, typeof(T)); + + if (layout == null) { - Fail($"Only a single ContentLayout.json can be analyzed (already imported \"{m_ImportedLayout}\")."); + throw Fail($"\"{filename}\" does not contain a ContentLayout."); } - // Only create the content_layout tables when a layout is actually imported. - m_Writer.Init(); - m_Writer.WriteContentLayout(filename, layout); - m_ImportedLayout = filename; + return layout; } - private static void Fail(string message) + private static Exception Fail(string message) { Console.Error.WriteLine(); Console.Error.WriteLine(message); - throw new Exception(message); + return new Exception(message); } // Called after all files are processed, so the analyzed .cf files all have their diff --git a/Analyzer/SQLite/Writers/ContentLayoutSQLWriter.cs b/Analyzer/SQLite/Writers/ContentLayoutSQLWriter.cs index ccd55c1..c63cd34 100644 --- a/Analyzer/SQLite/Writers/ContentLayoutSQLWriter.cs +++ b/Analyzer/SQLite/Writers/ContentLayoutSQLWriter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Microsoft.Data.Sqlite; using UnityDataTools.Analyzer.SQLite.Commands.ContentLayout; @@ -11,8 +12,10 @@ namespace UnityDataTools.Analyzer.SQLite.Writers // Populates the content_layout* tables from a ContentLayout.json (see // Documentation/contentlayout.md). The tables mirror the json structure, with two adjustments // that make the data natural to query: the top-level RootAssets list is folded into the - // is_root_asset flag, and the json's sentinel values (-1 for "dropped from build", missing - // ContentHash for built-ins) are stored as NULL. + // is_root_asset column (holding the 1-based root position), and the json's sentinel values + // (-1 for "dropped from build" and for the built-in entry's missing artifact) are stored as + // NULL. Version 2 layouts arrive here already upgraded to the current model (see + // ContentLayoutV2Upgrader); their extra source data lands in v2-only columns. internal class ContentLayoutSQLWriter : IDisposable { private AddContentLayout m_AddContentLayout = new(); @@ -47,14 +50,17 @@ public ContentLayoutSQLWriter(SqliteConnection database, } // Creates the content_layout tables and views. Called lazily on the first import so that - // analyzing other content (AssetBundles, Player builds) doesn't create empty tables. - public void Init() + // analyzing other content (AssetBundles, Player builds) doesn't create empty tables. The + // loadable-objects table and view get extra source columns when importing a v2 layout. + public void Init(bool v2LoadableColumns) { if (m_Initialized) return; m_Initialized = true; + m_AddLoadableObject.V2Columns = v2LoadableColumns; + m_AddContentLayout.CreateCommand(m_Database); m_AddSerializedFile.CreateCommand(m_Database); m_AddSourceAsset.CreateCommand(m_Database); @@ -67,10 +73,17 @@ public void Init() m_AddArtifactReference.CreateCommand(m_Database); ExecuteDDL(Properties.Resources.ContentLayoutViews); + // Created separately because it must come after the loadable-objects table exists in + // its version-dependent form: the view selects that table's columns as-is. + ExecuteDDL(Properties.Resources.ContentLayoutLoadableObjectsView); } - public void WriteContentLayout(string filename, ContentLayout layout) + // v2Data holds the v2-only source fields of each loadable, aligned by index with + // layout.LoadableObjectIds; null when a current-version layout is imported. + public void WriteContentLayout(string filename, ContentLayout layout, IReadOnlyList v2Data) { + var contentHashByFileIndex = ResolveContentHashes(layout); + using var transaction = m_Database.BeginTransaction(); SetTransaction(transaction); @@ -85,18 +98,18 @@ public void WriteContentLayout(string filename, ContentLayout layout) foreach (var file in layout.SerializedFiles) { m_AddSerializedFile.SetValue("file_index", file.Index); - m_AddSerializedFile.SetValue("cfid", file.ID); + m_AddSerializedFile.SetValue("stable_id", file.StableId); m_AddSerializedFile.SetValue("is_builtin", file.IsBuiltIn ? 1 : 0); - m_AddSerializedFile.SetValue("content_hash", - string.IsNullOrEmpty(file.ContentHash) ? null : file.ContentHash); + m_AddSerializedFile.SetValue("artifact_index", + file.ArtifactIndex < 0 ? null : file.ArtifactIndex); // Filled in by LinkSerializedFiles when the analyzed input also contains the // build content. m_AddSerializedFile.SetValue("serialized_file", null); m_AddSerializedFile.ExecuteNonQuery(); - if (!string.IsNullOrEmpty(file.ContentHash)) + if (contentHashByFileIndex.TryGetValue(file.Index, out var contentHash)) { - m_ImportedFiles.Add((file.Index, file.ContentHash)); + m_ImportedFiles.Add((file.Index, contentHash)); } // Empty arrays can be omitted from the json, leaving the fields null. @@ -118,10 +131,10 @@ public void WriteContentLayout(string filename, ContentLayout layout) m_AddSerializedFileDependency.ExecuteNonQuery(); } - foreach (var objectIdHash in file.LoadableDependencies ?? []) + foreach (var loadableIndex in file.LoadableDependencies ?? []) { m_AddLoadableDependency.SetValue("serialized_file_index", file.Index); - m_AddLoadableDependency.SetValue("object_id_hash", objectIdHash); + m_AddLoadableDependency.SetValue("loadable_index", loadableIndex); m_AddLoadableDependency.ExecuteNonQuery(); } @@ -133,19 +146,32 @@ public void WriteContentLayout(string filename, ContentLayout layout) } } - var rootAssets = new HashSet(layout.RootAssets ?? []); + // RootAssets order is the build's root input order, recorded as a 1-based position. + var rootPositions = new Dictionary(); + var rootAssets = layout.RootAssets ?? []; + for (int i = 0; i < rootAssets.Length; ++i) + { + rootPositions[rootAssets[i]] = i + 1; + } - foreach (var loadable in layout.LoadableObjectIds ?? []) + var loadables = layout.LoadableObjectIds ?? []; + for (int i = 0; i < loadables.Length; ++i) { - m_AddLoadableObject.SetValue("object_id_hash", loadable.ObjectIdHash); + var loadable = loadables[i]; + m_AddLoadableObject.SetValue("loadable_index", i); m_AddLoadableObject.SetValue("guid", loadable.GUID); - m_AddLoadableObject.SetValue("asset_path", loadable.AssetPath); m_AddLoadableObject.SetValue("lfid", loadable.LFID); m_AddLoadableObject.SetValue("identifier_type", loadable.IdentifierType); m_AddLoadableObject.SetValue("serialized_file_index", loadable.SerializedFile < 0 ? null : loadable.SerializedFile); - m_AddLoadableObject.SetValue("output_lfid", loadable.OutputLFID); - m_AddLoadableObject.SetValue("is_root_asset", rootAssets.Contains(loadable.ObjectIdHash) ? 1 : 0); + m_AddLoadableObject.SetValue("is_root_asset", rootPositions.GetValueOrDefault(i, 0)); + + if (v2Data != null) + { + m_AddLoadableObject.SetValue("asset_path", v2Data[i].AssetPath); + m_AddLoadableObject.SetValue("source_lfid", v2Data[i].SourceLfid); + } + m_AddLoadableObject.ExecuteNonQuery(); } @@ -185,31 +211,54 @@ public void WriteContentLayout(string filename, ContentLayout layout) // Indexes are created after the bulk insert so that a very large layout imports fast. ExecuteDDL(Properties.Resources.ContentLayoutIndexes); - PopulateContentFileDependencies(layout); + PopulateContentFileDependencies(layout, contentHashByFileIndex); + } + + // The content hash of each non-built-in layout file, resolved through its ArtifactIndex. + // The hash gives the on-disk filename, which is how the layout entries are matched to the + // analyzed files. + private static Dictionary ResolveContentHashes(ContentLayout layout) + { + var artifactHashByIndex = (layout.BinaryArtifacts ?? []).ToDictionary(a => a.Index, a => a.ContentHash); + var contentHashByFileIndex = new Dictionary(); + + foreach (var file in layout.SerializedFiles ?? []) + { + if (file.ArtifactIndex < 0) + continue; + + if (!artifactHashByIndex.TryGetValue(file.ArtifactIndex, out var hash) || string.IsNullOrEmpty(hash)) + { + throw new InvalidDataException( + $"SerializedFile {file.Index} references BinaryArtifacts[{file.ArtifactIndex}], which does not exist."); + } + + contentHashByFileIndex.Add(file.Index, hash); + } + + return contentHashByFileIndex; } // Fills the shared dependency map that resolves the external references of the analyzed // content files (see ContentFileDependencyMap): for each content file, the dependency - // indexes become the target names, in order. Built-in dependencies (no content hash) map to + // indexes become the target names, in order. Built-in dependencies (no artifact) map to // null so the reference falls back to the external table path. Keys and resolved names use // the normalized content-hash name so they match the analyzed files regardless of whether // those carry the ".cf" extension on disk. - private void PopulateContentFileDependencies(ContentLayout layout) + private void PopulateContentFileDependencies(ContentLayout layout, Dictionary contentHashByFileIndex) { - var hashByIndex = (layout.SerializedFiles ?? []).ToDictionary(f => f.Index, f => f.ContentHash); - foreach (var file in layout.SerializedFiles ?? []) { - if (string.IsNullOrEmpty(file.ContentHash)) + if (!contentHashByFileIndex.TryGetValue(file.Index, out var contentHash)) continue; var resolved = (file.SerializedFileDependencies ?? []) - .Select(i => hashByIndex.TryGetValue(i, out var hash) && !string.IsNullOrEmpty(hash) + .Select(i => contentHashByFileIndex.TryGetValue(i, out var hash) ? ContentFileDependencyMap.NormalizeFileName(hash) : null) .ToArray(); - m_ContentFileDependencies.Add(ContentFileDependencyMap.NormalizeFileName(file.ContentHash), resolved); + m_ContentFileDependencies.Add(ContentFileDependencyMap.NormalizeFileName(contentHash), resolved); } } diff --git a/Analyzer/Util/BuildHistoryHelper.cs b/Analyzer/Util/BuildHistoryHelper.cs index 11f57d8..184d53b 100644 --- a/Analyzer/Util/BuildHistoryHelper.cs +++ b/Analyzer/Util/BuildHistoryHelper.cs @@ -110,17 +110,35 @@ public static List CollectBuildFiles(string buildFolder) // file (layouts of large builds are big; the hash is one of the first properties). Returns // null when the value cannot be found or the file is not valid json. public static string TryReadBuildManifestHash(string contentLayoutPath) + { + return TryReadTopLevelProperty(contentLayoutPath, "BuildManifestHash", + reader => reader.ReadAsString()?.ToLowerInvariant()); + } + + // Reads the top-level Version of a ContentLayout.json without parsing the whole file (it is + // the first property). Returns null when the value cannot be found or the file is not valid + // json. + public static int? TryReadLayoutVersion(string contentLayoutPath) + { + return TryReadTopLevelProperty(contentLayoutPath, "Version", + reader => reader.ReadAsInt32()); + } + + // Streams the start of a json file looking for a top-level property, without parsing the + // whole file. Returns null when the property is not found within the first tokens or the + // file is not valid json. + private static T TryReadTopLevelProperty(string path, string propertyName, Func readValue) { try { - using var reader = new JsonTextReader(File.OpenText(contentLayoutPath)); + using var reader = new JsonTextReader(File.OpenText(path)); for (int i = 0; i < 64 && reader.Read(); ++i) { if (reader.TokenType == JsonToken.PropertyName && reader.Depth == 1 && - "BuildManifestHash".Equals(reader.Value)) + propertyName.Equals(reader.Value)) { - return reader.ReadAsString()?.ToLowerInvariant(); + return readValue(reader); } } } @@ -128,7 +146,7 @@ public static string TryReadBuildManifestHash(string contentLayoutPath) { } - return null; + return default; } static bool HasExtension(string path, string extension) diff --git a/Analyzer/Util/ContentLayoutV2Upgrader.cs b/Analyzer/Util/ContentLayoutV2Upgrader.cs new file mode 100644 index 0000000..ac54c53 --- /dev/null +++ b/Analyzer/Util/ContentLayoutV2Upgrader.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityDataTools.Models; + +namespace UnityDataTools.Analyzer.Util +{ + // Source data of a v2 loadable that has no place in the current ContentLayout model, kept for + // the v2-only database columns. Aligned by index with ContentLayout.LoadableObjectIds. + internal record LoadableObjectV2Data(string AssetPath, long SourceLfid); + + // Converts a version 2 ContentLayout (Unity 6.6) into the current in-memory model so a single + // import path handles both versions. v3 only removed and reorganized data, so the conversion + // is exact: hash-based references become indices and each ContentHash becomes the index of the + // matching binary artifact. The v2-only source fields are returned separately. + internal static class ContentLayoutV2Upgrader + { + private const string CfidExtension = ".cfid"; + + public static ContentLayout Upgrade(Models.V2.ContentLayout v2, out LoadableObjectV2Data[] v2Data) + { + var artifactIndexByHash = (v2.BinaryArtifacts ?? []) + .Where(a => a.Category == Models.V2.BuildArtifactCategory.ContentFile) + .ToDictionary(a => a.ContentHash, a => a.Index); + + var loadables = v2.LoadableObjectIds ?? []; + var loadableIndexByHash = new Dictionary(loadables.Length); + for (int i = 0; i < loadables.Length; ++i) + { + loadableIndexByHash.Add(loadables[i].ObjectIdHash, i); + } + + var layout = new ContentLayout + { + Version = v2.Version, + BuildManifestHash = v2.BuildManifestHash, + SerializedFiles = (v2.SerializedFiles ?? []).Select(f => new SerializedFileLayout + { + Index = f.Index, + StableId = StripCfidExtension(f.ID), + IsBuiltIn = f.IsBuiltIn, + SourceAssets = f.SourceAssets, + SerializedFileDependencies = f.SerializedFileDependencies, + LoadableDependencies = f.LoadableDependencies? + .Select(hash => ResolveLoadable(loadableIndexByHash, hash)).ToArray(), + LoadableSceneDependencies = f.LoadableSceneDependencies, + ArtifactIndex = ResolveArtifact(artifactIndexByHash, f), + }).ToArray(), + RootAssets = v2.RootAssets? + .Select(hash => ResolveLoadable(loadableIndexByHash, hash)).ToArray(), + LoadableObjectIds = loadables.Select(l => new LoadableObjectIdLayout + { + GUID = l.GUID, + // The current definition of LFID: the output-file lfid when the object was + // placed in the build, the source lfid otherwise. + LFID = l.SerializedFile >= 0 ? l.OutputLFID : l.LFID, + IdentifierType = l.IdentifierType, + SerializedFile = l.SerializedFile, + }).ToArray(), + LoadableSceneIds = (v2.LoadableSceneIds ?? []).Select(s => new LoadableSceneIdLayout + { + GUID = s.GUID, + Path = s.Path, + SerializedFile = s.SerializedFile, + }).ToArray(), + BinaryArtifacts = (v2.BinaryArtifacts ?? []).Select(a => new BinaryArtifact + { + Index = a.Index, + ContentHash = a.ContentHash, + Category = a.Category, + Size = a.Size, + ArtifactReferences = a.ArtifactReferences, + }).ToArray(), + }; + + v2Data = loadables.Select(l => new LoadableObjectV2Data(l.AssetPath, l.LFID)).ToArray(); + return layout; + } + + private static int ResolveLoadable(Dictionary loadableIndexByHash, string objectIdHash) + { + if (!loadableIndexByHash.TryGetValue(objectIdHash, out var index)) + { + throw new InvalidDataException( + $"ObjectIdHash \"{objectIdHash}\" has no matching LoadableObjectIds entry."); + } + + return index; + } + + private static int ResolveArtifact(Dictionary artifactIndexByHash, Models.V2.SerializedFileLayout file) + { + // Built-in entries are not produced by the build and have no ContentHash. + if (string.IsNullOrEmpty(file.ContentHash)) + return -1; + + if (!artifactIndexByHash.TryGetValue(file.ContentHash, out var index)) + { + throw new InvalidDataException( + $"SerializedFile {file.Index} has ContentHash \"{file.ContentHash}\" with no matching contentfile BinaryArtifact."); + } + + return index; + } + + // v2 IDs carry a ".cfid" extension (built-in entries use a path instead); the current + // schema uses the bare identity hash, so strip it for a uniform stable_id column. + private static string StripCfidExtension(string id) + { + return id != null && id.EndsWith(CfidExtension, StringComparison.Ordinal) + ? id.Substring(0, id.Length - CfidExtension.Length) + : id; + } + } +} diff --git a/Documentation/analyze-examples-contentlayout.md b/Documentation/analyze-examples-contentlayout.md index 07e2c75..111bdf0 100644 --- a/Documentation/analyze-examples-contentlayout.md +++ b/Documentation/analyze-examples-contentlayout.md @@ -62,10 +62,10 @@ WHERE s.asset_path = 'Assets/Scenes/Scene1.unity'; ## The loadables and what they resolve to (layout + content) Each loadable with the analyzed object it points at (type, name, size), and whether it is a root -asset of the build: +asset of the build (`is_root_asset` holds the root's 1-based input position, 0 for non-roots): ```sql -SELECT * FROM content_layout_loadable_objects_view ORDER BY is_root_asset DESC, asset_path; +SELECT * FROM content_layout_loadable_objects_view ORDER BY is_root_asset DESC, name; ``` ## Data files of a serialized file (layout only) @@ -75,7 +75,7 @@ serialized file: ```sql SELECT * FROM content_layout_resource_files_view -WHERE filename = 'c0152db4dd710be51b2decb997325f34.cf'; +WHERE filename = 'eb3abd5ab5b0d790980fe9e9df872484.cf'; ``` ## Checking reference resolution (layout + content) diff --git a/Documentation/analyzer-schema.md b/Documentation/analyzer-schema.md index d4a0564..0777830 100644 --- a/Documentation/analyzer-schema.md +++ b/Documentation/analyzer-schema.md @@ -552,6 +552,7 @@ Any schema change - a new or changed table, view or column - must bump the pragm | 6 | `archives.name` is UNIQUE ([#51](https://github.com/Unity-Technologies/UnityDataTools/issues/51)) | | 7 | Unity 6.6 `build_reports` columns and `build_report_content_*` tables ([#107](https://github.com/Unity-Technologies/UnityDataTools/issues/107)); `asset_name` / `asset_extension` columns on `build_report_source_assets` ([#110](https://github.com/Unity-Technologies/UnityDataTools/issues/110)) | | 8 | `archives.name` is the path relative to the scanned directory, not the bare file name ([#149](https://github.com/Unity-Technologies/UnityDataTools/issues/149)) | +| 9 | `content_layout*` tables track the version 3 layout schema of Unity 6.7: loadables keyed by `loadable_index`, `stable_id` / `artifact_index` columns replace `cfid` / `content_hash`, `is_root_asset` records the root position, and the v2-only columns (`asset_path`, `source_lfid`) exist only in databases imported from a version 2 layout ([#131](https://github.com/Unity-Technologies/UnityDataTools/issues/131)) | ## Related documentation diff --git a/Documentation/contentdirectory-format.md b/Documentation/contentdirectory-format.md index 147c1fa..64656ba 100644 --- a/Documentation/contentdirectory-format.md +++ b/Documentation/contentdirectory-format.md @@ -52,7 +52,7 @@ runtime. Every build artifact is named by the hash of its own content: -- **Content Files** use the `.cf` extension (for example `c0152db4dd710be51b2decb997325f34.cf`). +- **Content Files** use the `.cf` extension (for example `eb3abd5ab5b0d790980fe9e9df872484.cf`). - **`.resS`** files hold streamed texture and mesh data. - **`.resource`** files hold audio and video data. @@ -73,7 +73,7 @@ it is best to avoid assumption that the extensions are always present. ### The build manifest The build manifest is a JSON file, also named by its content hash (for example -`baff06b928d147276f2245dd3b19216a.json`). It records everything needed to load the content: the list of +`e320fc78984f8430afa90a591fd02004.json`). It records everything needed to load the content: the list of Content Files, their dependencies, and the loadable objects and scenes. Its schema is internal and may change substantially, so this page does not document it. Instead, use [`ContentLayout.json`](contentlayout.md), which presents the same information (plus source-asset @@ -145,8 +145,8 @@ on) are not packed with it — they land in their own Content Files following th [granularity rules](#build-layout-granularity), and the scene's file reaches them through the manifest dependency list. This differs from Player builds, where each scene comes with companion `sharedassets` files holding its assets. Scene Content Files also differ in their object IDs: the -scene's objects get small sequential IDs (1, 2, 3, ...), rather than the 64-bit hash-based IDs used -in other Content Files. +scene's objects get small sequential IDs (1, 2, 3, ...), rather than keeping the local file ids of +their source objects like other Content Files do. In `ContentLayout.json` (schema details on the [ContentLayout.json](contentlayout.md) page), the top-level `LoadableSceneIds` array lists each scene with its source path and the Content File that @@ -155,27 +155,29 @@ scenes: ```json "LoadableSceneIds": [ - { "GUID": "590cfeb4e0ff90f4e92f9e1262bcfe6f", "Path": "Assets/Scenes/Scene2.unity", "SerializedFile": 6 }, - { "GUID": "162c015549f8733449ac70ae78ad3aa5", "Path": "Assets/Scenes/Scene1.unity", "SerializedFile": 2 } + { "GUID": "590cfeb4e0ff90f4e92f9e1262bcfe6f", "Path": "Assets/Scenes/Scene2.unity", "SerializedFile": 12 }, + { "GUID": "162c015549f8733449ac70ae78ad3aa5", "Path": "Assets/Scenes/Scene1.unity", "SerializedFile": 6 } ] ``` -The scene's own `SerializedFiles` entry names the scene as its single source asset, and its symbolic -`ID` is derived from the scene's GUID: +The scene's own `SerializedFiles` entry names the scene as its single source asset, and its +`StableId` is derived from the scene's GUID: ```json { - "Index": 2, - "ID": "162c015549f8733449ac70ae78ad3aa5.cfid", + "Index": 6, + "StableId": "162c015549f8733449ac70ae78ad3aa5", + "ArtifactIndex": 6, "SourceAssets": [ "Assets/Scenes/Scene1.unity" ], - "SerializedFileDependencies": [ 0, 1, 9 ], - "ContentHash": "c271b85494f5e4cc35c4ec4a776324af" + "SerializedFileDependencies": [ 0, 1, 13 ] } ``` -So this scene lives in `c271b85494f5e4cc35c4ec4a776324af.cf`, and depends on three other files: the -built-in `unity default resources` entry, the Content File built from `Resources/unity_builtin_extra` -(the default sprite material), and the file holding the Sprite it shows. +Its `ArtifactIndex` points at the `BinaryArtifacts` entry whose `ContentHash` is +`8ec44fb05f86ef7dcb7bbef683d8a330`, so this scene lives in `8ec44fb05f86ef7dcb7bbef683d8a330.cf`. It +depends on three other files: the built-in `unity default resources` entry, the Content File built +from `Resources/unity_builtin_extra` (the default sprite material), and the file holding the Sprite +it shows. A `LoadableSceneId` reference is an on-demand reference, like a `Loadable`: loading the referencing file does not load the scene. The file holding the reference records the scene by path in its @@ -186,12 +188,12 @@ GUID. The reference build's `SceneList` asset, a ScriptableObject with a diction ```json { - "Index": 7, - "ID": "70a210050f71a924aa83be7146547111.cfid", + "Index": 3, + "StableId": "70a210050f71a924aa83be7146547111", + "ArtifactIndex": 3, "SourceAssets": [ "Assets/ScriptableObjects/SceneList.asset" ], - "SerializedFileDependencies": [ 8 ], - "LoadableSceneDependencies": [ "Assets/Scenes/Scene2.unity", "Assets/Scenes/Scene1.unity" ], - "ContentHash": "8ad4924ac5e264fde63d8e95a0dab8ab" + "SerializedFileDependencies": [ 4 ], + "LoadableSceneDependencies": [ "Assets/Scenes/Scene2.unity", "Assets/Scenes/Scene1.unity" ] } ``` @@ -230,24 +232,25 @@ File**, in the manifest, rather than in the file's own external-reference table. reproduced in [`ContentLayout.json`](contentlayout.md), which is what the examples below use. Consider `ContentDirectoryRoot.asset`, which directly references several ScriptableObjects. Its entry -in `ContentLayout.json` names the source asset, the file's own content hash, and — crucially — an -ordered list of the files it depends on: +in `ContentLayout.json` names the source asset, the artifact holding the file's content, and — +crucially — an ordered list of the files it depends on: ```json { - "Index": 5, - "ID": "52b43dad178849b42ac753005736e7bb.cfid", + "Index": 10, + "StableId": "52b43dad178849b42ac753005736e7bb", + "ArtifactIndex": 10, "SourceAssets": [ "Assets/ScriptableObjects/ContentDirectoryRoot.asset" ], - "SerializedFileDependencies": [ 8, 4, 11, 13, 7 ], - "ContentHash": "c0152db4dd710be51b2decb997325f34" + "SerializedFileDependencies": [ 4, 5, 2, 9, 3 ] } ``` -The `ContentHash` tells us this asset lives in `c0152db4dd710be51b2decb997325f34.cf`. Dumping that file -shows the object's references and the file's external-reference table: +`BinaryArtifacts` entry 10 has the content hash `eb3abd5ab5b0d790980fe9e9df872484`, so this asset +lives in `eb3abd5ab5b0d790980fe9e9df872484.cf`. Dumping that file shows the object's references and +the file's external-reference table: ``` -UnityDataTool dump --stdout c0152db4dd710be51b2decb997325f34.cf +UnityDataTool dump --stdout eb3abd5ab5b0d790980fe9e9df872484.cf ``` ``` External References @@ -257,13 +260,13 @@ path(3): "21679be819d6e9146a63bb02a7e51f2f.cfid" GUID: 0000000000000000000000000 path(4): "78532141fd7679a458405eb16bdb75fd.cfid" GUID: 00000000000000000000000000000000 Type: 0 path(5): "70a210050f71a924aa83be7146547111.cfid" GUID: 00000000000000000000000000000000 Type: 0 -ID: -775554941117088049 (ClassID: 114) MonoBehaviour +ID: 11400000 (ClassID: 114) MonoBehaviour ... data[1] (SerializedKeyValue`2) key (string) SingleAudioClipLoadableReference value (PPtr<$ScriptableObject>) m_FileID (int) 3 - m_PathID (SInt64) -2313013086301746513 + m_PathID (SInt64) 11400000 ``` The external table lists symbolic names ending in `.cfid`, **not** the content-hash filenames. @@ -275,29 +278,29 @@ through the build. A key goal of the content directory design is to reduce the a Because of that, the content of the external table is effectively ignored by the loading system for Content File references. What counts is the ordered dependency list in the manifest. The -`"SerializedFileDependencies": [8, 4, 11, 13, 7]` array corresponds, in exact size and order, to the +`"SerializedFileDependencies": [4, 5, 2, 9, 3]` array corresponds, in exact size and order, to the five entries of the external table. So resolving the reference to `SingleAudioClipLoadableReference` above: 1. The `PPtr` has `m_FileID` 3. A non-zero `m_FileID` is a 1-based index into the external table (0 would mean "this same file"). -2. Index 3 maps to the 3rd entry of `SerializedFileDependencies`, which is `11`. -3. `ContentLayout.json` entry with `"Index": 11` is `SingleAudioClipLoadableReference.asset`, whose - `ContentHash` is `5c43454a3823f172a2a326410a36ba6b`. -4. The referenced file is therefore `5c43454a3823f172a2a326410a36ba6b.cf`. +2. Index 3 maps to the 3rd entry of `SerializedFileDependencies`, which is `2`. +3. `ContentLayout.json` entry with `"Index": 2` is `SingleAudioClipLoadableReference.asset`, whose + `ArtifactIndex` leads to the content hash `38abbace71a8a756c679a7ff3f501f02`. +4. The referenced file is therefore `38abbace71a8a756c679a7ff3f501f02.cf`. A partial mapping for this file, showing how `m_FileID` values in the external table resolve through -the dependency list `[8, 4, 11, 13, 7]` to a content-hash filename: +the dependency list `[4, 5, 2, 9, 3]` to a content-hash filename: ```mermaid flowchart TD - Root["ContentDirectoryRoot
cfid 52b43dad…
file c0152db4….cf
deps [8, 4, 11, 13, 7]"] - Loadable["LoadableAudioClipReference
Index 4 · cfid 4038ff67…
file bfcf18a2….cf"] - Single["SingleAudioClipLoadableReference
Index 11 · cfid 21679be8…
file 5c43454a….cf"] + Root["ContentDirectoryRoot
cfid 52b43dad…
file eb3abd5a….cf
deps [4, 5, 2, 9, 3]"] + Loadable["LoadableAudioClipReference
Index 5 · cfid 4038ff67…
file 2357d63c….cf"] + Single["SingleAudioClipLoadableReference
Index 2 · cfid 21679be8…
file 38abbace….cf"] - Root -->|"m_FileID 2 → dep 4 → Index 4"| Loadable - Root -->|"m_FileID 3 → dep 11 → Index 11"| Single + Root -->|"m_FileID 2 → dep 5 → Index 5"| Loadable + Root -->|"m_FileID 3 → dep 2 → Index 2"| Single ``` > [!NOTE] @@ -322,7 +325,7 @@ audio clips and a texture, so it exercises both kinds of data file. Dumping the `Assets/Textures/GreenStatic.png`'s texture data shows the Texture2D pointing at its `.resS` file: ``` -ID: -6933612096100796476 (ClassID: 83) AudioClip +ID: 8300000 (ClassID: 83) AudioClip m_Name (string) a ... m_Resource (StreamedResource) @@ -331,7 +334,7 @@ ID: -6933612096100796476 (ClassID: 83) AudioClip m_Size (UInt64) 47424 ``` ``` -ID: 1183010003894172340 (ClassID: 28) Texture2D +ID: 2800000 (ClassID: 28) Texture2D m_Name (string) GreenStatic m_StreamData (StreamingInfo) offset (UInt64) 0 @@ -366,8 +369,8 @@ File's `ArtifactReferences` point at the resource artifacts it needs. Continuing above: ```json -{ "Index": 11, "ContentHash": "730d2d641a53eeb1e633f2ff60d730e9", "Category": "contentfile", "Size": 1288, "ArtifactReferences": [12] }, -{ "Index": 12, "ContentHash": "4226b5c16a50dab6eff0f08dd1253d4b", "Category": "audio", "Size": 47424 } +{ "Index": 11, "ContentHash": "39105176560358e0cd444e7a0a513ed3", "Category": "contentfile", "Size": 1400, "ArtifactReferences": [17] }, +{ "Index": 17, "ContentHash": "4226b5c16a50dab6eff0f08dd1253d4b", "Category": "audio", "Size": 47424 } ``` When [`analyze`](command-analyze.md) imports the layout it preserves this: the artifacts land in diff --git a/Documentation/contentlayout-database.md b/Documentation/contentlayout-database.md index b7217c3..72b9821 100644 --- a/Documentation/contentlayout-database.md +++ b/Documentation/contentlayout-database.md @@ -10,10 +10,11 @@ For what `ContentLayout.json` contains conceptually, see [ContentLayout.json](co * The `content_layout*` tables and views are only created when a `ContentLayout.json` is actually part of the analyzed input, so databases produced from AssetBundles or Player builds are not cluttered with empty tables. * A single layout per database is supported. When several `ContentLayout.json` files are on the input, the one whose `BuildManifestHash` matches the analyzed build is selected and the rest are ignored (see [the `analyze` command](command-analyze.md#contentdirectory-builds)). -* The tables mirror the json structure. Entries that cross-reference each other by array index in the json do the same in the database: `file_index` and `artifact_index` columns hold the json array indexes. +* The tables mirror the json structure of the current layout version (version 3, Unity 6.7). Entries that cross-reference each other by array index in the json do the same in the database: `file_index`, `artifact_index` and `loadable_index` columns hold the json array indexes. +* Version 2 layouts (Unity 6.6) are upgraded on import, so both versions produce the same schema and the same queries work on both. See [Layout version differences](#layout-version-differences). * Two adjustments are made so the data is natural to query: - * The json's sentinel values are stored as SQL `NULL`: a `SerializedFile` value of `-1` (an object dropped from the build) and the missing `ContentHash` of built-in entries. - * The top-level `RootAssets` list is folded into the `is_root_asset` flag of `content_layout_loadable_objects`. + * The json's `-1` sentinels are stored as SQL `NULL`: the `SerializedFile` of an object dropped from the build, and the `ArtifactIndex` of the built-in entry. + * The top-level `RootAssets` list is folded into the `is_root_asset` column of `content_layout_loadable_objects`, which records each root's 1-based position in the list (0 for non-roots), preserving the root input order. * This page is the reference for the columns; [`ContentLayout.cs`](../UnityDataModels/ContentLayout.cs) is the reference for the json schema. A produced database also carries short notes on its non-obvious columns inside the `CREATE` statements, so `sqlite3 Analysis.db ".schema content_layout_serialized_files"` describes them without needing this page. ## Tables @@ -36,9 +37,9 @@ One row per serialized file (`.cf` Content File) of the build. | Column | Type | Description | |---|---|---| | `file_index` | INTEGER | The json array index. How every other `content_layout` table names a file. Primary key. | -| `cfid` | TEXT | The symbolic `.cfid` reference string. For built-ins, the built-in path. | +| `stable_id` | TEXT | The identity hash used to reference this file from other files (see [Stable ids](contentlayout.md#stable-ids)). Stored without the `.cfid` extension that v2 layouts carry. For built-ins, the built-in path. | | `is_builtin` | INTEGER | 1 for a built-in entry, which is not a file produced by this build. | -| `content_hash` | TEXT | Gives the filename, `content_hash \|\| '.cf'`. NULL for built-ins. | +| `artifact_index` | INTEGER | References `content_layout_binary_artifacts.artifact_index`, whose `content_hash` gives the filename (`content_hash \|\| '.cf'` — `content_layout_serialized_files_view` derives it). NULL for built-ins. | | `serialized_file` | INTEGER | [`serialized_files.id`](analyzer-schema.md#serialized_files), connecting the layout to the analyzed objects. NULL for built-ins. See [below](#analyzing-the-layout-with-or-without-the-build-content) for what the referenced row contains when the build content was not analyzed. | ### content_layout_source_assets @@ -68,14 +69,16 @@ The objects that can be loaded on demand, identified independently of the serial | Column | Type | Description | |---|---|---| -| `object_id_hash` | TEXT | Hash of the GUID, LFID and `identifier_type`. Primary key, and how `content_layout_loadable_dependencies` names a loadable. | +| `loadable_index` | INTEGER | The json array index. Primary key, and how `content_layout_loadable_dependencies` names a loadable. | | `guid` | TEXT | AssetDatabase GUID of the source asset. | -| `asset_path` | TEXT | Project path of the source asset. | -| `lfid` | INTEGER | Local file id of the object within the source asset. | +| `lfid` | INTEGER | Local file id of the object in its output serialized file when placed, otherwise the source local file id. This is the join key to the analyzed [`objects`](analyzer-schema.md#objects) (`objects.object_id`). | | `identifier_type` | INTEGER | Distinguishes the kinds of identifier a loadable can have. | | `serialized_file_index` | INTEGER | References `content_layout_serialized_files.file_index`. NULL when the object was dropped from the build (json value `-1`, e.g. server-build shader references). | -| `output_lfid` | INTEGER | Local file id of the object in its output serialized file. | -| `is_root_asset` | INTEGER | 1 for the root assets the build was made from. | +| `is_root_asset` | INTEGER | The 1-based position in the layout's `RootAssets` (the root input order); 0 when not a root asset. | +| `asset_path` | TEXT | **v2 layouts only.** Project path of the source asset. | +| `source_lfid` | INTEGER | **v2 layouts only.** Local file id of the object within the source asset. | + +The last two columns exist only in a database imported from a version 2 layout — see [Layout version differences](#layout-version-differences). ### content_layout_loadable_scenes @@ -94,7 +97,7 @@ The loadable objects each serialized file references. | Column | Type | Description | |---|---|---| | `serialized_file_index` | INTEGER | References `content_layout_serialized_files.file_index`. | -| `object_id_hash` | TEXT | References `content_layout_loadable_objects.object_id_hash`. | +| `loadable_index` | INTEGER | References `content_layout_loadable_objects.loadable_index`. | ### content_layout_loadable_scene_dependencies @@ -131,10 +134,10 @@ Both columns together are the primary key. | View | Description | |------|-------------| -| `content_layout_serialized_files_view` | One row per layout serialized file with the derived filename, its artifact size, and the core-table link (`serialized_file`, `archive`). Built-in entries show their path as the filename. | +| `content_layout_serialized_files_view` | One row per layout serialized file with its content hash, derived filename, artifact size, and the core-table link (`serialized_file`, `archive`), resolved through the artifact link. Built-in entries show their path as the filename. | | `content_layout_source_assets_view` | Source asset → the file(s) it was built into. | | `content_layout_serialized_file_dependencies_view` | The dependency edges with filenames resolved on both sides. | -| `content_layout_loadable_objects_view` | The loadables resolved to their analyzed object (`object`, `type`, `name`, `size`). | +| `content_layout_loadable_objects_view` | The loadables resolved to their analyzed object (`object`, `type`, `name`, `size`). In a database from a v2 layout it also carries the `asset_path` and `source_lfid` columns. | | `content_layout_binary_artifacts_view` | The artifacts with their derived on-disk filename (content hash + category-based extension). | | `content_layout_resource_files_view` | The `.resS`/`.resource` data files each serialized file uses, derived from the artifact graph. Note: the same resource can be referenced by multiple serialized files. | @@ -147,7 +150,18 @@ SELECT category, COUNT(*) AS count, SUM(size) AS bytes FROM content_layout_binary_artifacts GROUP BY category ORDER BY bytes DESC; ``` -Indexes are created after the tables are populated, so importing a very large layout stays fast. The `content_hash` and `asset_path` indexes carry the views; the rest serve reverse lookups such as "who depends on X" and "which loadables live in file Y". +Indexes are created after the tables are populated, so importing a very large layout stays fast. The indexes serve reverse lookups such as "who depends on X", "which loadables live in file Y" and "which artifact has this hash". + +## Layout version differences + +The [`analyze` command](command-analyze.md) accepts layout versions 2 (Unity 6.6) and 3 (Unity 6.7 and later; see [Schema versioning](contentlayout.md#schema-versioning)). A version 2 file is upgraded to the version 3 structure during import: the `.cfid` extension is stripped from the stable ids, and its hash-based references (`ObjectIdHash` strings in `LoadableDependencies` and `RootAssets`, `ContentHash` on the files) are resolved to the `loadable_index` / `artifact_index` values the tables store. The original file version stays queryable in `content_layout.version`. + +Version 2 layouts record two things about a loadable that newer versions no longer contain, and these become extra columns of `content_layout_loadable_objects` (and its view) that exist **only** in a database imported from a v2 file: + +* `asset_path` — the loadable's source asset path. In v2 builds a serialized file could combine several source assets (clustering), so the path identified which one the loadable came from. Unity 6.7 builds don't remap, and the layout only records the per-file `SourceAssets` lists. +* `source_lfid` — the object's local file id within its source asset. In v2 builds the id in the output file (`lfid`) could differ from the source id; in 6.7 output they match (MonoScripts excepted), so only `lfid` exists. + +The `ObjectIdHash` concept was removed in version 3 and is not stored for either version — loadables are keyed by their array index. ## Analyzing the layout with or without the build content @@ -155,7 +169,7 @@ A `ContentLayout.json` can be analyzed on its own — useful for running SQL que * When the build content is part of the analyzed input, each `content_layout_serialized_files` row links to its analyzed file, and the views resolve across that link (e.g. `content_layout_loadable_objects_view` shows the object, type, name and size of each loadable). * When a file was not part of the analyzed input (a layout-only analyze, or a subset of a build), its layout entry links to a placeholder `serialized_files` row instead: the row holds only the filename, with `archive` NULL and no objects. The link column is therefore always valid, and whether a file was actually analyzed is visible through its objects: `EXISTS (SELECT 1 FROM objects o WHERE o.serialized_file = f.serialized_file)`. In a layout-only database, expect NULL in the `archive` column of `content_layout_serialized_files_view` and in the `object`, `type`, `name` and `size` columns of `content_layout_loadable_objects_view`. -* Built-in entries (`is_builtin = 1`) always have a NULL `content_hash` and `serialized_file` — they are not files produced by the build. +* Built-in entries (`is_builtin = 1`) always have a NULL `artifact_index` and `serialized_file` — they are not files produced by the build. ## Related documentation diff --git a/Documentation/contentlayout.md b/Documentation/contentlayout.md index 30ce27a..981ab32 100644 --- a/Documentation/contentlayout.md +++ b/Documentation/contentlayout.md @@ -40,18 +40,27 @@ The file uses a few terms consistently: |--------|-------------| | `Version` | Schema version of the file. See [Schema versioning](#schema-versioning). | | `BuildManifestHash` | Hash of the build manifest this layout corresponds to. | -| `SerializedFiles` | One entry per serialized file in the build. Each entry records the source assets it contains, its content hash, and its dependencies on other serialized files, loadables, and loadable scenes. The same source asset can appear in more than one serialized file (for example, a single FBX file can be split into multiple output files). | -| `RootAssets` | The `ObjectIdHash` of each root asset the build was made from. Each one has a corresponding entry in `LoadableObjectIds`. | -| `LoadableObjectIds` | The objects that can be loaded on demand. Each entry records its `ObjectIdHash`, where the object lives in the built content (which serialized file) and where it came from in the source project (GUID, asset path, local file ID, and identifier type). | +| `SerializedFiles` | One entry per serialized file in the build. Each entry records its stable id (see below), the source assets it contains, the index of its artifact in `BinaryArtifacts`, and its dependencies on other serialized files, loadables, and loadable scenes. The same source asset can appear in more than one serialized file (for example, a single FBX file can be split into multiple output files). | +| `RootAssets` | The root assets the build was made from, as indices into `LoadableObjectIds`, in root input order. | +| `LoadableObjectIds` | The objects that can be loaded on demand. Each entry records where the object lives in the built content (which serialized file) and its identity (source asset GUID, local file ID, and identifier type). | | `LoadableSceneIds` | The scenes in the build, each with its source project path and GUID, and the serialized file that contains it. | | `BinaryArtifacts` | The artifacts that make up the build output. See [Binary artifacts](#binary-artifacts). | +### Stable ids + +Each serialized file has a **stable id**: an identity hash used to reference the file from other +serialized files in a way that doesn't break when its content changes (unlike the content hash, +which names the produced artifact and changes with every content change). Inside the built files, +the external reference tables list these ids with a `.cfid` extension as symbolic placeholder names — +see [Content Directory Format](contentdirectory-format.md). The synthetic built-in entry uses its +resource path (`Library/unity default resources`) as its stable id. + ### Binary artifacts `BinaryArtifacts` is essentially the list of files in the build output. Each entry has a `Category` and a `Size`, and lists its direct dependencies in `ArtifactReferences`: * The entry with category `manifest` is the root of the graph. -* Entries with category `contentfile` each have a matching entry in `SerializedFiles` (matched by content hash). +* Entries with category `contentfile` each have a matching entry in `SerializedFiles` (which references its artifact by index through `ArtifactIndex`). * `BinaryArtifacts` also reports the additional data files that hold audio, video, texture, and mesh data (the `.resource` and `.resS` files). * BinaryArtifacts are identified by the hash of their content. When saved as a file, the filename is the hash and the file extension is based on the category. @@ -61,7 +70,14 @@ The file uses a few terms consistently: The schema is subject to change. The `Version` field records the schema version of the file, independently of the Unity version that produced it. When the schema changes, the version number increments. -[`ContentLayout.cs`](../UnityDataModels/ContentLayout.cs) always represents the latest schema version (currently version 2). +[`ContentLayout.cs`](../UnityDataModels/ContentLayout.cs) always represents the latest schema version (currently version 3, written by Unity 6.7). The version 2 schema written by Unity 6.6 remains available as a reference definition in [`ContentLayoutV2.cs`](../UnityDataModels/ContentLayoutV2.cs) (namespace `UnityDataTools.Models.V2`). + +The [`analyze` command](command-analyze.md) accepts versions 2 and 3, importing both into the same database schema (see [ContentLayout in the Analyze Database](contentlayout-database.md)). Version 3 made these changes relative to version 2: + +* `SerializedFiles` entries carry `StableId` (the bare identity hash, no `.cfid` extension) instead of `ID`, and `ArtifactIndex` (the index of the file's artifact in `BinaryArtifacts`, `-1` for the built-in entry) instead of `ContentHash`. +* `LoadableDependencies` and `RootAssets` reference loadables as indices into `LoadableObjectIds` instead of `ObjectIdHash` strings, and `ObjectIdHash` itself was removed. +* `LoadableObjectIds` entries were trimmed to `{GUID, LFID, IdentifierType, SerializedFile}`. `AssetPath` and `OutputLFID` were removed: Unity 6.7 no longer remaps objects into clusters, so the local file id of an object in its output file matches the source id (MonoScripts excepted), and `LFID` now records the output-file id directly for placed objects. +* `IsBuiltIn` is only written when true. ## Related documentation diff --git a/ReferenceFinder/ReferenceFinderTool.cs b/ReferenceFinder/ReferenceFinderTool.cs index 2edc9e7..fe83f1e 100644 --- a/ReferenceFinder/ReferenceFinderTool.cs +++ b/ReferenceFinder/ReferenceFinderTool.cs @@ -201,7 +201,9 @@ FROM object_view o using var loadableCommand = m_LoadableObjectIds.Count > 0 ? db.CreateCommand() : null; if (loadableCommand != null) { - loadableCommand.CommandText = "SELECT asset_path, filename FROM content_layout_loadable_objects_view WHERE object = @id"; + // The guid is the loadable's identity in every layout version (v3 layouts no + // longer record the asset path). + loadableCommand.CommandText = "SELECT guid, filename FROM content_layout_loadable_objects_view WHERE object = @id"; loadableCommand.Parameters.Add("@id", SqliteType.Integer); } @@ -224,7 +226,7 @@ FROM object_view o if (loadableReader.Read()) { m_Writer.WriteLine("Found reference in loadable:"); - m_Writer.WriteLine(loadableReader.GetString(0)); + m_Writer.WriteLine($"GUID {loadableReader.GetString(0)}"); m_Writer.WriteLine($"(SerializedFile = {loadableReader.GetString(1)})"); } } diff --git a/TestCommon/Data/ContentLayoutVersions/v2/ContentLayout.json b/TestCommon/Data/ContentLayoutVersions/v2/ContentLayout.json new file mode 100644 index 0000000..3fad48c --- /dev/null +++ b/TestCommon/Data/ContentLayoutVersions/v2/ContentLayout.json @@ -0,0 +1 @@ +{"Version":2,"BuildManifestHash":"baff06b928d147276f2245dd3b19216a","SerializedFiles":[{"Index":0,"ID":"Library/unity default resources","IsBuiltIn":true,"SourceAssets":[],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[]},{"Index":1,"ID":"0000000000000000f000000000000000.cfid","IsBuiltIn":false,"SourceAssets":["Resources/unity_builtin_extra"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"6a60c190881f43bb4a91a360b9e8850d"},{"Index":2,"ID":"162c015549f8733449ac70ae78ad3aa5.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Scenes/Scene1.unity"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[0,1,9],"ContentHash":"c271b85494f5e4cc35c4ec4a776324af"},{"Index":3,"ID":"20408785117e138f38c2cc4ca0e638c8.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Audio/6.mp3"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"93bb3159569284a5c7bcceb4acb91bae"},{"Index":4,"ID":"4038ff673d390134d924b57fcbed0432.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/LoadableAudioClipReference.asset"],"LoadableDependencies":["20408785117e138f38c2cc4ca0e638c8","dd30e3af3b8bddf1ea36d768fc41aafd"],"LoadableSceneDependencies":[],"SerializedFileDependencies":[8],"ContentHash":"bfcf18a244e45f867750e0223a9b3a43"},{"Index":5,"ID":"52b43dad178849b42ac753005736e7bb.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/ContentDirectoryRoot.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[8,4,11,13,7],"ContentHash":"c0152db4dd710be51b2decb997325f34"},{"Index":6,"ID":"590cfeb4e0ff90f4e92f9e1262bcfe6f.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Scenes/Scene2.unity"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[0,1,9],"ContentHash":"4d49f1aa2acf2ecf20f51660e1bf77ea"},{"Index":7,"ID":"70a210050f71a924aa83be7146547111.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/SceneList.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":["Assets/Scenes/Scene2.unity","Assets/Scenes/Scene1.unity"],"SerializedFileDependencies":[8],"ContentHash":"8ad4924ac5e264fde63d8e95a0dab8ab"},{"Index":8,"ID":"a2a42d71dddef12e8889849faf59bdd7.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Scripts/DirectScriptableObjectReference.cs","Assets/Scripts/LoadableAudioClipReference.cs","Assets/Scripts/SceneList.cs","Assets/Scripts/SerializationDemo.cs"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"86d71ff2bb38e064697257d35d6421b8"},{"Index":9,"ID":"baf46637edbb6ff884858f6630b66eda.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Textures/GreenStatic.png"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[12],"ContentHash":"61fa28f6b22b739b19ece22ea0b3f29f"},{"Index":10,"ID":"dd30e3af3b8bddf1ea36d768fc41aafd.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Audio/a.mp3"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"730d2d641a53eeb1e633f2ff60d730e9"},{"Index":11,"ID":"21679be819d6e9146a63bb02a7e51f2f.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/SingleAudioClipLoadableReference.asset"],"LoadableDependencies":["dd30e3af3b8bddf1ea36d768fc41aafd"],"LoadableSceneDependencies":[],"SerializedFileDependencies":[8],"ContentHash":"5c43454a3823f172a2a326410a36ba6b"},{"Index":12,"ID":"34814583296b527bf650f548effcd62c.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Textures/GreenStatic.png"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"c046f78347acd69f03fa7f80362fde94"},{"Index":13,"ID":"78532141fd7679a458405eb16bdb75fd.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/SerializationDemo.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[8],"ContentHash":"51690f09f541d7442ce8faf26fd84696"}],"RootAssets":["3dcb957e6cb206bfec2aaadd3d11d85c"],"LoadableObjectIds":[{"ObjectIdHash":"20408785117e138f38c2cc4ca0e638c8","GUID":"278c261333bf8604eb5c83790d02004d","AssetPath":"Assets/Audio/6.mp3","LFID":8300000,"IdentifierType":3,"SerializedFile":3,"OutputLFID":6313987888022012634},{"ObjectIdHash":"3dcb957e6cb206bfec2aaadd3d11d85c","GUID":"52b43dad178849b42ac753005736e7bb","AssetPath":"Assets/ScriptableObjects/ContentDirectoryRoot.asset","LFID":11400000,"IdentifierType":2,"SerializedFile":5,"OutputLFID":-775554941117088049},{"ObjectIdHash":"dd30e3af3b8bddf1ea36d768fc41aafd","GUID":"b65a7916245593b4e89f4bd0aa920533","AssetPath":"Assets/Audio/a.mp3","LFID":8300000,"IdentifierType":3,"SerializedFile":10,"OutputLFID":-6933612096100796476}],"LoadableSceneIds":[{"GUID":"590cfeb4e0ff90f4e92f9e1262bcfe6f","Path":"Assets/Scenes/Scene2.unity","SerializedFile":6},{"GUID":"162c015549f8733449ac70ae78ad3aa5","Path":"Assets/Scenes/Scene1.unity","SerializedFile":2}],"BinaryArtifacts":[{"Index":0,"ContentHash":"6a60c190881f43bb4a91a360b9e8850d","Category":"contentfile","Size":96364,"ArtifactReferences":[1]},{"Index":1,"ContentHash":"ea7c9fa2d58ef71544726747da8ade6d","Category":"texture","Size":524280},{"Index":2,"ContentHash":"c271b85494f5e4cc35c4ec4a776324af","Category":"contentfile","Size":17572},{"Index":3,"ContentHash":"93bb3159569284a5c7bcceb4acb91bae","Category":"contentfile","Size":1288,"ArtifactReferences":[4]},{"Index":4,"ContentHash":"68c9d0b12420e1951eec7790bd0754fe","Category":"audio","Size":30688},{"Index":5,"ContentHash":"bfcf18a244e45f867750e0223a9b3a43","Category":"contentfile","Size":2184},{"Index":6,"ContentHash":"c0152db4dd710be51b2decb997325f34","Category":"contentfile","Size":1524},{"Index":7,"ContentHash":"4d49f1aa2acf2ecf20f51660e1bf77ea","Category":"contentfile","Size":17572},{"Index":8,"ContentHash":"8ad4924ac5e264fde63d8e95a0dab8ab","Category":"contentfile","Size":1304},{"Index":9,"ContentHash":"86d71ff2bb38e064697257d35d6421b8","Category":"contentfile","Size":2012},{"Index":10,"ContentHash":"61fa28f6b22b739b19ece22ea0b3f29f","Category":"contentfile","Size":8516},{"Index":11,"ContentHash":"730d2d641a53eeb1e633f2ff60d730e9","Category":"contentfile","Size":1288,"ArtifactReferences":[12]},{"Index":12,"ContentHash":"4226b5c16a50dab6eff0f08dd1253d4b","Category":"audio","Size":47424},{"Index":13,"ContentHash":"5c43454a3823f172a2a326410a36ba6b","Category":"contentfile","Size":2136},{"Index":14,"ContentHash":"c046f78347acd69f03fa7f80362fde94","Category":"contentfile","Size":2260,"ArtifactReferences":[15]},{"Index":15,"ContentHash":"f0a44ad4a4babd121543fd44032928e7","Category":"texture","Size":215970},{"Index":16,"ContentHash":"51690f09f541d7442ce8faf26fd84696","Category":"contentfile","Size":4812},{"Index":17,"ContentHash":"baff06b928d147276f2245dd3b19216a","Category":"manifest","Size":2858,"ArtifactReferences":[0,2,3,5,6,7,8,9,10,11,13,14,16]}]} \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildContentTEP.json b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildContentTEP.json index 3604806..ec94edf 100644 --- a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildContentTEP.json +++ b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildContentTEP.json @@ -3,206 +3,124 @@ "Instructions": "1) Open Chrome or Edge, 2) go to chrome://tracing, 3) click Load, 4) navigate to this file.", -"UnityVersion": "6000.6.0b5", -"OperatingSystem": "Windows 11 (10.0.26200) Professional", -"ProcessorType": "12th Gen Intel(R) Core(TM) i9-12900K", -"ProcessorFrequencyMHz": 3187, -"ProcessorCount": 24, -"PhysicalProcessorCount": 16, -"PhysicalMemoryMB": 65277, +"UnityVersion": "6000.7.0b3", +"OperatingSystem": "Windows 11 (10.0.26100) Professional", +"ProcessorType": "11th Gen Intel(R) Core(TM) i9-11950H @ 2.60GHz", +"ProcessorFrequencyMHz": 2611, +"ProcessorCount": 16, +"PhysicalProcessorCount": 8, +"PhysicalMemoryMB": 65239, "traceEvents": [ -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362598077, "dur": 34236, "name": "ApplyBuildHistoryRetention"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362632316, "dur": 7444502, "name": "UnifiedBuild"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362632361, "dur": 43, "name": "Validate Root Assets"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362635968, "dur": 38037, "name": "InitializeBuildCallbacks:BuildProcessors, SceneProcessors, FilterAssembliesProcessors, ShaderProcessors, ComputeShaderProcessors"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362674023, "dur": 17, "name": "OnBuildStepBegin", "args": {"step": "Refresh Asset Database", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362674340, "dur": 71987, "name": "BuildPipeline::RegisterCustomDependencies"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362746611, "dur": 248, "name": "OnBuildStepEnd", "args": {"step": "Refresh Asset Database", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362746897, "dur": 13, "name": "OnBuildStepBegin", "args": {"step": "Call Pre-Process callbacks", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362747701, "dur": 106, "name": "ContentSelectionBuildPreprocessor.OnPreprocessBuild:WithContext"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362747816, "dur": 7, "name": "OnBuildStepEnd", "args": {"step": "Call Pre-Process callbacks", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362751163, "dur": 58703, "name": "ConfigureTargetPlatform"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362751166, "dur": 11, "name": "OnBuildStepBegin", "args": {"step": "Configure target platform", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362755616, "dur": 54191, "name": "CompressAssets"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362809813, "dur": 24, "name": "OnBuildStepEnd", "args": {"step": "Configure target platform", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362809868, "dur": 1564153, "name": "ReCompileScripts"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367362809874, "dur": 13, "name": "OnBuildStepBegin", "args": {"step": "Compile scripts", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367364373957, "dur": 27, "name": "OnBuildStepEnd", "args": {"step": "Compile scripts", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367364374447, "dur": 12585, "name": "LoadingTypeDB"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367364387044, "dur": 292487, "name": "DeleteCachedBuildArtifacts"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367364679545, "dur": 4805694, "name": "GatherBuildMetaData"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367364679561, "dur": 22, "name": "OnBuildStepBegin", "args": {"step": "Gather and collect build metadata", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367364679845, "dur": 4805096, "name": "BuildPipeline::GatherAllAssetMetadata"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369484945, "dur": 10, "name": "BuildPipeline::GatherMainObjectIdsForInitialAssets"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369484958, "dur": 46, "name": "BuildPipeline::GatherBuiltInObjectIds"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369485008, "dur": 74, "name": "BuildPipeline::BuildObjectIdToGlobalObjectIdMap"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369485084, "dur": 50, "name": "BuildPipeline::BuildObjectIdToObjectBuildMetaDataMap"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369485138, "dur": 17, "name": "BuildPipeline::CombineGlobalUsageFromMetadata"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369485192, "dur": 20, "name": "OnBuildStepEnd", "args": {"step": "Gather and collect build metadata", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369485246, "dur": 11, "name": "OnBuildStepBegin", "args": {"step": "Calculate build layout", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369485546, "dur": 154, "name": "BuildLayout"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369485702, "dur": 7, "name": "OnBuildStepEnd", "args": {"step": "Calculate build layout", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369485727, "dur": 8, "name": "OnBuildStepBegin", "args": {"step": "Aggregate build usage for objects", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486002, "dur": 17, "name": "BuildPipeline::AggregateBuildUsageForObjectIds"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486020, "dur": 5, "name": "OnBuildStepEnd", "args": {"step": "Aggregate build usage for objects", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486042, "dur": 5, "name": "OnBuildStepBegin", "args": {"step": "Generate build instructions", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486213, "dur": 196, "name": "BuildPipeline::BuildInstructionHelper::GenerateBuildInstructions"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486220, "dur": 29, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486261, "dur": 30, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486295, "dur": 3, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486302, "dur": 8, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486316, "dur": 12, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486334, "dur": 16, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486354, "dur": 6, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486364, "dur": 5, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486372, "dur": 5, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486380, "dur": 3, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486387, "dur": 6, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486396, "dur": 3, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486403, "dur": 5, "name": "GenerateBuildInstruction"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486432, "dur": 92721, "name": "SerializeInstructions"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486438, "dur": 8, "name": "OnBuildStepBegin", "args": {"step": "Serialize build instructions", "depth": "2"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486639, "dur": 4466, "name": "SerializeBuildInstructions", "args": {"Count": "13", "changedCount": "13", "unchangedCount": "0", "bytesWritten": "8288", "bytesHashed": "8288"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486640, "dur": 49, "name": "InstructionSet:LoadVersions"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486690, "dur": 6, "name": "InstructionSet:HashInstructions", "args": {"totalBytesHashed": "8288"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486698, "dur": 41, "name": "InstructionSet:PrepareWriteTasks", "args": {"taskCount": "13", "bytesWritten": "8288"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369486740, "dur": 4098, "name": "InstructionSet:WriteChangedInstructions", "args": {"changedCount": "13", "bytesWritten": "8288"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369490840, "dur": 234, "name": "InstructionSet:UpdateVersionMap"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369491106, "dur": 87994, "name": "AssetDatabase Refresh"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579106, "dur": 17, "name": "OnBuildStepEnd", "args": {"step": "Serialize build instructions", "depth": "2"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579154, "dur": 3, "name": "OnBuildStepEnd", "args": {"step": "Generate build instructions", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579184, "dur": 8, "name": "OnBuildStepBegin", "args": {"step": "Execute instructions", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579496, "dur": 444475, "name": "ExecuteBuildInstructions"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579502, "dur": 444468, "name": "BuildPipeline::BuildInstructionHelper::ProduceSerializedFilesForBuildInstructions"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579503, "dur": 49, "name": "Build Instructions to GUIDs"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579553, "dur": 12, "name": "Filter Up to Date Build Instructions"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579566, "dur": 343033, "name": "Precompile Shaders From Build Instructions"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579568, "dur": 8, "name": "OnBuildStepBegin", "args": {"step": "Precompile Shaders", "depth": "2"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369579840, "dur": 342697, "name": "BuildPipeline::PreCompileShadersFromInstructions"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369580263, "dur": 342246, "name": "BuildPipeline::PreCompileShaders"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369580283, "dur": 342214, "name": "Compiling Shader", "args": {"Shader": "Sprites/Default"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369602565, "dur": 8652, "name": "OnPreprocessShaderVariants", "args": {"shader": "Sprites/Default"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369611626, "dur": 199, "name": "OnPreprocessShaderVariants", "args": {"shader": "Sprites/Default"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369612128, "dur": 194, "name": "OnPreprocessShaderVariants", "args": {"shader": "Sprites/Default"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369612567, "dur": 180, "name": "OnPreprocessShaderVariants", "args": {"shader": "Sprites/Default"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369922544, "dur": 25, "name": "OnBuildStepEnd", "args": {"step": "Precompile Shaders", "depth": "2"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369922602, "dur": 14, "name": "OnBuildStepBegin", "args": {"step": "Write serialized files", "depth": "2"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367369922910, "dur": 101013, "name": "Write serialized files"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370023928, "dur": 16, "name": "OnBuildStepEnd", "args": {"step": "Write serialized files", "depth": "2"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370023972, "dur": 3, "name": "OnBuildStepEnd", "args": {"step": "Execute instructions", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370023984, "dur": 1131, "name": "BuildPipeline::BuildInstructionHelper::PopulateContentFileBuildResults"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370025121, "dur": 13, "name": "OnBuildStepBegin", "args": {"step": "Generate build manifest", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370025432, "dur": 161, "name": "BuildPipeline::CreateManifestJsonText"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370025594, "dur": 99, "name": "WriteBuildManifestToUDS"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370025724, "dur": 6, "name": "OnBuildStepEnd", "args": {"step": "Generate build manifest", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370025744, "dur": 16437, "name": "BuildPipeline::ExportPlayerArtifactsToDirectory"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370042255, "dur": 31, "name": "OnBuildStepBegin", "args": {"step": "Clean output directory", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370042675, "dur": 844, "name": "BuildPipeline::CleanupUnreferencedFilesFromBuildOutput"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370043525, "dur": 22, "name": "OnBuildStepEnd", "args": {"step": "Clean output directory", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370044119, "dur": 2573, "name": "PopulateBuildReport"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370044126, "dur": 23, "name": "OnBuildStepBegin", "args": {"step": "Populate build report", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370044532, "dur": 2027, "name": "BuildPipeline::CalculateAndRecordContentSummary"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370046649, "dur": 16, "name": "OnBuildStepEnd", "args": {"step": "Populate build report", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370046698, "dur": 11, "name": "OnBuildStepBegin", "args": {"step": "Record type usage", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370046975, "dur": 5926, "name": "BuildPipeline::WriteTypeUsage"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370047732, "dur": 1663, "name": "BuildPipeline::PopulateRuntimeClassRegistry"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370052908, "dur": 20, "name": "OnBuildStepEnd", "args": {"step": "Record type usage", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370052970, "dur": 8, "name": "OnBuildStepBegin", "args": {"step": "Generate build layout", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370053283, "dur": 348, "name": "BuildPipeline::CreateLayoutJsonText"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370054085, "dur": 13, "name": "OnBuildStepEnd", "args": {"step": "Generate build layout", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370054366, "dur": 13, "name": "OnBuildStepBegin", "args": {"step": "Finalize ContentBuildRoot", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370054757, "dur": 6, "name": "OnBuildStepEnd", "args": {"step": "Finalize ContentBuildRoot", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370054779, "dur": 0, "name": "ResetActiveTargetPlatform"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370054781, "dur": 8607, "name": "BuildPipeline::CleanupAfterBuild"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370054782, "dur": 6, "name": "OnBuildStepBegin", "args": {"step": "Clean up after build", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370063334, "dur": 15, "name": "OnBuildStepEnd", "args": {"step": "Clean up after build", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370063390, "dur": 10762, "name": "Unloading unused assets immediately"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370063393, "dur": 8, "name": "OnBuildStepBegin", "args": {"step": "Unloading unused assets", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370074105, "dur": 22, "name": "OnBuildStepEnd", "args": {"step": "Unloading unused assets", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370076813, "dur": 1, "name": "ResetActiveTargetPlatform"}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370076822, "dur": 16, "name": "OnBuildStepBegin", "args": {"step": "Call Post-Process callbacks", "depth": "1"}}, -{"pid": 69268, "tid": 67564, "ph": "X", "ts": 367370077629, "dur": 8, "name": "OnBuildStepEnd", "args": {"step": "Call Post-Process callbacks", "depth": "1"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368569908, "dur": 36341, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/ContentDirectoryRoot.asset"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368569990, "dur": 15153, "name": "LoadingTypeDB"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368585199, "dur": 20723, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368620401, "dur": 2803, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/SceneList.asset"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368620488, "dur": 2522, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368630899, "dur": 2304, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/LoadableAudioClipReference.asset"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368631008, "dur": 2052, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368635069, "dur": 2965, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/SerializationDemo.asset"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368635173, "dur": 2695, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368639960, "dur": 4252, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Audio/a.mp3"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368640015, "dur": 4049, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368645709, "dur": 254483, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Scenes/Scene2.unity"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368652566, "dur": 4569, "name": "InitializeBuildCallbacks:SceneProcessors"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368657205, "dur": 55295, "name": "BuildPipeline::CalculateBuildMetaDataForScene"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368699911, "dur": 1934, "name": "UGUIBuildAnalytics.OnProcessScene:WithReport:Scene2"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368701870, "dur": 3614, "name": "TimelineAnalyticsProcess.OnProcessScene:WithReport:Scene2"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368705508, "dur": 671, "name": "MuiltiplayerRolesScenePostprocessor.OnProcessScene:WithReport:Scene2"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368706196, "dur": 2011, "name": "UnityBuildPostprocessor.OnProcessScene:WithReport:Scene2"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368914029, "dur": 486, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Scripts/SceneList.cs"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368914139, "dur": 239, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368916085, "dur": 562, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Scripts/SerializationDemo.cs"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368916160, "dur": 245, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368918359, "dur": 2405, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Textures/GreenStatic.png"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368918510, "dur": 1919, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368922591, "dur": 49503, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/0000000000000000e000000000000000.metadataimportproxy"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368922667, "dur": 49117, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368974934, "dur": 394, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Scripts/DirectScriptableObjectReference.cs"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368975008, "dur": 198, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368976516, "dur": 382697, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/0000000000000000f000000000000000.metadataimportproxy"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367368976577, "dur": 382386, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369360749, "dur": 1514, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/SingleAudioClipLoadableReference.asset"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369360818, "dur": 1316, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369363654, "dur": 13755, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Scenes/Scene1.unity"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369363742, "dur": 92, "name": "InitializeBuildCallbacks:SceneProcessors"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369363874, "dur": 9393, "name": "BuildPipeline::CalculateBuildMetaDataForScene"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369371340, "dur": 12, "name": "UGUIBuildAnalytics.OnProcessScene:WithReport:Scene1"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369371360, "dur": 25, "name": "TimelineAnalyticsProcess.OnProcessScene:WithReport:Scene1"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369371390, "dur": 8, "name": "MuiltiplayerRolesScenePostprocessor.OnProcessScene:WithReport:Scene1"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369371402, "dur": 43, "name": "UnityBuildPostprocessor.OnProcessScene:WithReport:Scene1"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369478448, "dur": 870, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Audio/6.mp3"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369478550, "dur": 670, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369480842, "dur": 329, "name": "BuildMetaDataImporter(worker)", "args": {"assetPath": "Assets/Scripts/LoadableAudioClipReference.cs"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369480906, "dur": 164, "name": "BuildPipeline::CalculateBuildMetaDataForAsset"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369925282, "dur": 6406, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/21679be819d6e9146a63bb02a7e51f2f.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369925403, "dur": 6240, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/21679be819d6e9146a63bb02a7e51f2f.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369925632, "dur": 722, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369933892, "dur": 1565, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/4038ff673d390134d924b57fcbed0432.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369934003, "dur": 1419, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/4038ff673d390134d924b57fcbed0432.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369934044, "dur": 375, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369937330, "dur": 10975, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/590cfeb4e0ff90f4e92f9e1262bcfe6f.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369937439, "dur": 8824, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/590cfeb4e0ff90f4e92f9e1262bcfe6f.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369944428, "dur": 12, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369957909, "dur": 2040, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/34814583296b527bf650f548effcd62c.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369958063, "dur": 1842, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/34814583296b527bf650f548effcd62c.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369958084, "dur": 483, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369961954, "dur": 3038, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/52b43dad178849b42ac753005736e7bb.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369962061, "dur": 2894, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/52b43dad178849b42ac753005736e7bb.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369962142, "dur": 1390, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369966962, "dur": 1838, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/baf46637edbb6ff884858f6630b66eda.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369967066, "dur": 1699, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/baf46637edbb6ff884858f6630b66eda.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369967083, "dur": 481, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369970678, "dur": 6548, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/0000000000000000f000000000000000.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369970800, "dur": 6348, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/0000000000000000f000000000000000.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369970814, "dur": 7, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369979323, "dur": 1741, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/20408785117e138f38c2cc4ca0e638c8.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369979434, "dur": 1596, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/20408785117e138f38c2cc4ca0e638c8.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369979474, "dur": 476, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369982756, "dur": 1623, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/a2a42d71dddef12e8889849faf59bdd7.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369982863, "dur": 1477, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/a2a42d71dddef12e8889849faf59bdd7.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369982881, "dur": 9, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369986184, "dur": 1620, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/70a210050f71a924aa83be7146547111.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369986298, "dur": 1472, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/70a210050f71a924aa83be7146547111.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369986324, "dur": 387, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369989676, "dur": 9586, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/162c015549f8733449ac70ae78ad3aa5.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369989789, "dur": 7521, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/162c015549f8733449ac70ae78ad3aa5.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367369996158, "dur": 12, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367370009818, "dur": 1787, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/dd30e3af3b8bddf1ea36d768fc41aafd.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367370009956, "dur": 1611, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/dd30e3af3b8bddf1ea36d768fc41aafd.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367370010006, "dur": 427, "name": "BuildPipeline::PopulateWriteData"}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367370013464, "dur": 2861, "name": "BuildInstructionImporter(worker)", "args": {"assetPath": "Library/BuildInstructions/78532141fd7679a458405eb16bdb75fd.buildinst"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367370013572, "dur": 2713, "name": "BuildPipeline::WriteSerializedFile", "args": {"filePath": "mem:/Temp/78532141fd7679a458405eb16bdb75fd.cfid"}}, -{"pid": 60488, "tid": 12660, "ph": "X", "ts": 367370013598, "dur": 429, "name": "BuildPipeline::PopulateWriteData"} +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268473364, "dur": 21661, "name": "ApplyBuildHistoryRetention"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268495033, "dur": 11415267, "name": "UnifiedBuild"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268495094, "dur": 2516, "name": "Validate Root Assets"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268502215, "dur": 26011, "name": "InitializeBuildCallbacks:BuildProcessors, SceneProcessors, FilterAssembliesProcessors, ShaderProcessors, ComputeShaderProcessors"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268528249, "dur": 36, "name": "OnBuildStepBegin", "args": {"step": "Refresh Asset Database", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268528603, "dur": 31373, "name": "BuildPipeline::RegisterCustomDependencies"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268560194, "dur": 232, "name": "OnBuildStepEnd", "args": {"step": "Refresh Asset Database", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268560477, "dur": 9, "name": "OnBuildStepBegin", "args": {"step": "Call Pre-Process callbacks", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268561391, "dur": 23102, "name": "ContentSelectionBuildPreprocessor.OnPreprocessBuild:WithContext"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268584514, "dur": 19, "name": "OnBuildStepEnd", "args": {"step": "Call Pre-Process callbacks", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268584615, "dur": 43479, "name": "ConfigureTargetPlatform"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268584620, "dur": 11, "name": "OnBuildStepBegin", "args": {"step": "Configure target platform", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268587752, "dur": 40282, "name": "CompressAssets"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268628041, "dur": 16, "name": "OnBuildStepEnd", "args": {"step": "Configure target platform", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268628098, "dur": 3777327, "name": "ReCompileScripts"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268628106, "dur": 13, "name": "OnBuildStepBegin", "args": {"step": "Compile scripts", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821268628468, "dur": 374265, "name": "CompilePlayerScripts:EditorPreRecompile"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821269003631, "dur": 2893000, "name": "CompilePlayerScripts:CompilePlayerAssemblies"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821271896905, "dur": 479335, "name": "CompilePlayerScripts:ExtractEditorTypeDB"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821272376532, "dur": 16646, "name": "GenerateAssemblyTypeInfos:Editor"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821272393193, "dur": 7640, "name": "GenerateAssemblyTypeInfos:Player"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821272400855, "dur": 2, "name": "PostProcessAssemblyTypeInfos"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821272405357, "dur": 25, "name": "OnBuildStepEnd", "args": {"step": "Compile scripts", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821272468167, "dur": 2414, "name": "LoadingTypeDB"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821272470592, "dur": 93, "name": "DeleteCachedBuildArtifacts"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821272483052, "dur": 21, "name": "OnBuildStepBegin", "args": {"step": "Single-pass import", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821272483340, "dur": 6951949, "name": "SinglePassImport", "args": {"assetsProcessed": "10", "cached": "0", "uncached": "10"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279435295, "dur": 12, "name": "OnBuildStepEnd", "args": {"step": "Single-pass import", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279435330, "dur": 109, "name": "PruneUnreachableContentFiles"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279435441, "dur": 19, "name": "Aggregate shader usage"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279435464, "dur": 14, "name": "OnBuildStepBegin", "args": {"step": "Deferred shader pass", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279435712, "dur": 1, "name": "BuildPipeline::RunSinglePassDeferredShaderPass"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279435715, "dur": 11, "name": "OnBuildStepEnd", "args": {"step": "Deferred shader pass", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279435741, "dur": 3838, "name": "BuildMonoScriptContentFile"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279439585, "dur": 14, "name": "OnBuildStepBegin", "args": {"step": "Build builtin-extra content file", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279439827, "dur": 394616, "name": "BuildBuiltinExtraContentFile"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279465505, "dur": 8747, "name": "OnPreprocessShaderVariants", "args": {"shader": "Sprites/Default"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279474696, "dur": 216, "name": "OnPreprocessShaderVariants", "args": {"shader": "Sprites/Default"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279475264, "dur": 190, "name": "OnPreprocessShaderVariants", "args": {"shader": "Sprites/Default"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279475789, "dur": 215, "name": "OnPreprocessShaderVariants", "args": {"shader": "Sprites/Default"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279834450, "dur": 17, "name": "OnBuildStepEnd", "args": {"step": "Build builtin-extra content file", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279834492, "dur": 99, "name": "PruneUnreachableContentFiles"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279834602, "dur": 16, "name": "ValidateLoadables"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279834620, "dur": 19, "name": "BuildPipeline::BuildInstructionHelper::PopulateContentFileBuildResults"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279834630, "dur": 9, "name": "Sort Content File Build Results"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279834711, "dur": 16, "name": "OnBuildStepBegin", "args": {"step": "Generate build manifest", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279834985, "dur": 179, "name": "BuildPipeline::CreateManifestJsonText"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279835164, "dur": 123, "name": "WriteBuildManifestToUDS"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279835331, "dur": 8, "name": "OnBuildStepEnd", "args": {"step": "Generate build manifest", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279835371, "dur": 7523, "name": "BuildPipeline::ExportPlayerArtifactsToDirectory", "args": {"artifactCount": "18", "force": "true", "filesCopied": "18", "bytesCopied": "920270", "filesCached": "0", "bytesCached": "0"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279835379, "dur": 40, "name": "ExportPlayerArtifacts:BuildExportList"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279835428, "dur": 7453, "name": "BuildPipeline::ExportArtifactFilesToDirectory", "args": {"fileCount": "18", "force": "true", "filesCopied": "18", "bytesCopied": "920270", "filesCached": "0", "bytesCached": "0"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279842981, "dur": 31, "name": "OnBuildStepBegin", "args": {"step": "Clean output directory", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279843301, "dur": 2435, "name": "BuildPipeline::CleanupUnreferencedFilesFromBuildOutput"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279845741, "dur": 13, "name": "OnBuildStepEnd", "args": {"step": "Clean output directory", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279846028, "dur": 1648, "name": "PopulateBuildReport"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279846033, "dur": 16, "name": "OnBuildStepBegin", "args": {"step": "Populate build report", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279846284, "dur": 1239, "name": "BuildPipeline::CalculateAndRecordContentSummary"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279847644, "dur": 11, "name": "OnBuildStepEnd", "args": {"step": "Populate build report", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279847678, "dur": 8, "name": "OnBuildStepBegin", "args": {"step": "Record type usage", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279847876, "dur": 9848, "name": "BuildPipeline::WriteTypeUsage"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279848752, "dur": 6259, "name": "BuildPipeline::PopulateRuntimeClassRegistry"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279857729, "dur": 12, "name": "OnBuildStepEnd", "args": {"step": "Record type usage", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279857769, "dur": 9, "name": "OnBuildStepBegin", "args": {"step": "Generate build layout", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279857954, "dur": 374, "name": "BuildPipeline::CreateLayoutJsonText"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279858618, "dur": 301, "name": "Validate Circular References"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279858645, "dur": 46, "name": "Detect Circular References"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279858696, "dur": 219, "name": "Write Circular References Report"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279858925, "dur": 11, "name": "OnBuildStepEnd", "args": {"step": "Generate build layout", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279859060, "dur": 9, "name": "OnBuildStepBegin", "args": {"step": "Finalize ContentBuildRoot", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279859587, "dur": 11, "name": "OnBuildStepEnd", "args": {"step": "Finalize ContentBuildRoot", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279859624, "dur": 0, "name": "ResetActiveTargetPlatform"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279859626, "dur": 966, "name": "BuildPipeline::CleanupAfterBuild"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279859628, "dur": 8, "name": "OnBuildStepBegin", "args": {"step": "Clean up after build", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279860567, "dur": 6, "name": "OnBuildStepEnd", "args": {"step": "Clean up after build", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279860594, "dur": 48802, "name": "Unloading unused assets immediately"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279860596, "dur": 5, "name": "OnBuildStepBegin", "args": {"step": "Unloading unused assets", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279909316, "dur": 33, "name": "OnBuildStepEnd", "args": {"step": "Unloading unused assets", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279910294, "dur": 2, "name": "ResetActiveTargetPlatform"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279910327, "dur": 20, "name": "OnBuildStepBegin", "args": {"step": "Call Post-Process callbacks", "depth": "1"}}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279911860, "dur": 455, "name": "TextCorePostBuildProcessor.OnPostprocessBuild:WithContext"}, +{"pid": 26788, "tid": 27868, "ph": "X", "ts": 1821279912332, "dur": 19, "name": "OnBuildStepEnd", "args": {"step": "Call Post-Process callbacks", "depth": "1"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279000269, "dur": 69492, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/ContentDirectoryRoot.asset"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279000692, "dur": 4276, "name": "LoadingTypeDB"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279023609, "dur": 19009, "name": "BuildPipeline::GatherSinglePassAssetObjects"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279042648, "dur": 26998, "name": "WriteAssetContentFiles"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279086091, "dur": 6121, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/SerializationDemo.asset"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279086366, "dur": 1238, "name": "BuildPipeline::GatherSinglePassAssetObjects"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279087611, "dur": 4509, "name": "WriteAssetContentFiles"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279094839, "dur": 6327, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/SingleAudioClipLoadableReference.asset"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279095178, "dur": 1396, "name": "BuildPipeline::GatherSinglePassAssetObjects"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279096580, "dur": 4547, "name": "WriteAssetContentFiles"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279103055, "dur": 3930, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/SceneList.asset"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279103372, "dur": 1643, "name": "BuildPipeline::GatherSinglePassAssetObjects"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279105022, "dur": 1920, "name": "WriteAssetContentFiles"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279108771, "dur": 3382, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/Audio/a.mp3"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279108991, "dur": 1814, "name": "BuildPipeline::GatherSinglePassAssetObjects"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279110810, "dur": 1297, "name": "WriteAssetContentFiles"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279113657, "dur": 3473, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/ScriptableObjects/LoadableAudioClipReference.asset"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279113896, "dur": 968, "name": "BuildPipeline::GatherSinglePassAssetObjects"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279114879, "dur": 2217, "name": "WriteAssetContentFiles"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279118946, "dur": 245046, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/Scenes/Scene1.unity"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279123081, "dur": 1533, "name": "InitializeBuildCallbacks:SceneProcessors"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279375795, "dur": 24672, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/Scenes/Scene2.unity"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279376073, "dur": 92, "name": "InitializeBuildCallbacks:SceneProcessors"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279411224, "dur": 10706, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/Textures/GreenStatic.png"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279411503, "dur": 7757, "name": "BuildPipeline::GatherSinglePassAssetObjects"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279419266, "dur": 2625, "name": "WriteAssetContentFiles"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279423522, "dur": 1689, "name": "SinglePassBuildImporter(worker)", "args": {"assetPath": "Assets/Audio/6.mp3"}}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279423760, "dur": 624, "name": "BuildPipeline::GatherSinglePassAssetObjects"}, +{"pid": 28108, "tid": 31520, "ph": "X", "ts": 1821279424388, "dur": 786, "name": "WriteAssetContentFiles"} ] } \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildLog.jsonl b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildLog.jsonl index 018a102..f4fb14e 100644 --- a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildLog.jsonl +++ b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildLog.jsonl @@ -1,46 +1,35 @@ -{"timestamp":"2026-07-14T18:37:14.230563100Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"BuildStart\",\"rootAssetCount\":1,\"startTime\":\"2026-07-14T18:37:14.2095142Z\",\"targetPlatform\":19,\"subtarget\":2,\"metadataPath\":\"Library/BuildHistory/20260714-183714Z-f64157fb\",\"outputPath\":\"C:/UnitySrc/UnityDataTools/UnityProjects/LeadingEdge/../../TestCommon/Data/LeadingEdgeBuilds/ContentDirectory\",\"rootAssetPaths\":[\"Assets/ScriptableObjects/ContentDirectoryRoot.asset\"]}"} -{"timestamp":"2026-07-14T18:37:14.307101700Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Refresh Asset Database\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:14.379914600Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Refresh Asset Database\",\"depth\":1,\"durationTicks\":723116}"} -{"timestamp":"2026-07-14T18:37:14.379954000Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Call Pre-Process callbacks\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:14.380867700Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Call Pre-Process callbacks\",\"depth\":1,\"durationTicks\":9190}"} -{"timestamp":"2026-07-14T18:37:14.384219100Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Configure target platform\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:14.442890200Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Configure target platform\",\"depth\":1,\"durationTicks\":586438}"} -{"timestamp":"2026-07-14T18:37:14.442923300Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Compile scripts\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:16.007045100Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Compile scripts\",\"depth\":1,\"durationTicks\":15640772}"} -{"timestamp":"2026-07-14T18:37:16.312658400Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Gather and collect build metadata\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:20.600853000Z","thread_id":"67564","log_level":"Warning","message":"[Worker0] Fallback fonts are not supported in content directory builds. Skipping fallback fonts for font LegacyRuntime."} -{"timestamp":"2026-07-14T18:37:21.118264900Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Gather and collect build metadata\",\"depth\":1,\"durationTicks\":48056324}"} -{"timestamp":"2026-07-14T18:37:21.118294500Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Calculate build layout\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.118751100Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Calculate build layout\",\"depth\":1,\"durationTicks\":4561}"} -{"timestamp":"2026-07-14T18:37:21.118771700Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Aggregate build usage for objects\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.119066000Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Aggregate build usage for objects\",\"depth\":1,\"durationTicks\":2929}"} -{"timestamp":"2026-07-14T18:37:21.119082700Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Generate build instructions\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.119485600Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Serialize build instructions\",\"depth\":2}"} -{"timestamp":"2026-07-14T18:37:21.212177400Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Serialize build instructions\",\"depth\":2,\"durationTicks\":926677}"} -{"timestamp":"2026-07-14T18:37:21.212194500Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Generate build instructions\",\"depth\":1,\"durationTicks\":931121}"} -{"timestamp":"2026-07-14T18:37:21.212228900Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Execute instructions\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.212616700Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Precompile Shaders\",\"depth\":2}"} -{"timestamp":"2026-07-14T18:37:21.555624500Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Precompile Shaders\",\"depth\":2,\"durationTicks\":3429711}"} -{"timestamp":"2026-07-14T18:37:21.555652700Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Write serialized files\",\"depth\":2}"} -{"timestamp":"2026-07-14T18:37:21.656995800Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Write serialized files\",\"depth\":2,\"durationTicks\":1013235}"} -{"timestamp":"2026-07-14T18:37:21.657011800Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Execute instructions\",\"depth\":1,\"durationTicks\":4447880}"} -{"timestamp":"2026-07-14T18:37:21.658176900Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Generate build manifest\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.658769600Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Generate build manifest\",\"depth\":1,\"durationTicks\":6036}"} -{"timestamp":"2026-07-14T18:37:21.675268600Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"BuildStatistic\",\"context\":\"BuildOutput\",\"statistics\":[{\"name\":\"sizeReusedContentInOutputDirectory\",\"value\":0,\"unit\":\"bytes\"},{\"name\":\"sizeWrittenToOutputDirectory\",\"value\":980052,\"unit\":\"bytes\"},{\"name\":\"filesReusedInOutputDirectory\",\"value\":0,\"unit\":\"count\"},{\"name\":\"filesWrittenToOutputDirectory\",\"value\":18,\"unit\":\"count\"}]}"} -{"timestamp":"2026-07-14T18:37:21.675327700Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Clean output directory\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.676605500Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Clean output directory\",\"depth\":1,\"durationTicks\":12714}"} -{"timestamp":"2026-07-14T18:37:21.677202600Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Populate build report\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.679717700Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Populate build report\",\"depth\":1,\"durationTicks\":25225}"} -{"timestamp":"2026-07-14T18:37:21.679745900Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Record type usage\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.685985100Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Record type usage\",\"depth\":1,\"durationTicks\":62055}"} -{"timestamp":"2026-07-14T18:37:21.686015000Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Generate build layout\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.687145400Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Generate build layout\",\"depth\":1,\"durationTicks\":11120}"} -{"timestamp":"2026-07-14T18:37:21.687177200Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"BuildStatistic\",\"context\":\"BuildComplete\",\"statistics\":[{\"name\":\"loadableCount\",\"value\":3,\"unit\":\"count\"},{\"name\":\"objectsProcessed\",\"value\":810,\"unit\":\"count\"},{\"name\":\"objectsBuilt\",\"value\":28,\"unit\":\"count\"},{\"name\":\"scenesBuilt\",\"value\":2,\"unit\":\"count\"},{\"name\":\"serializedFilesBuilt\",\"value\":13,\"unit\":\"count\"},{\"name\":\"archivesBuilt\",\"value\":0,\"unit\":\"count\"},{\"name\":\"serializedFilesReused\",\"value\":0,\"unit\":\"count\"},{\"name\":\"serializedFilesRebuilt\",\"value\":13,\"unit\":\"count\"},{\"name\":\"reusedSerializedFileSize\",\"value\":0,\"unit\":\"bytes\"},{\"name\":\"rebuiltSerializedFileSize\",\"value\":158832,\"unit\":\"bytes\"}]}"} -{"timestamp":"2026-07-14T18:37:21.687419000Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Finalize ContentBuildRoot\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.687802300Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Finalize ContentBuildRoot\",\"depth\":1,\"durationTicks\":3912}"} -{"timestamp":"2026-07-14T18:37:21.687823800Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Clean up after build\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.696410500Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Clean up after build\",\"depth\":1,\"durationTicks\":85462}"} -{"timestamp":"2026-07-14T18:37:21.696438900Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Unloading unused assets\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.707177300Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Unloading unused assets\",\"depth\":1,\"durationTicks\":107079}"} -{"timestamp":"2026-07-14T18:37:21.709880500Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Call Post-Process callbacks\",\"depth\":1}"} -{"timestamp":"2026-07-14T18:37:21.710682200Z","thread_id":"67564","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Call Post-Process callbacks\",\"depth\":1,\"durationTicks\":8055}"} +{"timestamp":"2026-09-21T16:53:11.246415000Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"BuildStart\",\"rootAssetCount\":1,\"startTime\":\"2026-09-21T16:53:11.2245541Z\",\"targetPlatform\":19,\"subtarget\":2,\"metadataPath\":\"Library/BuildHistory/20260921-165311Z-f096bba0\",\"outputPath\":\"C:/UnitySrc/UnityDataTools/UnityProjects/LeadingEdge/../../TestCommon/Data/LeadingEdgeBuilds/ContentDirectory\",\"rootAssetPaths\":[\"Assets/ScriptableObjects/ContentDirectoryRoot.asset\"]}"} +{"timestamp":"2026-09-21T16:53:11.302562000Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Refresh Asset Database\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:11.334682400Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Refresh Asset Database\",\"depth\":1,\"durationTicks\":317327}"} +{"timestamp":"2026-09-21T16:53:11.334714100Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Call Pre-Process callbacks\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:11.358805500Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Call Pre-Process callbacks\",\"depth\":1,\"durationTicks\":240262}"} +{"timestamp":"2026-09-21T16:53:11.358857600Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Configure target platform\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:11.402288900Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Configure target platform\",\"depth\":1,\"durationTicks\":434120}"} +{"timestamp":"2026-09-21T16:53:11.402340300Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Compile scripts\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:15.179216900Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Compile scripts\",\"depth\":1,\"durationTicks\":37768315}"} +{"timestamp":"2026-09-21T16:53:15.256887100Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Single-pass import\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.208354200Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Single-pass import\",\"depth\":1,\"durationTicks\":69514845}"} +{"timestamp":"2026-09-21T16:53:22.208519300Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Deferred shader pass\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.208766800Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Deferred shader pass\",\"depth\":1,\"durationTicks\":2513}"} +{"timestamp":"2026-09-21T16:53:22.212645600Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Build builtin-extra content file\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.607474200Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Build builtin-extra content file\",\"depth\":1,\"durationTicks\":3948183}"} +{"timestamp":"2026-09-21T16:53:22.607726700Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Generate build manifest\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.608338200Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Generate build manifest\",\"depth\":1,\"durationTicks\":6193}"} +{"timestamp":"2026-09-21T16:53:22.615936400Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"BuildStatistic\",\"context\":\"BuildOutput\",\"statistics\":[{\"name\":\"sizeReusedContentInOutputDirectory\",\"value\":0,\"unit\":\"bytes\"},{\"name\":\"sizeWrittenToOutputDirectory\",\"value\":920270,\"unit\":\"bytes\"},{\"name\":\"filesReusedInOutputDirectory\",\"value\":0,\"unit\":\"count\"},{\"name\":\"filesWrittenToOutputDirectory\",\"value\":18,\"unit\":\"count\"}]}"} +{"timestamp":"2026-09-21T16:53:22.616013100Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Clean output directory\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.618757800Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Clean output directory\",\"depth\":1,\"durationTicks\":27605}"} +{"timestamp":"2026-09-21T16:53:22.619051600Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Populate build report\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.620657900Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Populate build report\",\"depth\":1,\"durationTicks\":16094}"} +{"timestamp":"2026-09-21T16:53:22.620680100Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Record type usage\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.630744400Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Record type usage\",\"depth\":1,\"durationTicks\":100472}"} +{"timestamp":"2026-09-21T16:53:22.630771700Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Generate build layout\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.631933600Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Generate build layout\",\"depth\":1,\"durationTicks\":11536}"} +{"timestamp":"2026-09-21T16:53:22.631960700Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"BuildStatistic\",\"context\":\"BuildComplete\",\"statistics\":[{\"name\":\"loadableCount\",\"value\":3,\"unit\":\"count\"},{\"name\":\"objectsBuilt\",\"value\":28,\"unit\":\"count\"},{\"name\":\"scenesBuilt\",\"value\":2,\"unit\":\"count\"},{\"name\":\"serializedFilesBuilt\",\"value\":13,\"unit\":\"count\"},{\"name\":\"archivesBuilt\",\"value\":0,\"unit\":\"count\"},{\"name\":\"serializedFilesReused\",\"value\":0,\"unit\":\"count\"},{\"name\":\"serializedFilesRebuilt\",\"value\":13,\"unit\":\"count\"},{\"name\":\"reusedSerializedFileSize\",\"value\":0,\"unit\":\"bytes\"},{\"name\":\"rebuiltSerializedFileSize\",\"value\":99584,\"unit\":\"bytes\"}]}"} +{"timestamp":"2026-09-21T16:53:22.632062300Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Finalize ContentBuildRoot\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.632598300Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Finalize ContentBuildRoot\",\"depth\":1,\"durationTicks\":5245}"} +{"timestamp":"2026-09-21T16:53:22.632628900Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Clean up after build\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.633573200Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Clean up after build\",\"depth\":1,\"durationTicks\":9366}"} +{"timestamp":"2026-09-21T16:53:22.633593800Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Unloading unused assets\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.682366400Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Unloading unused assets\",\"depth\":1,\"durationTicks\":487076}"} +{"timestamp":"2026-09-21T16:53:22.683343700Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepStart\",\"stepName\":\"Call Post-Process callbacks\",\"depth\":1}"} +{"timestamp":"2026-09-21T16:53:22.685363800Z","thread_id":"27868","log_level":"Info","message":"{\"type\":\"StepEnd\",\"stepName\":\"Call Post-Process callbacks\",\"depth\":1,\"durationTicks\":20019}"} diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildReportSummary.json b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildReportSummary.json index e1bf60d..087d583 100644 --- a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildReportSummary.json +++ b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/BuildReportSummary.json @@ -3,7 +3,7 @@ "BuildContentOptions": [ "CleanBuildCache" ], - "BuildManifestHash": "baff06b928d147276f2245dd3b19216a", + "BuildManifestHash": "e320fc78984f8430afa90a591fd02004", "BuildName": "ContentDirectory", "BuildOptions": [ "None" @@ -11,17 +11,19 @@ "BuildProfilePath": "", "BuildResult": 1, "BuildResultName": "Succeeded", - "BuildSessionGUID": "f64157fb08bb9f645971d39c1203bd03", - "BuildStartedAt": "2026-07-14T18:37:14.2095142Z", + "BuildSessionGUID": "f096bba0b9ce4c44194edd738d64b9df", + "BuildStartedAt": "2026-09-21T16:53:11.2245541Z", "BuildType": 3, "BuildTypeName": "ContentDirectory", + "ContentSourceBuildSessionGUID": "00000000000000000000000000000000", "OutputPath": "C:/UnitySrc/UnityDataTools/UnityProjects/LeadingEdge/../../TestCommon/Data/LeadingEdgeBuilds/ContentDirectory", "Platform": 19, "PlatformName": "StandaloneWindows64", + "ProducerPackage": "", "Subtarget": 2, "SubtargetName": "Player", "TotalErrors": 0, - "TotalSizeBytes": 980084, - "TotalTimeMs": 7515, - "TotalWarnings": 1 + "TotalSizeBytes": 920302, + "TotalTimeMs": 11469, + "TotalWarnings": 0 } \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentBuildId.txt b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentBuildId.txt new file mode 100644 index 0000000..bf3fddd --- /dev/null +++ b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentBuildId.txt @@ -0,0 +1 @@ +ce87639b203da27885e650bfe54d2599 \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentLayout.json b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentLayout.json index 3fad48c..70e4491 100644 --- a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentLayout.json +++ b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentLayout.json @@ -1 +1 @@ -{"Version":2,"BuildManifestHash":"baff06b928d147276f2245dd3b19216a","SerializedFiles":[{"Index":0,"ID":"Library/unity default resources","IsBuiltIn":true,"SourceAssets":[],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[]},{"Index":1,"ID":"0000000000000000f000000000000000.cfid","IsBuiltIn":false,"SourceAssets":["Resources/unity_builtin_extra"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"6a60c190881f43bb4a91a360b9e8850d"},{"Index":2,"ID":"162c015549f8733449ac70ae78ad3aa5.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Scenes/Scene1.unity"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[0,1,9],"ContentHash":"c271b85494f5e4cc35c4ec4a776324af"},{"Index":3,"ID":"20408785117e138f38c2cc4ca0e638c8.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Audio/6.mp3"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"93bb3159569284a5c7bcceb4acb91bae"},{"Index":4,"ID":"4038ff673d390134d924b57fcbed0432.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/LoadableAudioClipReference.asset"],"LoadableDependencies":["20408785117e138f38c2cc4ca0e638c8","dd30e3af3b8bddf1ea36d768fc41aafd"],"LoadableSceneDependencies":[],"SerializedFileDependencies":[8],"ContentHash":"bfcf18a244e45f867750e0223a9b3a43"},{"Index":5,"ID":"52b43dad178849b42ac753005736e7bb.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/ContentDirectoryRoot.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[8,4,11,13,7],"ContentHash":"c0152db4dd710be51b2decb997325f34"},{"Index":6,"ID":"590cfeb4e0ff90f4e92f9e1262bcfe6f.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Scenes/Scene2.unity"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[0,1,9],"ContentHash":"4d49f1aa2acf2ecf20f51660e1bf77ea"},{"Index":7,"ID":"70a210050f71a924aa83be7146547111.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/SceneList.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":["Assets/Scenes/Scene2.unity","Assets/Scenes/Scene1.unity"],"SerializedFileDependencies":[8],"ContentHash":"8ad4924ac5e264fde63d8e95a0dab8ab"},{"Index":8,"ID":"a2a42d71dddef12e8889849faf59bdd7.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Scripts/DirectScriptableObjectReference.cs","Assets/Scripts/LoadableAudioClipReference.cs","Assets/Scripts/SceneList.cs","Assets/Scripts/SerializationDemo.cs"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"86d71ff2bb38e064697257d35d6421b8"},{"Index":9,"ID":"baf46637edbb6ff884858f6630b66eda.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Textures/GreenStatic.png"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[12],"ContentHash":"61fa28f6b22b739b19ece22ea0b3f29f"},{"Index":10,"ID":"dd30e3af3b8bddf1ea36d768fc41aafd.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Audio/a.mp3"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"730d2d641a53eeb1e633f2ff60d730e9"},{"Index":11,"ID":"21679be819d6e9146a63bb02a7e51f2f.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/SingleAudioClipLoadableReference.asset"],"LoadableDependencies":["dd30e3af3b8bddf1ea36d768fc41aafd"],"LoadableSceneDependencies":[],"SerializedFileDependencies":[8],"ContentHash":"5c43454a3823f172a2a326410a36ba6b"},{"Index":12,"ID":"34814583296b527bf650f548effcd62c.cfid","IsBuiltIn":false,"SourceAssets":["Assets/Textures/GreenStatic.png"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[],"ContentHash":"c046f78347acd69f03fa7f80362fde94"},{"Index":13,"ID":"78532141fd7679a458405eb16bdb75fd.cfid","IsBuiltIn":false,"SourceAssets":["Assets/ScriptableObjects/SerializationDemo.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[8],"ContentHash":"51690f09f541d7442ce8faf26fd84696"}],"RootAssets":["3dcb957e6cb206bfec2aaadd3d11d85c"],"LoadableObjectIds":[{"ObjectIdHash":"20408785117e138f38c2cc4ca0e638c8","GUID":"278c261333bf8604eb5c83790d02004d","AssetPath":"Assets/Audio/6.mp3","LFID":8300000,"IdentifierType":3,"SerializedFile":3,"OutputLFID":6313987888022012634},{"ObjectIdHash":"3dcb957e6cb206bfec2aaadd3d11d85c","GUID":"52b43dad178849b42ac753005736e7bb","AssetPath":"Assets/ScriptableObjects/ContentDirectoryRoot.asset","LFID":11400000,"IdentifierType":2,"SerializedFile":5,"OutputLFID":-775554941117088049},{"ObjectIdHash":"dd30e3af3b8bddf1ea36d768fc41aafd","GUID":"b65a7916245593b4e89f4bd0aa920533","AssetPath":"Assets/Audio/a.mp3","LFID":8300000,"IdentifierType":3,"SerializedFile":10,"OutputLFID":-6933612096100796476}],"LoadableSceneIds":[{"GUID":"590cfeb4e0ff90f4e92f9e1262bcfe6f","Path":"Assets/Scenes/Scene2.unity","SerializedFile":6},{"GUID":"162c015549f8733449ac70ae78ad3aa5","Path":"Assets/Scenes/Scene1.unity","SerializedFile":2}],"BinaryArtifacts":[{"Index":0,"ContentHash":"6a60c190881f43bb4a91a360b9e8850d","Category":"contentfile","Size":96364,"ArtifactReferences":[1]},{"Index":1,"ContentHash":"ea7c9fa2d58ef71544726747da8ade6d","Category":"texture","Size":524280},{"Index":2,"ContentHash":"c271b85494f5e4cc35c4ec4a776324af","Category":"contentfile","Size":17572},{"Index":3,"ContentHash":"93bb3159569284a5c7bcceb4acb91bae","Category":"contentfile","Size":1288,"ArtifactReferences":[4]},{"Index":4,"ContentHash":"68c9d0b12420e1951eec7790bd0754fe","Category":"audio","Size":30688},{"Index":5,"ContentHash":"bfcf18a244e45f867750e0223a9b3a43","Category":"contentfile","Size":2184},{"Index":6,"ContentHash":"c0152db4dd710be51b2decb997325f34","Category":"contentfile","Size":1524},{"Index":7,"ContentHash":"4d49f1aa2acf2ecf20f51660e1bf77ea","Category":"contentfile","Size":17572},{"Index":8,"ContentHash":"8ad4924ac5e264fde63d8e95a0dab8ab","Category":"contentfile","Size":1304},{"Index":9,"ContentHash":"86d71ff2bb38e064697257d35d6421b8","Category":"contentfile","Size":2012},{"Index":10,"ContentHash":"61fa28f6b22b739b19ece22ea0b3f29f","Category":"contentfile","Size":8516},{"Index":11,"ContentHash":"730d2d641a53eeb1e633f2ff60d730e9","Category":"contentfile","Size":1288,"ArtifactReferences":[12]},{"Index":12,"ContentHash":"4226b5c16a50dab6eff0f08dd1253d4b","Category":"audio","Size":47424},{"Index":13,"ContentHash":"5c43454a3823f172a2a326410a36ba6b","Category":"contentfile","Size":2136},{"Index":14,"ContentHash":"c046f78347acd69f03fa7f80362fde94","Category":"contentfile","Size":2260,"ArtifactReferences":[15]},{"Index":15,"ContentHash":"f0a44ad4a4babd121543fd44032928e7","Category":"texture","Size":215970},{"Index":16,"ContentHash":"51690f09f541d7442ce8faf26fd84696","Category":"contentfile","Size":4812},{"Index":17,"ContentHash":"baff06b928d147276f2245dd3b19216a","Category":"manifest","Size":2858,"ArtifactReferences":[0,2,3,5,6,7,8,9,10,11,13,14,16]}]} \ No newline at end of file +{"Version":3,"BuildManifestHash":"e320fc78984f8430afa90a591fd02004","SerializedFiles":[{"Index":0,"StableId":"Library/unity default resources","IsBuiltIn":true,"ArtifactIndex":-1,"SourceAssets":[],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[]},{"Index":1,"StableId":"80b335c0244a430ff7eadfc72ee8fc6a","ArtifactIndex":1,"SourceAssets":["Resources/unity_builtin_extra"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[]},{"Index":2,"StableId":"21679be819d6e9146a63bb02a7e51f2f","ArtifactIndex":2,"SourceAssets":["Assets/ScriptableObjects/SingleAudioClipLoadableReference.asset"],"LoadableDependencies":[1],"LoadableSceneDependencies":[],"SerializedFileDependencies":[4]},{"Index":3,"StableId":"70a210050f71a924aa83be7146547111","ArtifactIndex":3,"SourceAssets":["Assets/ScriptableObjects/SceneList.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":["Assets/Scenes/Scene2.unity","Assets/Scenes/Scene1.unity"],"SerializedFileDependencies":[4]},{"Index":4,"StableId":"a2a42d71dddef12e8889849faf59bdd7","ArtifactIndex":4,"SourceAssets":["Assets/Scripts/DirectScriptableObjectReference.cs","Assets/Scripts/LoadableAudioClipReference.cs","Assets/Scripts/SceneList.cs","Assets/Scripts/SerializationDemo.cs"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[]},{"Index":5,"StableId":"4038ff673d390134d924b57fcbed0432","ArtifactIndex":5,"SourceAssets":["Assets/ScriptableObjects/LoadableAudioClipReference.asset"],"LoadableDependencies":[0,1],"LoadableSceneDependencies":[],"SerializedFileDependencies":[4]},{"Index":6,"StableId":"162c015549f8733449ac70ae78ad3aa5","ArtifactIndex":6,"SourceAssets":["Assets/Scenes/Scene1.unity"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[0,1,13]},{"Index":7,"StableId":"34814583296b527bf650f548effcd62c","ArtifactIndex":7,"SourceAssets":["Assets/Textures/GreenStatic.png"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[]},{"Index":8,"StableId":"20408785117e138f38c2cc4ca0e638c8","ArtifactIndex":8,"SourceAssets":["Assets/Audio/6.mp3"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[]},{"Index":9,"StableId":"78532141fd7679a458405eb16bdb75fd","ArtifactIndex":9,"SourceAssets":["Assets/ScriptableObjects/SerializationDemo.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[4]},{"Index":10,"StableId":"52b43dad178849b42ac753005736e7bb","ArtifactIndex":10,"SourceAssets":["Assets/ScriptableObjects/ContentDirectoryRoot.asset"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[4,5,2,9,3]},{"Index":11,"StableId":"dd30e3af3b8bddf1ea36d768fc41aafd","ArtifactIndex":11,"SourceAssets":["Assets/Audio/a.mp3"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[]},{"Index":12,"StableId":"590cfeb4e0ff90f4e92f9e1262bcfe6f","ArtifactIndex":12,"SourceAssets":["Assets/Scenes/Scene2.unity"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[0,1,13]},{"Index":13,"StableId":"baf46637edbb6ff884858f6630b66eda","ArtifactIndex":13,"SourceAssets":["Assets/Textures/GreenStatic.png"],"LoadableDependencies":[],"LoadableSceneDependencies":[],"SerializedFileDependencies":[7]}],"RootAssets":[2],"LoadableObjectIds":[{"GUID":"278c261333bf8604eb5c83790d02004d","LFID":8300000,"IdentifierType":3,"SerializedFile":8},{"GUID":"b65a7916245593b4e89f4bd0aa920533","LFID":8300000,"IdentifierType":3,"SerializedFile":11},{"GUID":"52b43dad178849b42ac753005736e7bb","LFID":11400000,"IdentifierType":2,"SerializedFile":10}],"LoadableSceneIds":[{"GUID":"590cfeb4e0ff90f4e92f9e1262bcfe6f","Path":"Assets/Scenes/Scene2.unity","SerializedFile":12},{"GUID":"162c015549f8733449ac70ae78ad3aa5","Path":"Assets/Scenes/Scene1.unity","SerializedFile":6}],"BinaryArtifacts":[{"Index":0,"ContentHash":"e320fc78984f8430afa90a591fd02004","Category":"manifest","Size":2324,"ArtifactReferences":[1,2,3,4,5,6,7,8,9,10,11,12,13]},{"Index":1,"ContentHash":"ccccf25fc67d2bea8415c0846c1602dc","Category":"contentfile","Size":32252,"ArtifactReferences":[14]},{"Index":2,"ContentHash":"38abbace71a8a756c679a7ff3f501f02","Category":"contentfile","Size":2952},{"Index":3,"ContentHash":"70ae0762d8f82ea593516bcc52c94bf0","Category":"contentfile","Size":1896},{"Index":4,"ContentHash":"79bdcb3e9bc659d3519594280240cd1b","Category":"contentfile","Size":1900},{"Index":5,"ContentHash":"2357d63c989be24891a7e85b77bce4a6","Category":"contentfile","Size":3000},{"Index":6,"ContentHash":"8ec44fb05f86ef7dcb7bbef683d8a330","Category":"contentfile","Size":17540},{"Index":7,"ContentHash":"214959d6771e8c749dd41eca123456b2","Category":"contentfile","Size":2468,"ArtifactReferences":[15]},{"Index":8,"ContentHash":"9a17f57112aa00edaa582242bc31fb71","Category":"contentfile","Size":1400,"ArtifactReferences":[16]},{"Index":9,"ContentHash":"491d099506657727cb6683c78c0e2383","Category":"contentfile","Size":4428},{"Index":10,"ContentHash":"eb3abd5ab5b0d790980fe9e9df872484","Category":"contentfile","Size":2084},{"Index":11,"ContentHash":"39105176560358e0cd444e7a0a513ed3","Category":"contentfile","Size":1400,"ArtifactReferences":[17]},{"Index":12,"ContentHash":"3cfc30da76f416cbd860bd47c9e5ed95","Category":"contentfile","Size":17540},{"Index":13,"ContentHash":"812fe00f6f62ce8504f14dfe20976ba2","Category":"contentfile","Size":10724},{"Index":14,"ContentHash":"ea7c9fa2d58ef71544726747da8ade6d","Category":"texture","Size":524280},{"Index":15,"ContentHash":"f0a44ad4a4babd121543fd44032928e7","Category":"texture","Size":215970},{"Index":16,"ContentHash":"68c9d0b12420e1951eec7790bd0754fe","Category":"audio","Size":30688},{"Index":17,"ContentHash":"4226b5c16a50dab6eff0f08dd1253d4b","Category":"audio","Size":47424}]} \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentSizeSummary.txt b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentSizeSummary.txt index 33f9a8b..8800a72 100644 --- a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentSizeSummary.txt +++ b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ContentSizeSummary.txt @@ -3,19 +3,19 @@ Build Report - Content Size Statistics Serialized File count 13 Resource file count 4 Object count 28 -Serialized Files 155.1 kb 16.3% -Serialized Files Headers 142.0 kb 14.9% -Resource data (Texture,Audio...) 799.2 kb 83.7% -Total build size 954.3 kb +Serialized Files 97.2 kb 10.8% +Serialized Files Headers 84.2 kb 9.4% +Resource data (Texture,Audio...) 799.2 kb 89.2% +Total build size 896.4 kb Largest Types: - 512.2 kb 53.7% Cubemap - 211.1 kb 22.1% Texture2D - 76.5 kb 8.0% AudioClip + 512.2 kb 57.1% Cubemap + 211.1 kb 23.5% Texture2D + 76.5 kb 8.5% AudioClip Largest Assets: - 519.5 kb 54.4% Resources/unity_builtin_extra - 211.5 kb 22.2% Assets/Textures/GreenStatic.png - 46.4 kb 4.9% Assets/Audio/a.mp3 - 30.1 kb 3.2% Assets/Audio/6.mp3 + 519.4 kb 57.9% Resources/unity_builtin_extra + 211.5 kb 23.6% Assets/Textures/GreenStatic.png + 46.4 kb 5.2% Assets/Audio/a.mp3 + 30.1 kb 3.4% Assets/Audio/6.mp3 ------------------------------------------------------------------------------- diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ScriptsOnlyCache.yaml b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ScriptsOnlyCache.yaml index ef2bfee..2fe8a7a 100644 --- a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ScriptsOnlyCache.yaml +++ b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/ScriptsOnlyCache.yaml @@ -19,5 +19,5 @@ ScriptsOnlyBuild: scriptHashData: [] platform: 19 scenePathNames: [] - nativeClasses: 000000003000000012000000820000001c0000001b00000059000000150000000900000003000000680000009d00000004000000020000000100000019000000d4000000c4000000530000007de237150f01000072000000080000003100000073000000d5000000i + nativeClasses: 00000000300000001200000082000000150000001c0000001b00000059000000020000007200000008000000730000003100000001000000040000000900000003000000680000009d000000c400000019000000d4000000530000007de237150f010000d5000000i playerPath: diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/circularrefs.json b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/circularrefs.json new file mode 100644 index 0000000..74b6e1f --- /dev/null +++ b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/circularrefs.json @@ -0,0 +1,3 @@ +{ + "clusters": [] +} \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/f096bba0b9ce4c44194edd738d64b9df.buildreport b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/f096bba0b9ce4c44194edd738d64b9df.buildreport new file mode 100644 index 0000000000000000000000000000000000000000..f4c30fb1e0751ad8940d1ba5f3a6de1e6fa7197d GIT binary patch literal 11604 zcmc&)3z!tum97B{IDrTPl8BE|0g*T}Ro&H9-H9I#%pf4Iff?co60FB9rlnu$u9im> zI|&Hcbz?Lvh>wII<|9NUtB5F)_`t`ayNe2}9~)6uO?H#@nMBr4lG*d$s@q)@&4l=U z`8?lL*RA{CbL!l4&pr3tJ0(d~IhAS>x(fXtp8vsj*1a@*=O=65y=`^lfaUA2HfH{@&p)i9w%KmaJC9m_ zp001v)j$`>kfaKU>){U-{>y(FsKrnEN|gs1qzc}G_JRiu2$u)aeNsQEf;-fS;^$MH z8Q`Y`xM&0Z8~aF=Vt@|_aL54xpF;2d8NDC+LGOmj0I4rPBH*OE)J_-h)95Z$Bni5L zfFB_KnN$WoAC(nJf*v4&e~rq(uR&!+l0Jt!k-%*#1HT@X6-nxkJCVRWDg$4M%8DeR zAE1flbjF#(3i+ZZb#!>eOC~vKFJGLr)ML3)I-eWw*LD<{Yg$utCC|*+Y`o9bS))?BV?@)|GdIlm!wVnH{AvIFyMFve!Wh4yJL0qZ ziB zG!u9^z=f|x@mm61_*xXdC%}cTg>dfk4^jnu5-tFr6eN81DXIWI1VE)`*S36)ywj^W zU5ORbW+uIyIdH#DDv*yAS-HfV&iYU(UoP6L-So_$bvH8|Rtz4FmDiRp=e!dLtYN-H zykFGit!tR=i6?JafpvPOSDKzKL7IvAP^-Wg5BPY+wug2-c>nGv$1VB6=x+^OF(&Wa za7nU%CA;*zewR1!VdB0Z91L~=z0m022fz5%*f9r`iTi@*?ZgsoJDi$E1#hD|5qRMc zJc!X9fvFcKZcu4haAA=6dmbmIfEvhc;bbF824o)Z#C>EmrGUoyxf54V;}ee;+&;Vu zn3iYgo$oXrzV*ZFV(jsud&wR`U-l| z0=!<_0Immky|@7!aSih8#0}p6DFM#o29*T~{bo}I_=8y>V5;-`KQF-H=3x@JLzUo< z`i#JH0X`_q%&=XknvkWMqOa~K`>vP4w z-aBy3?2YUG`@Og4FB&!Cj%XsVInob${B-?j1*cCZ6Bw6K#)-fP4?KRBO495dZ%jUC z)uV6Ed-l$u6W@5~>}Y~?Bk>9QgI~;eWU_xL8bV8q8OAFd?ckk=hM0BrqGA2GcTeJx z!kOPcx9O%W8@8N3Tp5e}NN9%mA=8C7(f*;=xQ85{npHJJSr9Y>4y~Yhy|@P)UI_en zi~9Uu3~;mwlS63&rX9c2%;0yL7yM4M0u)`JQq(EYPsG7VbfKT1UY1lDc0gPbGlnw+ zKh#gY*Fb#6lFQ?3r6w14K;DQY7q+Mu|F9%~nK1ORUi<@2`f)rO|3XW$pY8C9$#ZFm z3q51elft=Ak@={f31M)ePO&$V&tKU1!cA&^(}KGi_xyUq#qtd{@d#srAAwU0GK`7N zMk(s9-1wo9Z8tsEmCiEBOWTV}oO|Ez9(?_k33pz-^!;J;hkSL@oJ~|KUJIW&hpv+t zL!S0vO~>0rdn$4gV~CssW5~Nv@iF{X_f0ccsbnr9^(b$YY(L}W(xS$SZf+hRDMD*# zfK>=ueCDwiyTPeBH_x>>|KFy*yE}jTv)BCo*=gTj)^%w#5)UDjMK9pzBB!8Yuk#0c zam*dqmfOAp4xfbWkpctf`Aa1L=?0fUoWMB;ELShT!2CL!u-@j^g#mwbennTH0%l4v zPis-yk-WTTX88WY&6tZy+(^-(j&2a{rxyt$L19ezPc_u7#2C=YEKg{P~pv#AH`nz;K+}9K9P4kKj`%%8}5ttXe~(y_W+X70}Nv?Ve+`0M?)((eL9IANR%S- z1s3m$_5f0%Cj^_RuAZKM>q!MWz|wG6u4e_>`2^jg_KZ%e0n|w74R_b*0w1Us`|@Q5 zfdlX5?JMB$3DX4AU<5s48t`cpV7LLtGBTRV@JhZ+0SE}w5O%{d1o?a$P^sxv4S_sZ zlqhPrI!DIANY&_hZjRXo>)*(4751x!+?iL;AyIaBcLB)68JZ$5;$gkm;}Cx zDuE-34in*AzuTx1`opdf{WyMifMap3hu<6E=N*spMSBO;p$M^^L&#wgh6M+-qT_DJZ|4s|=$*63t;eNyl z{u5A5sxACmfllv=9?!A)Bi|_a0EKOep79wry_Wk2ed$`(mk#5^fq zK=dz3TtaXJJS(WU6I4ttQwkVl`JMc1dLp-A$WU;;=*H+TZKR^7j?r}Nb&@Fp-YHaubBTdR zC`N)8B{<|rayTTL&`Gox?SO}Op&}54aij+b``{M=F^Xg50gfw7wFJ}7(74m)^TeCY zFyCKUmZ`m~q;$(N6)R;k&DJzEr7_1b;z>u>td!%9{#bfR`r>OMWLMvCT6$OG9sj$h z{g$_0HD0;wjz7V)SEO?~vM-p8CPvONGx;2=%|n^$L0P)mzS1%`m-d!*7Hz*k^UCbX z=QAb$k(>tqS}3(lr~g`zV@`4wv6kj${B@DLwdJ$fe69sd;TGJZN-e?EX-^kPM4tYA z_u0G?v8s+L(z}iRv8gxw;4z6K+=RQ@oAS}6x6N9qZ2M{BmFMo0qwYb?>e5iE#oEWM z5T2OL-cpb1;p)d=9r-l0DzSR{!MYDYm0p_2gjAWFm56@r2>%aXXbvSuCa{Q>tmOWWq8GizcY4*OLP$+8OCrlF?*# zEhXV5buv8ZW>^+;^<>;hn(;XA7vA4#GfYwb&-9h;R0s%tim4iP!qE*wWl7u6QjVjk z%r@0nTubOyT^l1%Y!00&jHYK-F*VI}G*h!o%TZ(G1##EWG$kHO#gfc886SYiP17)f zK0&{fsTyv=P}P`eDhhKxeWSof26j(`6-6r^(BE|xASMY0NMcGoi+rQ&%5EtEkPiSWLGP6b#IS;+Pisn5(!-(xKr^#2w8#S)1XU zv-J`B35e@xyLOzU8oH~gx@|d0oyJ0=K*AEtNhQ$NuMbD7l62vUp5!X}D5J%}r`?#dOUS*(#;FjuKN8t@)Z#K1c0f z>tkXoJ!APF7Mcr?Uu`D`k(2748g}eDfc?oF=91TS%l!OV#yV%2B_>HX|A)UCh?E2e zd2MDUBhyB;DM-*{n<^+c;>`EPt$nFQErr3}gx>6?7nO@l_VZ3z_alEv67n%V zwAdCXqmxT^FwD4Sby4?%Zf$j z$fa(4jUa>J#lS|QolNbxEoF-`L@%9dVoSZEDck-Lv5fR`{rF3$RjpsxvrVQ@Mh7U2 zvpI4$NKGX**suxtuy0$2nK{|dpk=3)C>&S}sWrKn?;ZIrVHW>fJbE1> zq$K^k!>`Aj0>RnNq6_irO1l2Qcf-DI&=S~X(`)kqb;v#5SA98fYFr9ko6%_^ACV*^ zNq-ltv10k?rn=jM+&{QU2dM6?fJyM~r@Ae{gJ?%nQwrRon*PHtlnC!#RJV~Xkl;l> z*YKKJ9oXDR-5z=tXBC1M^I4E6<2*v7T&0#W((;AD%v;E39^ucO?Jp#-9^Q3ke&e|I zCF`c%U%2w~J74OPJD2K$@$MoX3NEFV$wkI;_`W4=Hy3h?q;WHS+d~(~SbWcVbK>08 zwC{bqebCm6I^u{Ufs6@dC!KFGo3jOSq%Fbv03KY$=PWh;Nq6fno=&dcCrkT}FdMIDOb_i`HMhs^8lqckI66HH`BKeN=o^#SdiLSvK!G=+R(3g-nq7`o@~I(>7g~kjnOoU7vQ|!AC)&U;Vz*bM4DN7~FQ(#Q8@aJKTCf zim+3OZJBoexmP^mw)|( z4=?RazQ4FsrHj`^-x3UWwGZ8~tNy%e%(|)f4M;Vda@wPfYV;kY7JrKvjZihgHs+}p z(S3!t`@nxSKR)yJPmYwY`tpI(aHMdIZD`keVvE?NOhjJ)aSa{skGOr}*5vO-IFm*U z85@-swl#Mrd|>CBwmTwg*5+TxzrFg^DZAdh;ny$TykO1+QCZ}W!S{kS&!%2p6_LXh g_q=`J?n!Mcm+y@2pLfn#2#1kA)-)mh=@I__2h^EZR{#J2 literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/f64157fb08bb9f645971d39c1203bd03.buildreport b/TestCommon/Data/LeadingEdgeBuilds/BuildReport-ContentDirectory/f64157fb08bb9f645971d39c1203bd03.buildreport deleted file mode 100644 index db603fea04bf53156eab8ffdb9335a8239801269..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11268 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 Lr(3Sp9k})nrLqm# literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/38abbace71a8a756c679a7ff3f501f02.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/38abbace71a8a756c679a7ff3f501f02.cf new file mode 100644 index 0000000000000000000000000000000000000000..82bde3ce6b15c6966089a0cec84691f5a2904ce9 GIT binary patch literal 2952 zcmb7GeQXp}5P#cNTZ*WqAxbaO(=@R*P`cNTyNgDtG#;cNXoHC#_1NCNqet&ryk0BC zMmL4vM;l}OgcuY5sUe`b#8jo>OASb@`~ilD20;=U4J4+)nivxZ&hPEc-DW*d=JMXo z&TnSkyf?G+PKX>;k#d+S&SVKMiL?yjy8ck_iKKkjQN%>^|_*ZR*tNzrl z&NRwrGbf_4ulIg7xb;YT<>%EE3uW_NdroJ6+BO`EObjn2+f;WZb2pjaOv6V5?n*Ub z1%|fVnBSwcoKEUbizm`wn(#G4n1Kc2ju#Ih$R!7 z`c)|NFY9ZLP48HKYI^aN>?OUgI6Jc8$ITDaeDF*p{msgV5Y^BwzEK05FmmcX`%md$=*GtRu(c>>1Rx_@RiQroe`QFl<+!w48n z0W+wZrYYlkK!!r0aHuI9H{#9VF4GLw$KnZ7>Cd70yG6zK_U!$={Op^53!M6&S5fU%k1P~J>Y!kti$&69h?u{i*q3%`EFwS8WO@k zC!ci~U*g~>N;fgyLPFS=xP4%yb#_R=o-q%{-+x{h0W;9 z1ej@LriqzmW?GmDG81AZ%#5CQo%5~doa;I7dd|I0{z2aFa68Gkx>03ZacCWAP2Q7* z5A6P|Y6ec*Q8EjVBH;DZT99>A2CAF749zyBqK_n!a%G!HMb{dcZel>=&6As|-l$$O zwrl6Edlod*o+KGZhWho>KrW8c%81N+<^&P0iH~Sy@FnUUT%H(5RKN>;cd&lD1A$rs7a$UNA39N6bomd$PdMhj=jo&1&K`suN^Eh6pYl49QA!% zwC}+?P8_WK&HH|kba*pxT1Vr5Y)0LYdE72=fVN_=bk(Q~iG5A8d0p>wdT^sg_x?`TfEBNXx?8oV;ngNVFDh2fv1 ziwg}EIP0^XE3j0QU)FSOl(Ec8Q)#~x@lB`e4xE0c-xz)Q0Iyh(0)iwwS4Hi8yh}5 z6n80oy4X9eRWFXli0c$B2u~00$KMLH9}MUK$2y=?G_p>$&0PQN$e@hWtEq~}+eN+q zW^6|VXL~pK|5JX_>PYl%NXpg$Gtsx4{@ZayJTBuhEqh~O=uHA6rSymwXIfzsHi9{qK6`yrfpipL;7PeP%t&EJ<4QvlB{NT)7=d=BC?0- z&6@{%5ERr)#a{FvD0mP=MG+K15EZ<5@Z`};{od~Eb_r=;n3-?h_x^TfUKopHe}q`u zj{vtX%5`VRC6D9Bc44b1nDV4wTbqA4(!~zWW>-F4?OXaYGx~V-{q=WWU$eQp3yGU| zy7&EVVazDIZXc|7V0^|Pcajmtyfuiw@%-7J6sGi^X=UDu67XcNcv~X_@a#K$yP#>q zXpfixg#2#FZ`pwFm3+qr{4vR|XATT0{ypSBCHc-K{?p)3Lr=9+opB+5;^(0xzMIGl zO@V~v-;@WJzDgY;JO)r{C2H z*hF=DiTj~Jtuf9Ud_3>GO0I|8Kg^2rlJsZ_c!tPZ64a8l!iv?vyn#pI}_4Wxz# QwV^`jf;ICj*igOy0hqz^tN;K2 literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/3cfc30da76f416cbd860bd47c9e5ed95.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/3cfc30da76f416cbd860bd47c9e5ed95.cf new file mode 100644 index 0000000000000000000000000000000000000000..692ee57819e746aed24482c6b03aa72068b234c8 GIT binary patch literal 17540 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 T8ChCT(4`bv)jY}=*w+66p{H02 literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/491d099506657727cb6683c78c0e2383.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/491d099506657727cb6683c78c0e2383.cf new file mode 100644 index 0000000000000000000000000000000000000000..33f8f2fb220a566302e123a964bf161029779ddf GIT binary patch literal 4428 zcmb8y3w(`b9KiACxHVI5p`vUqmFw7S*`zwNg-J0J$)dBJbGF0TIXXL&OXftDvVGyx%^UgYx&!=af?|a_&`9IHlp7*)EJ4%(O#^q-! zsHuKluebgg)Ypi04C7DT%%#c1!K3GApII~@F?!?qJo~m8CoU#63@fT~yj}IOY3p*! zF6Sm;Sz0Igz1N9)cyTPPcGM0g zWeSa~qI@GMb0axYK30q92jXgeRponvxWuZ#RH1;>Pp5vdzkYSSnsy&Km3U9;7Z8{4 z50X4zkg6g<67dN697d}oCT);ZRsT>K7MCT>X6|(N42#uGw*LVn}{Y>sh&Fn~#KS zF_-OFC=Vh@`!t_hreFDwm!uga35t|1+iSHMd#JrpNn-y>mIzB^gtc?H)oQib!y|0b z(a|x{kui3QJt`)}YV91BYIj(*Ta^#Mzv@@INUI%{S3jij)QJi6mzFk9%v(=q8RJ@q z(#v7A<~rl5!svG?ul0Grx5&88A%2@bZj6UfOyY+bla^%2m>c6!F`iHT_Wrn(shFgG zC~?}SjkPu}%vVcDdzoY5?{dcjp0(-9_-IIJ|vz+d=eRQ?^;H{|0!{4|8%aU@3eVq)=sq& z#I<=pmn#|enF6zwJ4|Cl{UcQ%(AJh ztEUc^*PmB@mYMGM7?~B$GaYFzn>8-o;_+8-1?Hr<-A+Abce*Y9TUgy$DNdW7omyTw z)9Y~KGfJs!(w?ZE?=UGa*xDCXm^LxmJqKphW531(>ak8xDEYR%VdB3e=XB)uN|)4va9iHw=A&d zXHvoFGkJbV(g!jFud{=g!s#<1GgO7kOH!6Bz|m-H`C&Z5bx)$o!{+m#C6rIhTN#gMuO>Li#Q z0~<^UUR&M#IM+>4sV$-UrV=_*+vCb7lhl_^m1mJTB>RE9DdcS+eJJZyc5d07W!=jz zTO0Bmbs>Et`-A*vC5@MHYJ^*GE9C7b|DZL&?YINdz7T|>8JeR7TH;RJg}c!Ttye>{o-7>LI(2r?#vk&GuW1Viy8EJ#5rWHQ=dM;g*0>tGm$!-)*I z;D)?;pMnRO@FEK%@iazZG{#^op20X|V?2C#785WL&tVdB@H{5t1-ytU$i+*TikC4B z(=h|DU?yJ0EWC!kb<_aAXjBktA{e!BBW^-%)InX;Lw($g255*z zxCOW3HZ(>P+>SfY6d?#jGc-pFw1oV^x(j!s6X9! zj=@L;hyC~-2XGLFa2P+}2!6y* zIErI9j-PP?zu+W_aSFfUG)hp4GdPRia1Q5j0T=N*{y-W2#9z3C%lI2tVEoVV4}f35 A9smFU literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/4d49f1aa2acf2ecf20f51660e1bf77ea.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/4d49f1aa2acf2ecf20f51660e1bf77ea.cf deleted file mode 100644 index b80551332f1dc38358b3b42b31e9aada4487f26b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17572 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 Qym=?Zaj9xYDFg5F|Hc~A;s5{u diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/51690f09f541d7442ce8faf26fd84696.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/51690f09f541d7442ce8faf26fd84696.cf deleted file mode 100644 index e82c8f417a74ff3df8d92a1696be7205169e0de0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4812 zcmaLa4SY>y9KiACj>)D{LS8DD2`OW1Yn$HgSXg=87L|&--Lvg-cdzcfqfxpkFNN|} zLJ4_W61~64ltQAF=uM9F@6*Fn6WO&{8JZkXtA*I8ueojFCI7`bHlOu#an0WJ(1re>G<9?d6g|rx9b@= z;#qmBS=Rh62NIG-1zE;gCIjt8Xlv zkrt-{BRS_>YUKRv+Azm)YIm?jUFze4=a{ERJh_evBv4Twbz!YM!zK{j|2Z*ni|R%HD`+J1moZubvNa0rEh)@TNL=P&u?2&& z&x~aLExCq_J+U^V?@4?B@nMkpHHe8@;u=vWap_g=adw83>E-VC)4POZy9?J|G*TF#d@7UnteZudfetevQK$vu{!H%Ej+ENc$!d4_IZZ7mKQ=Kfj3_NxZ4l24rIt z3#&W)`N`Uke17#K#^20r7;0ZX;`ud?i5vY-rC$1PA@P80j1t{{ORCS-|0QO_Q2WUK zFO7*C{ZFG_`oB!#0ofQOy8l*GpRIpuu$}p1Vbz7N-_~Xz_$$w67W?virK*i=!b;hY z9!{5*o1@Bx^tby@5)_49s~dD2v#XQY$UHCfYfrVOr8+WF91e$8oRX?#W@ct(rezh| zi_^0T9gd9TqGDz(&E=tmn*IOI+`jAA&a9r%ZO^8o?!L=U<_@h>o6F^$SPgSDXUpZ1 zizNrhgv|5V)r;iXImBg+OI&h!c#*ig>uss53x*I;B)*jPov4+#2rm-15@&q+we?(X zt35;FlZkf=#wC|4n{13C@pR&SIh3(WE{~2cAbxi+u0J%JoMaSfFYyr^N_)xW;U6=J zFJykBsg|)PN<6Gcyqx%W>g3x0k<0b*PbVh#PS52weU~HY)A-iRV_zP-p34IgF-r8e zM$hFoGb-&PzcsPBJn~zk=knU-djFE^mr*DE>$yBodqQ;odM>wx$0PgKb9rqzG(O1% za{r=pdA{a#+Fj1knnQo%TpoAHK)b74Q{}b0PPac;7uxfcdc0<3*uKwM;?^A9OYPoZ z19x;yp~vGgV#O|xJ$MO+r@YXm8P!Fh#-tjjIT||7_Im9V${erVPu9uN$~>LTn&1pn zw$G=P6}l=qbASu8>^K{*{+nV#OG$;+E9Z?C+e@}|g}CU29xG4fW(%9qtGD_B;k z+!I-CvZ7?wNJf({xqPkVi!2#QzO3@~lrN-w1!a8lHM62M+8_ySaXH$dJv!hDbi|do z3X-9c(HUKkf>d0MG^FDiNT$j}7P{hET!-s%18#&3+31Gu$iYqMfu87vT-=OX&>MXq zS*#!WV*v7yj{@9^ff$6_a69Cg491d3k!iixR4j0Pch6l20 z?|~OS_(7IaBQXm1q5`8a2KV89jKu>8;6aSTLwFeDF#!`X36J1WOvV&U#bbCJ(=Z(~ zFcY)z1fIlfJcT)!i+Ok&&tN{D#R4qEB0Pu1Sc0WkhUKWl^H_lwuo5p~6;|UVyo^`y zDqh3ucmr?ZExe6)um;|wHu$X}&#sDrwQM?LVDrK*nvB%%S%#d$a%4RHZ3L?c{;#%O}3 zxERgQ94*iim*7%dhE}kmHQFEvZE-o;p*=d_3UtJkxC-(gw`6oi7o;E+S0fGSxCR-> zL>9W@T3mThJSQ&=>vC9|Mqwd=%hT48$PZhTCxm z2IEff*Rr}BLogI}6rul=3*Y6#xt0Y zXR!bau?Wv$F_vH{mSH(6@jO=G1+2u2ScTPi2`}Rnyo%TGI^MvWcnfdi9jw8-Sc@vG z!+Uri>+u0T#0G4{CVYg=_!wKT6`!CQpW-uY!{^wJ9ryxY;wyZOZ?F^JVi$JfJM6*t z_yIrSC;W_Guou7LH`HJs_TvB!;t&qw2#(@+9K&&(z#sS%f8iwl#wq-R(>Q~F0qp<9 Ar~m)} diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/5c43454a3823f172a2a326410a36ba6b.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/5c43454a3823f172a2a326410a36ba6b.cf deleted file mode 100644 index 98b2c87707b46fdbaa2a5106d009a1eef23d536c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2136 zcmZ{lU1$_n6vxjb>u$8BerwgXYW@CNGHY~q(FdbazY2bY1iyk#GI!n8jY_hL5h(*L z1r78`=u;sT;)8uy5g#H5Me0+Z3O)$p7l=Lx5=37DivIsIcQ$ui=Pc*mGv{~DIrrR| z%Zk)wPR)|kjlga?5X`A(!7S2}#!qJ8(Xue~1^&UC|J+sw`-kstyEpHh{c*ITNoFVZ zC;t64ojvq$O^o|&;}^ighHNf6mKFNP0&VYlv^?c-i7@1Glyjq zVErQMztZ(5fSdApeYA)OueRj#`l&i@$qv;28$s-!d$c@`fgs+p(I1tZeOQ|{q>YZ) z|9s@&-Y1PC-y+`!egew=zN+JvOowkdl=*qa#`cjvTEDrDN9%u0ZV2l$KkGC96f%!P zAeJQj{@3D>d@9`@hd?Zu5b1v!+|mA%k^WiB6ibqk{`n1?!umK{*#un!ooPbYs4uq@ z;%(7goBSI(4GDP}KJvK+qZ#rF zZ1Q<@8p8hheWdUWlh21n_m}kV2SfPPeftG|3H@J$pZ$GT$5a0O8T9=k-(xm~`x`?4 zSK#FLx4?wHFW`S_zA%Pg(|l13uWJ573?I>aaSXq!`H~p^pXN(r_>!T1?BEm1D#I!tU8vFo^t6-znoM9^3!R@b-K&ym@VfB5g5Ambe~W? zm7OkJ+dGiSbt=x`mabs7^_sD7>w3qBEh zXkZP!110#!yW1poj5 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/61fa28f6b22b739b19ece22ea0b3f29f.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/61fa28f6b22b739b19ece22ea0b3f29f.cf deleted file mode 100644 index b5c3c31b95db18963bd4b6d8ccd828783eab9574..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8516 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 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/6a60c190881f43bb4a91a360b9e8850d.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/6a60c190881f43bb4a91a360b9e8850d.cf deleted file mode 100644 index 101d567b03b3cb30b864af6d399caa79d6cd3e6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96364 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 HuS)+9nCC@( diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/70ae0762d8f82ea593516bcc52c94bf0.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/70ae0762d8f82ea593516bcc52c94bf0.cf new file mode 100644 index 0000000000000000000000000000000000000000..af52b650c9d848ac2beda513180f4fdc811a5218 GIT binary patch literal 1896 zcmb7FT}V_x6rStfDn*!KlpDBOg#Bply0WYFpjfFWn20@8rZ?}kUDGsOHG7C9NME9Q zP*OrmYSXX~su!amA|lMd2m2w!m!#4jB&CQf?0j?YEX#EhJm*VEir$rMWa()Iq#=- z{~h{ntqUExaPq_1ecO7DZ(a<*96Ul?~F~qnE!A0_TVCXxLkeo>#EQk?xFz*8vr zQM7}=Bj|y6ZTUfWjhero)s`QUJuX^+eCfmWaaZG$FAa1LB*vyDjnkg64C%EH!!oL7 zZdo*~jf9);(9PHnBQ^$2;IIm~Ceoz_f-{#^L5KyS)w({f0xxzn3fdfS$@PVn@_e7!_tid;{W&==)A;?6WP~#~ zG@f_`&hSnqI@MBVBnwJqGo6J}Fm}SA#-37JSu?sC_ubg6j1_9cJLnqWhIj!m3k`QQ zIgzqRNv9R$gcVahkQ3a+b0U<3%@6{M{)uM9A4Q?N(L-w_9sgE*k19oe9$ngT#gWr7 gVw!3Hc%q!95IESbcKPn>}j*ZM`gX{0*W8?inRvFI~KkjugRK5{0UHbeA_ zAJ9%fh1`Dw z^)G9E_kZ%|pPcu z&iDUlUIb?SxFF(B!0ZkC%y-C)`Z~U?FCeH7V|453_wx-r-#7VtnE=|RJGSG`ww$(b zZ_WGCuC28d;A~TRo@#}FbAh_sQJ%F1JmW;+f(okAZMV2=UU@4~6QB~-0%_e*!JHqs zG6FO0)}7Gz91FPGsfT{Yvn!sh?qR+Vs1|A^waYS+pjf-&&0vp}fJ;fK6=|)FmPIuR zlysH7q_9h1v2YGnGwyn$5h|9hI`@-1_-;E;Vd(gtJ{9krJkE#Jr=K5x|M)C>g=PkN zL?%fLM*hRguY};2a%y$hM9^l4vp|+?Ub-+=DoxbKt&s^iUbJO>LNyx2MsdovM@r+P P_E$J5zlh(Irn_$p7}Xv?h=xc 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 literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/86d71ff2bb38e064697257d35d6421b8.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/86d71ff2bb38e064697257d35d6421b8.cf deleted file mode 100644 index fca06852f3bbde29d54e95515c7134cc7582add1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2012 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 T8ChCT(4`bv)jY}=*w+66p1fEJ literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/93bb3159569284a5c7bcceb4acb91bae.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/93bb3159569284a5c7bcceb4acb91bae.cf deleted file mode 100644 index df311678125f876ceb449e92ca57f3c318dbdda7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1288 zcmZ`&%Wl&^6dgAykMMpJkPs3q*da(l+cXHIGz}GzBGEQQ-4!|ZBr(KxupL1W3oj6C z_<^pV5@J)t4%P@(u!I#X5FY>wKA>b=^XsE?kAE%B+ zr?E#C9F8yq>Wk3tU~q4)NrUy7p94v4JS`$l^a-W!8lj(6`tA|>1*QMHS-g<@Z=n8F zrSJVuegpb-@H{`ynFjNzuYjb!k79);Msj`wQ{HdC;uDwT`yiNqAQ6;L^e0L`I6_}n z`XTBSni$M~iSR83`*)ahND~A7jr#u{Q|d?HwGc)D{Tu54QSih4&kpsUZ#E4Lw@&x} zcv?iv`e9atpAfS*>@&YZ0_y8{TVFs>KY=l<$M5GGcz)mL?^QvdFgq|Ucdlpkg?+o> z>gMuVUm~3A>W(9OKEioV-Wy2ASVLU30)IhzP2KMIxU3A$ NGD(5|c|V?T@4sw@!UO;S diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/9a17f57112aa00edaa582242bc31fb71.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/9a17f57112aa00edaa582242bc31fb71.cf new file mode 100644 index 0000000000000000000000000000000000000000..35f613a492cd786ec5cecd0d0c6903d525fd8908 GIT binary patch literal 1400 zcmaJ>J7^R^7@p0;m_(yNL{x+X(ISZ6<#mZz+_C-z{gD3}V=udYr#$~B2Ylli63*V-5UO!Pfj{&3^{w>RSaz1h}VcboVB zt`Wj4d0soLcVT?NpgScegue<2HhwS%lOmM1Gqu8BQbL~gioZQE055*Pw+Wg$jQWHL zLCo)s_-&i;Z4uwF34bi&*D{BOjDH{bPepv=2L5B?BTZ#WE0o?V^hH@MM4e6?q%r(MYxR4}f}z-#?ohOv4P zlfp9^c`O89e&$uv0G3D;aRf+HOD9kC_G$fkrkL&Q?##$+e@|9QEtl)h6!lE5r+Zq) ME?8Htgd5iTACp7!wg3PC literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/BuildManifestHash.txt b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/BuildManifestHash.txt index 647aa06..9fc4123 100644 --- a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/BuildManifestHash.txt +++ b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/BuildManifestHash.txt @@ -1 +1 @@ -baff06b928d147276f2245dd3b19216a \ No newline at end of file +e320fc78984f8430afa90a591fd02004 \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/baff06b928d147276f2245dd3b19216a.json b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/baff06b928d147276f2245dd3b19216a.json deleted file mode 100644 index 5cac3c9..0000000 --- a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/baff06b928d147276f2245dd3b19216a.json +++ /dev/null @@ -1 +0,0 @@ -{"Version":1,"BuildInfo":{"BuildName":"ContentDirectory","BuiltWithTypeTrees":true},"RootAssets":["3dcb957e6cb206bfec2aaadd3d11d85c"],"Loadables":[{"ObjectIdHash":"20408785117e138f38c2cc4ca0e638c8","SerializedFile":3,"Identifier":6313987888022012634},{"ObjectIdHash":"3dcb957e6cb206bfec2aaadd3d11d85c","SerializedFile":5,"Identifier":-775554941117088049},{"ObjectIdHash":"dd30e3af3b8bddf1ea36d768fc41aafd","SerializedFile":10,"Identifier":-6933612096100796476}],"LoadableScenes":[{"GUID":"162c015549f8733449ac70ae78ad3aa5","Path":"Assets/Scenes/Scene1.unity","SerializedFile":2},{"GUID":"590cfeb4e0ff90f4e92f9e1262bcfe6f","Path":"Assets/Scenes/Scene2.unity","SerializedFile":6}],"SerializedFiles":[{"Index":0,"ID":"Library/unity default resources","IsBuiltIn":true,"SerializedFileDependencies":[]},{"Index":1,"ID":"0000000000000000f000000000000000.cfid","IsBuiltIn":false,"SerializedFileDependencies":[],"ContentHash":"6a60c190881f43bb4a91a360b9e8850d"},{"Index":2,"ID":"162c015549f8733449ac70ae78ad3aa5.cfid","IsBuiltIn":false,"SerializedFileDependencies":[0,1,9],"ContentHash":"c271b85494f5e4cc35c4ec4a776324af"},{"Index":3,"ID":"20408785117e138f38c2cc4ca0e638c8.cfid","IsBuiltIn":false,"SerializedFileDependencies":[],"ContentHash":"93bb3159569284a5c7bcceb4acb91bae"},{"Index":4,"ID":"4038ff673d390134d924b57fcbed0432.cfid","IsBuiltIn":false,"SerializedFileDependencies":[8],"ContentHash":"bfcf18a244e45f867750e0223a9b3a43"},{"Index":5,"ID":"52b43dad178849b42ac753005736e7bb.cfid","IsBuiltIn":false,"SerializedFileDependencies":[8,4,11,13,7],"ContentHash":"c0152db4dd710be51b2decb997325f34"},{"Index":6,"ID":"590cfeb4e0ff90f4e92f9e1262bcfe6f.cfid","IsBuiltIn":false,"SerializedFileDependencies":[0,1,9],"ContentHash":"4d49f1aa2acf2ecf20f51660e1bf77ea"},{"Index":7,"ID":"70a210050f71a924aa83be7146547111.cfid","IsBuiltIn":false,"SerializedFileDependencies":[8],"ContentHash":"8ad4924ac5e264fde63d8e95a0dab8ab"},{"Index":8,"ID":"a2a42d71dddef12e8889849faf59bdd7.cfid","IsBuiltIn":false,"SerializedFileDependencies":[],"ContentHash":"86d71ff2bb38e064697257d35d6421b8"},{"Index":9,"ID":"baf46637edbb6ff884858f6630b66eda.cfid","IsBuiltIn":false,"SerializedFileDependencies":[12],"ContentHash":"61fa28f6b22b739b19ece22ea0b3f29f"},{"Index":10,"ID":"dd30e3af3b8bddf1ea36d768fc41aafd.cfid","IsBuiltIn":false,"SerializedFileDependencies":[],"ContentHash":"730d2d641a53eeb1e633f2ff60d730e9"},{"Index":11,"ID":"21679be819d6e9146a63bb02a7e51f2f.cfid","IsBuiltIn":false,"SerializedFileDependencies":[8],"ContentHash":"5c43454a3823f172a2a326410a36ba6b"},{"Index":12,"ID":"34814583296b527bf650f548effcd62c.cfid","IsBuiltIn":false,"SerializedFileDependencies":[],"ContentHash":"c046f78347acd69f03fa7f80362fde94"},{"Index":13,"ID":"78532141fd7679a458405eb16bdb75fd.cfid","IsBuiltIn":false,"SerializedFileDependencies":[8],"ContentHash":"51690f09f541d7442ce8faf26fd84696"}]} \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/bfcf18a244e45f867750e0223a9b3a43.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/bfcf18a244e45f867750e0223a9b3a43.cf deleted file mode 100644 index 85cf1dafe8d168f3ac367d64632abc0c951c9ec6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2184 zcmZ{lU1$_n6vxjb>uzj~)@s$7R%2`ZsO^&3#OyBZLt>?Vf?q^X8+0~vmtEadNme0J zWT1*If%;PLsgM>TVjm)}`b2lP2Sy;0qbuqA;J`3j5{a_YpOXE-G;nA`%3~utiQGRAN%=4 zPm?T2>`eS{GM(M~`}nQ08vAD3*yEwio5#N%ncQu!d6E?AFIB57nT9SyE~ZOa;$=m= z=MX=$=a2qiVMRxSc*n427CcoryIqa2}cvO5gO%|jDYtd zKmE*M83kCsg!+$k{Tbkv**OY;r?bpZQsz z`R9;%90IW<;rIV89?4&#+v5<3B{L%Z&xJeMe=^cPYnft6GSWZ4VN+NiXDjbR-+|6I zA#Bu_%L(z;Y5qzbPs&~FxCegjeE~H3{!?-e@8%=;`F@P+h9|BKxC@(n5uJvFyaFHj zs|KSP@+xfd#dI3N{`q~R@C}opKRXO#(kUvNGXny-%GlbXLB!yA#D`PanoX3cpAObPe5P;=f>Q~LLR zSgxo|>!pAHcBfi`u}ckGw<)!En98_c$zGkR|C#j-GBqcd2N!cbKWa(!SRATSUS7zhcB zj?h>e8l9n$4UJrA~<}^jri<0t9Bjt7ZXeuC*yQv+^p@ot}5CYmCNPwxsH6%DXz^ITsNEUE0*2qLG}6h3lW}p z_ffZe_yfF~ovNq?)W>~l>OW{F)(zY)*zNz8x~~10JA1cPe&v79`=clPBCTDgPt83s z?@aFI&s#?x=5BM}4)oCIS!Z2Oe#;k=-#1@ay}jLxx!3mB*n7kGo;>ROt@O0}!n6MX D!<#!A diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c0152db4dd710be51b2decb997325f34.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c0152db4dd710be51b2decb997325f34.cf deleted file mode 100644 index 03a4020b476b31c444bc9dab59bffa2866f6a528..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1524 zcmaJ>Jxml)5Pk~>sNfI&CSq{~2_4zp+uwv3k&8-1Jvl;W;O)J)vgh6{E(-}(TNnx( zt+XW?ZT!K8SZJg(v7#{{T413u@d{(1(GF+!_Kw{nGMky5H{Z;BZ}z<`Ax$~a^hoAs z-S7Anzq1WY9E67)fu$gT@$2zdAGOtu+Z&~|^}_O{0s44_>fdCox9gGnr1rJD@M5Cu zW>2l1Uv;M|nVW_Fa=x9A%1jU-CF%qyw(Al|s1p)yfxr^H$VNth45f{P3y{+T5zHUn zhGE@CHsgv6#M|PWMhfDC?Ihd_fjywGg{a3iE|6uwuR(w8jXi_}0rJlPe=X+kXcWfp zR{l<`PYlq?-?al5$OYhk3Pm_S?okNQ)&=SX3FbpZ9On?u#31@G5zgNSJ#g>)wxP&F z{3_sOP@J!O2QHAq5E}r+{`id(+lT&X{{1`fH2(p_6N3EMANjHWLBv8dbrDD=nqP(` z;)gICqN$5OI#ToZLO4BtXKH@rN{T=_Q}g2;P73mC5V#DAvmZ@@-~)f^ejSTqVKWQy z)-_IW8Z~lgdC~A zgvwNrnJzKLxGl-tFbvaBOq<%OX)&e`mTZSLvr~>D<_z1`^c>5XqLfpZDJzz$+a-%L zQOU`>l**E(n-(`DlWE+P6pd;*%MxX(b5*isdzVt(P;;`RNH)`<2US!<5mjzUn#C+# zwb^c^B9$dkR7G2tC~THeBWH0P=v76RB=6EBnGdtcm+xp+>&E0paulxFYvXEPJs@D^ljnZZX{;gK-3`~An`t})b ve&g=Y!11biUhyD?yf2p?jE~lWEdCveC0^xY&RhUKp8NZ=OZPIt!@vIkg~I`$ diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c046f78347acd69f03fa7f80362fde94.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c046f78347acd69f03fa7f80362fde94.cf deleted file mode 100644 index 2126d126cc0f8912debfdf870873007c41f90498..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2260 zcmai$zi%8x6vt=pOcH(t0t6CFNJ0|A&v16mBB4MRJBoc^lS?k($P$Xi-Z|fz{iWSG zDWpJGfhcI`ph_YH1)@L-1qBiXe*#EUga&A!K!V!&es^c~_U>$W*36rEpLy@i&piuK z$YVuI?EE`#@A}c`nq{>U_!AGK>o};MpnY@mo6Gxa@s7V5fBgFIKP#>8_wEb7`)H=O za^T?1W%u~P?#T~#3K4X*W{MLi-@FJP01!Cch=%39ABegHpxBw_#xZdr?N?bA4Ud z>31JWS?v0IFp}SIv>jvl`^LVTpus8^a-QF(=<<9Xpt;W~m(la%-2UtQ_E^WlfB5_! z8squ-ea*NiytAY8uABJBN%{o_t%y&M|0{65{bvR!YmxK& zVDmq_1?T&^W#aQYG914;w13Y753g@x===D>Sp12h@8gRjJh;+K&-igy&UIxND*t?} zy(pBv2(B!9uI@rFC~v*1(U^;Z*jHLL1G%o+-Zcf=k{73Kop^DqTy!s~tGbt{b5RmV zx&Y5by^zgjdQnMNuKILqrmaM^l4wn(Y3A5m52Hk3cp&3NFYt`B?ae1qFMdM?^hG)) z120_9N2gnuT8S4Wo_?2^I9WRfGrhB3pu!aE6!VM5)K*#}B!i7x&UwC8$x`Gp{h829 zBWPrw^&}mZ6B)m0^jk(RLoagQu!3SY<|02z+Ob??oR%+j2m3VZN2|i)EDyO-OHnMZ z8FrW_@1G@#I-OK$5lfs41$Rb>Ti1U6;?i#$#YgQk8vjT5D>9!b6}Gk1-dYyuG@|_u z`_0#@nK$b!LDm~ov{ekVoZ1%tf?7M8g)lB(-vieYWqnnyx>M6rGxd`l Rw_dNEoIX8$O5wi=aR-A~QO^JX diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c271b85494f5e4cc35c4ec4a776324af.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/c271b85494f5e4cc35c4ec4a776324af.cf deleted file mode 100644 index 1a9ca1cf91f06807860ab3e8f6fbb5b95ea98da4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17572 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 Qym=?Zaj9xYDFg5F|HCiS;Q#;t diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/ccccf25fc67d2bea8415c0846c1602dc.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/ccccf25fc67d2bea8415c0846c1602dc.cf new file mode 100644 index 0000000000000000000000000000000000000000..560c2aa750cdd703cf2ff688edaf7bb0559ef234 GIT binary patch literal 32252 zcmeHQd0>>q*`Ga-0MP`(X^^Z4h?H_)wiXg{KqMp~2Zx|xlPt+Xvb*kXxD;Ixm8z(% zN2{e36~(Is6zhe66l}{!>xGI+t@?RYtpeUB-|si?%t` zpZM2wX!JzHJ`Qd#oEwfbR*2Xkki3b>U-{z@ z4$owlh~4F|a0fyiv6OfO;_Y$Q6OQTI5m|vayn={7gY^C3C*G|CJTW1hxR5kH6n^5p z6wYdqG>-B~+|vqPsPL0p!Ov9q5QXb{>--xOK0@JU`4%gDjKXC-5ytcfP@_u}ej4%R z4si@FB;cY5_>Iyp#3bU`aEh4z3E=B-pAI*fIEXwYG5rwm?_v1K^{EOcMk=rO*80Mt z+!ywH8~jUswJM;os?-~f_-YG+;YJZ^@J8ps6A{6A^CG@zMAQTuLt$S85sG|)x@dhw z)cRV6B|;`P1gk|P>aFwT2AcxNps^~?7xD#aeSsRkkGI9az|>%^f1cl4-QcV6Esi!J ze}r4*DU3|?F7VajJ_iqtPO2=-)Bd7hjklo?8AXxU7cLAG_#1q(uDf=`20goe)8@|q zJMz~Z9(iJ4EMwZL;m7a1clWKwRKA#fUYCwH1V(*2;Pq3UxM$+Z;J=EhW6Z1TN4I})^ycL~UYj%hbN7O7nY}mmc)xbTm-ii-S-EfP+`R0R zsuA1kK1sXsvq#>2`PR`7hK|1~>&KVhe*NuzyA2yi{&wTfp+k>}U*A3N=F3X&Y!`d6 z^H#^@BVHX@{LHfhx)&7fn!e}NjopN;YaNRe@51R$a#USwg-&IO7F)YJTq;~b8rf=;tS=QtXcTvUGZ|2(|*#+=P7zsMM|{^5p~4i0?g;aADt zS^PP4=(&$NKHO{9o|8V?@%GdAKL60l(#NlxyXo~6_r5uI@y8Q;4f<##WLb|yZ1yW5 zU%J_js=G8FiKM1wH=wx6?vf3UB8oG`@kHG7S=7+!`l0Y^{1k;#15+bwBF^qI0s&0M z=a@M2FHktUZZf=5;T>^hIW_6$`w+nVJHeUhi;#Z^;Y>%BU?-+0z6^oHsfNt_iC>|@ zX#p7BRgUyGBbePqs|!XW>HHs*ej%+7BfaGBZs06`SGZ*On+m69HS0(IeW-A%BYXLs z0+t2>dcjfEXmzCHX#V{O0epTRI6GJk(`E5y*Ibs8wmIsN4#^#w@HZgDPI0ztIF2jI%ZSzC-FS=mUjT^7MHgn9Raep@rl)xJom#v;rJg(~fn`Zp`ua)N) z?K!dCf{&kkZ&=5lq?A1KQ`(4@I5-B;o1nFsmfxwuH>mqzkQMwFZxvhk%e}2DClpT0 z8~^1nZ>sn~NUZfVD>Md956^AsX%6e0ovvOCQQ(4f`j+Pn_{+G3yu_Xa6ED ziwsz(Q*fm)2`RL(nTdvf(3wJ@%pXuRVDAtz*u)W%xgu zHcpzleD$2`etF{)tG0*7-!r@Z!&_$l@%qBAm*4W+`fEBiuKI1CJ5PD}*gy8_T|M!S z+dhAG*q@dSK6!8b>9a~tO>cj4-q!oS=+-b}6Y|vDWB$~3tR;=MHn`^)4(`#hc9JXG zkVuiKWi(eb?i5ZB{nRql@UpdH46z7CPuRa0a|84|m_M>Sm(^qp9ho=kgnz!+{|r~% zqi3$Ga+me~tnj!u8?Ld5009v;B;%XQ4gt2yC8179x>b~Vk?pl9^4bZ zh~cqBz8W8;@T(QB>q~7p1z~z#cZ0&Ubn5gREt!8u7C4sBmWc@ybXFa>!5-XHt!^kN zPy$PQVIxfQjhdk_Q0s@b6Dm!dr%VjR+87Kp3rajunUpZiqQ0r#$O37qe0=ws;$OY> z>9y-dbiJqDfh)b6uiJk7Ip==m9eY}j3y1%@Pj_>2bIn(MPI~y^UWIQwy)$M08@b_Q zawqv>S&@eZA9wq$^Itl!`-7L4E-lKtq5G=OZ~ko1%D?Tr)YgbJZRWvgX139Ya!BKF zO97URPLCqZ+gLz>t}P?f1Y>WxK91D&)EI1rCTqZ{p?fT{p-&j zv8Hxi$GUwFJn_K61s!`eJ@WT6w@>T;5~ugNi?OCX;8>%hRu$Pur08f=1v#&~j!tiJ zZK5Ay9;iz&kHz{|?M)XqE<5;#Tb~*A$4v!$w}1M@*spFFwEOrm1?Pl2K3Sgs8uHTB zp$6bMq_b~B*yMDOb|-~_&k+~%$098Z?VXl=)lmcLN{zrVr43<|_WMr}hb$hWr3`)& zg|QIV*>HURSmLo}!YFZe9OC(`P!c?%@NzyR363C{-pc_s3I2112dQe4;MXX88GCVa zTy}s0tMu0< z!9M`zM&X$M^$K?*jKusY?wu5Vo5IN^QbK=nHk|0gQU@nMYaTCktL(kdvA8N7EwybT zu2%ga3<1+Ir-nM9w;=4TgE<+MH1slEofb^DmmsEtkobBT| zlf97^vwtfX&#Lj&Hifa)WiAgOLS@9KY++@q%DY5K)v5qYS!Ptq!aI9rXlY?W+y6xjyt zZ_4$u;38`l$t>Sn)7P=}!vlSe`DgFu}#R6QsB%x-fr-dH13mrA>;~9HW7i-+p^7dk79c6HC&5erF|-f-dykNwJ-Zk>ZvvfV6Ul!)10CN#Fxus zaR$j2;2KtE1hpmsv{CxQk;6JacqLH+h|`cIt|bm$No*k+yxF*;1Tc=Bv;>qOkT};M zMK*sdQiDsJec*P&5>}QTThy}o;4AlXfk-x~HNZ`6YKZ!+Q10en-9N14)|?!r*AbChW+co zBji+=;w}e<7(*;dh%>}ZE~(2Ry_-<$MBpkma)X6v%3a_aPYLdiIG;wszhVP^_?9j>k;)O951`l!eHL1+CXbm{OHF1qo$?aL>5 z-ba|OC-b9y&w7&D5G`qIc10YDhKwI$3X4!q*$;={miQl7;aDPH)`R_A+Z+!Y9WfzV z(3F;vLuXODI0?|S>WWJ8BuhV^6JF8#+2@|WY0Rnai?4Y1Q_~DH&APQHot*`^l`J@b zA~s{eav~O?3N3qAOruQSd^x*iQ#(Cb7`!QxWRVofX88-zFD?3~wA4RelG%Oru~VMf zw(!b_KbiSLboSVjM%*7u={f)G-FTe6+yimXrucqn1#5>czt)fC04#r7TXm)WkgvfX z@X2!KzPx?GXMx^Z_B>S3cSXmu{pR}Z7NdUL>e75{)-EvsiYQvwqYH9yr&LMNwDl9> z?4o*`LFZ>nFQ;)dLgMWb)2K98t+jT!hHae(5Fs!21^6U$0}h#rVoD2Tq$;@zQU1J=Epy z4PX3i)LH8{uiJRzsrjdVwDhWrcAWWweT>wcVLhlYnu*DBT}sx|T7USBQYfaF5Z{3G z9U=UwNTzKg84vet1yg+Aj9vEiZ}z^u^YPa{_|xan0gH3HN1t-^+?jt~{lNicFWkOr zE7I8cor(MJn%`&-4<2&_W{D+3@pCpyWYMNPd9cU+3*Nddzi7|nwdVxCGR@dyzz1C= z>W^Q-ku{`dtdGec`9s#Svou?*U(piBri9cf(yWYGtYR#kK1bowGzPAptMO8WdvF!8 zM5obsHG-+L$`x*=KS$xx5I}mZ7Iped6ke~=YY|b?iMZJ-XAL1=jXW~t<@Ttap35kN z?Upj{ZA-|!)$y(1mLbZ5CQz9(J)WyFr>*lC2huK`HpO-9CI1X=?~xkz+&})KuP+jE&9rw$az7boy$_+h?3vGW7U{)w@q= zcm2tgc!KIEp$&T?=f$g+HwLy=v9Q!#3V3$G+4baCmI1sZe5qq*awnbC8`Y#Fp;;_Rvq zd;k9VOz*Pk&%OJ*jay$^-R};RT33W>opUk__h|BjTJyCd1kNiOU0LF-EdT=O4l?ad`qHra)XYzD(i$3B~wh zktrl)6%!k9e#=^GHbI*NMZz^QBF%-S~ zJ?fpcqx`a0dY8Vs!d3~L87u#7Z3wCXsw+x>R97)%s)+fUUEJJ=U5hw&<}xYM9W`^e zF{MR^v(acXF#l$gt(Z=2J^$$m<1Z}e`RSIYe);I47f&nQ`)KBn-+g`8x_KMlnJ~cL z>B%W6D5Neo>zxP3a+BKX(QHhvN0DDLehfE?*kg!~f+L=+M-x9);mLY5alPVc_GnwD zit?l}4l~*Ifm*2Q5I2VAdHoGyX#szEep%Mh>@enX)h|#6%^S*^Ijx}vGzoQ=6ER}! zWSW;cp2lem5!c3y#uJNd`GJ}sj)m%TL=C~HjPv)jl&4F-nqS{-#*BL>++O0m7@_t; z>S@jvWmHr5o5VtJp@QMMkT}WDTS>&Zs;!OZZ)O zW&3E&E+bW23=dV*+_G#wXq#s`%oDob*LE;fyEzTM26fl%Kz?}Ow4_je%&r3%xV0Ig zdx4HmjHGcI0mRHaT29a-@&KQRdc$=%SWPmGr1c21m-9!s*ZH!Xt@S{z*U%DV-;m|h zokZR{JS^jhaJmP=8>bf5Tf|zD7YY5Leaza_Z04D^@2iMR``x&$2d^)A-F?MXx6j}B zYK1vD+JU-h){}c4IM(;5yJ|KPDLRr-mr*(DCN->EpF+m1oGmMtJ@M&zCtP~*tNw`} z^gVXIxg@#*`RjW92dx~b^l9agSJKL1MHG>5rj=tJFI$T^x?IeI^o{tvO(vB;N5b#J zSvlrzjJaELD*B@?H;~7gunX6UDL9>ZYiCB}~69ljoZ! zr`X)_VFGoBeIMH$c7JSl*aJ#DO|Q}D-8&gi1U{?5x{SVJ%GToa5o_!ql3+r^V$#TryL~hgTArIT*fCa7p$3VU$YSJGOk9IRuy| zOCLlWT`S$(On20(bQ=;WI$YOohY1X$wl;xDG^*`r0SjfG%P?1-GZ9HwhP(8W;Os*( zw^1a;)D;)XJDZ68Fy2J;DAM@1IdL=^{rQ`oZ82Be?M>cTbwnqMxGzsK-#GZ0ja{6q z6~fusMGM#@%?_%aw%Enx6yiERoe$IB1a_aSa6Qb~aW=9l2j^HA3xY*nHx4T3w8 z?#HoCn{zV$HY8M}o3;<`$tLPaJ7G>9Y3r1m^&%Kw;$_a}X0`be`+7*KZWJ1<{{wd< zHK36=0IoF!!WvSe5$b6e_Q*C=yT5>eCDCMhFy2xt8bWQuld@cIZ&9p z?4f3ZUOwrU` zDv<-k*m#xe|Ervrsd7>+BrB&WH?5VEs^Vz)>BvrS<4E{94ZLiNpXM>GCy>3CSJtZt z?nqQF>oE{o4sAs$j?Gpht8z7z*vYcMCfB@@v+|Iy}A4I zeH`l(YS(ID#&J~ z>e3?&ypj?{)|P3r#Re}Dx^jI8bQ#|N__ z`&2*SXlIc}dw^S$CrR2_WEJ`|JGpK!=|1kxk}Q&fLjdfjGL-dT3_4Z+SREuG1XNw!s~LTmILr z*(0AhG-GP)UC+<9jzb}IxwsCK2gh=enyHhR43a-&CYRcPx2B^du7_4RAB!QfBAx!H zxTlV1qly@REYdgygSdIffH;SCeF&i!lf@F{PK*K8o}sloXB`eRG25vhYTTt_k#%;A z0%;yXFuR<2>i=G((Y$oSp9V)>ewV7kC7d2S;t1AhI17LA{#@L{;xC0OJ63ZLByUaW8``(){7D12ip_*{kW zYX$c!oS$(@mVa2`BU<8eK7S5yLTBaqt>Ek`EWfn1VhOp?<$qJ*E0d)E02uko^j9c6F)}zj#5*f| zT`Rap;oFknekeul!|X$9x!u~@$Yt>A1o;-4hJn-KO3 zg@2`RH|j(Jf2E;v1Hu`0ysnr;6Zp*v&uoc%)&nEo`260)V~N>>&i~IU{V6TeOZomA zaQqj8h-bqoGDn%@Cr+CkM-5vCO@|5P8LmAev}dIDjMARb+A~Ib#==wX3t=@r>az~6 znnkeJvj^8GeSt(SO$R2=|W-%{V2WEz&^yCL> zdCB94#PFdpv!?c8AJOD@qZnUqBGtv;S;i5$M9O3@_%-m`Yh()TBhv;jk71iOgL$aa zw7<+Z18F{` zW8oZ(S#LWL9}S$V&CFX4*4c>|33j$vHn6{Z9$%IN%wjC6eHQ>N1HI@njJSjN}B8#rg>&+4YIHoQ(I2oa#G+ywt|xa zT55WxqpNSXq*w)0piOJg6wsweqsz?nWQWeX4S|$j3hIow6gU~*wmy*%7}fux{Adbe znFl7#S5Y=2jxG&rN^xbn>1bq@h9?Fr@@Nn6mMb&528^U}){foBYG@)BX`C1x1uR*5 zb{ieXj)z5^s1{zJSjjX#7`PT;x;{)xV%npgLS!?gMMtvQz$3|AZJ-2dBP5Yt2=i#O z`DJDrnD%nB6a6>J%`&!D?q(g5g)y~+`Hinlh-0q=h36YpVjB`FIvn%u`|Io)lKC;6 zy5+}eSRV5uKIZn(1Bhd<2NzVn>w2J`9z5m<=AqaUv!>;;Oy83p__{9S%@8=& zh18b1;p{9EapLMG#uOHz1IzV;{1ltN_b~FZ9UIYgYv8>M*HJS|&u3q|;PcFWLLTs$A^% zMvzLOo6TN6xq>5`lXa~spgC&QO?f3|HFxrKtXq|Q-wG^+pY_OJjCx>s&SSlYg%B&x zBjGW-iux$H&PL|K6PDJu!aX|Np1tH%vi$jMmR~#ao-)yI623?2ZHT}t*GsH#p!&jj zVecZw?E+^Hv*gCo&E&Dh@Z~e?S0|SbJ5TvcyDXA#S_2h5XLX zZOQv&Vae-}7;g&n;*Jy&&PA+o2;p4o0nsFnhv|hMgtdJBHxFx89%a|iOq_$wLyoDO z#pZeg_yVXkD(6HSa0c13G4SnSAGdu9Cl!~J<(HO~oSvUsQBo#o;}PqGD?Yow(C=F$ zPlhl*ERx{!L{{F@D)TGz;b%O2q%;u0QXW5SO1r_bt7NhkIABS7sYcHobvVay8ayyIVWj=1Cu*Wy^?u$6d@@BZsQ(qAz7@jO|9iMSDu`VS2 z{MN2kEbDpNuj}N|0W1V)7#uO}Se7`cIK1Bg$2d-PCEwB+K(fW-h697i0}|uQWiok( zAH-!nD9@4@0px*WIyyUc;DxH z-BxB9+aZAM^nKJ<H8YsC06U{Ln_T2O}QGozlXY`98Rpf%pYY;vP3njpZU4C)K zub`1;h_~6T^%Qj&3)5e`sHdEqqHger9>;pboqGBia`Y3KUGl^{i#LA}sSc6l8YNQP z9+CQM{`sFQ$5q1JU8FfWd?~WpeJN6V99JZMm5bSH*ek?U_2Oyl<%fMf>rXI5inz#{ zH82(xmNgmp5}_ z?k#_9obBH5=P%zmd%*QsC99X-Ao^Cg0^$SbV5C_MN2Og@KB>6=Vk~YtGD}a#w|2$s zG_>_BaHSMpVgiu9CNR%Ms zIe3eJ*J@mYXu0y~RT0tdfViuCa$ebKXtQiQ=t{9$3h5K}u1J7FNNZE_VapsM|*uL74QLx+5d3=o{v)A3O43}GExKrD?H@h-B zZ+5x6x?i^UxMFv22+3|&*Zq#OzaGhkHIrq8T{OQV{-uy|;axn})UHjlC?E&GGA^EWP+D~@4 zQiQvor~m^yjc5>EV#1jY6Q^@_mdKdk7Vg2~*va7PX(ByaxI3YE#|;xHjv_GNa;6hS z?u_ldMaFIQj+NxPi>Lrc=;yE~x2 zWI48VPR(%q&hcTpWGhiu9MhxcOe)Uw4(OSE#aJwS^%QQDer1;|M@pB})D5CX=h6-- z72?XrAmmOo=iGJ~`yKiJ>nq$)`NpTm^l1O9)VICwjuive07+l-Sl2FMZo9Po?g3f& z0f+XxJBiwM8H4j+vx+Rwo_3DTI2U$7h4r0IK?-84suDu2K3 z+`aSPExxSew@+2>Tzj_|UnnvA4^2sB*ZujA4v2Bhf%9U$S16k83C1R1$`RjdGcm4xnR`NJ97K_) zJ`|sRT)tpQb#U>I^`EmO3|rE26_soOi*KyANh~LfBw0_5F@<(4tCfRBt?Q@wa$SFT yYrOS88S3+ntvPj`cSP;zalUzDhmRgLYV3$HV@Hjx^^U{e+8$Gjczn)(ivACRPOT;Y literal 0 HcmV?d00001 diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/e320fc78984f8430afa90a591fd02004.json b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/e320fc78984f8430afa90a591fd02004.json new file mode 100644 index 0000000..796b02b --- /dev/null +++ b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/e320fc78984f8430afa90a591fd02004.json @@ -0,0 +1 @@ +{"Version":2,"BuildInfo":{"BuildName":"ContentDirectory","BuiltWithTypeTrees":true},"RootAssets":[{"SerializedFile":10,"Identifier":11400000}],"LoadableScenes":[{"GUID":"590cfeb4e0ff90f4e92f9e1262bcfe6f","Path":"Assets/Scenes/Scene2.unity","SerializedFile":12},{"GUID":"162c015549f8733449ac70ae78ad3aa5","Path":"Assets/Scenes/Scene1.unity","SerializedFile":6}],"SerializedFiles":[{"Index":0,"StableId":"Library/unity default resources","IsBuiltIn":true,"SerializedFileDependencies":[]},{"Index":1,"StableId":"80b335c0244a430ff7eadfc72ee8fc6a","SerializedFileDependencies":[],"ContentHash":"ccccf25fc67d2bea8415c0846c1602dc"},{"Index":2,"StableId":"21679be819d6e9146a63bb02a7e51f2f","SerializedFileDependencies":[4],"ContentHash":"38abbace71a8a756c679a7ff3f501f02"},{"Index":3,"StableId":"70a210050f71a924aa83be7146547111","SerializedFileDependencies":[4],"ContentHash":"70ae0762d8f82ea593516bcc52c94bf0"},{"Index":4,"StableId":"a2a42d71dddef12e8889849faf59bdd7","SerializedFileDependencies":[],"ContentHash":"79bdcb3e9bc659d3519594280240cd1b"},{"Index":5,"StableId":"4038ff673d390134d924b57fcbed0432","SerializedFileDependencies":[4],"ContentHash":"2357d63c989be24891a7e85b77bce4a6"},{"Index":6,"StableId":"162c015549f8733449ac70ae78ad3aa5","SerializedFileDependencies":[0,1,13],"ContentHash":"8ec44fb05f86ef7dcb7bbef683d8a330"},{"Index":7,"StableId":"34814583296b527bf650f548effcd62c","SerializedFileDependencies":[],"ContentHash":"214959d6771e8c749dd41eca123456b2"},{"Index":8,"StableId":"20408785117e138f38c2cc4ca0e638c8","SerializedFileDependencies":[],"ContentHash":"9a17f57112aa00edaa582242bc31fb71"},{"Index":9,"StableId":"78532141fd7679a458405eb16bdb75fd","SerializedFileDependencies":[4],"ContentHash":"491d099506657727cb6683c78c0e2383"},{"Index":10,"StableId":"52b43dad178849b42ac753005736e7bb","SerializedFileDependencies":[4,5,2,9,3],"ContentHash":"eb3abd5ab5b0d790980fe9e9df872484"},{"Index":11,"StableId":"dd30e3af3b8bddf1ea36d768fc41aafd","SerializedFileDependencies":[],"ContentHash":"39105176560358e0cd444e7a0a513ed3"},{"Index":12,"StableId":"590cfeb4e0ff90f4e92f9e1262bcfe6f","SerializedFileDependencies":[0,1,13],"ContentHash":"3cfc30da76f416cbd860bd47c9e5ed95"},{"Index":13,"StableId":"baf46637edbb6ff884858f6630b66eda","SerializedFileDependencies":[7],"ContentHash":"812fe00f6f62ce8504f14dfe20976ba2"}]} \ No newline at end of file diff --git a/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/eb3abd5ab5b0d790980fe9e9df872484.cf b/TestCommon/Data/LeadingEdgeBuilds/ContentDirectory/eb3abd5ab5b0d790980fe9e9df872484.cf new file mode 100644 index 0000000000000000000000000000000000000000..f53dd7594d30d46d2cdd58ab0577be1518bb6163 GIT binary patch literal 2084 zcmb7FYiJZ#6uy(#CO#121AKr)2nf=Kx%1eWK@lY(DyF(7AcD_3d+#KpyE`(DkBC1; zk^U= z=gfES5>k-`6^*nkO7+%`bZ*H@pV|pOG8vK$-}yCOdT(U(M%%yosi`;r zdboa0-{#}r%^befJGkgbpy1Y7zAGm$;Y&sS&NQqX$E!Cxe|R2UdMvA~1WJWz%+R<+ zIp@Nony_u#u?@##p6R%pTb%{Z=Q8;H*-&-c^KzEXbw{PT!5z(TP0K5|f~$tEHGpa~ zXF0C0sl#)^p+=77bk|iiW(kven%4ko*`}^hgL>S8HJD-AhH46z=3MStrpFsWRi;r@ zHC4}|43cHc)?H!2_NHM`O5Z_|jI|%-Td+y8;>Vr^16zz_Lb4`AfcI)aGX9g$BO@X> zfmf9f^mhW;rh)~Dh9s-@mv;2{MX|PAZuagjo&(OVpd5*E)a{3GID&Af zF9;$`4F7z41IF>>tlG!@psVQb>68B+DI4US-wv_;etDoOQbH>TY>7f7Pyhe` literal 0 HcmV?d00001 diff --git a/UnityDataModels/ContentLayout.cs b/UnityDataModels/ContentLayout.cs index 5b19dfb..d75cefe 100644 --- a/UnityDataModels/ContentLayout.cs +++ b/UnityDataModels/ContentLayout.cs @@ -3,9 +3,9 @@ // // See Documentation/contentlayout.md for further details. // -// ContentLayout always represents the latest schema version (currently 2). If the schema changes -// significantly in future, older versions can be preserved under a version-specific namespace while this type -// continues to track the latest version. +// ContentLayout always represents the latest schema version (currently 3, written by Unity 6.7). +// Older versions are preserved under a version-specific namespace: the version 2 schema (Unity 6.6) +// is defined in ContentLayoutV2.cs as UnityDataTools.Models.V2.ContentLayout. namespace UnityDataTools.Models { /// @@ -40,12 +40,15 @@ public struct SerializedFileLayout /// Index of this entry inside . public int Index; - /// Stable identifier used to reference this SerializedFile from other SerializedFiles - /// in a way that doesn't break when the content changes. Currently based on the cluster or guid of the source. - public string ID; + /// Stable identity hash of this SerializedFile, used to reference it from other + /// SerializedFiles in a way that doesn't break when the content changes. Computed from the + /// cluster or object identity of the source. For the synthetic built-in entry this is the + /// resource path instead. + public string StableId; /// True for synthetic entries representing built-in Unity resources that are not produced - /// by the build (currently only "Library/unity default resources"). Such entries have no ContentHash. + /// by the build (currently only "Library/unity default resources"); such entries resolve through + /// the PersistentManager at runtime and have no artifact. Only written when true; absent means false. public bool IsBuiltIn; /// The source assets included in this SerializedFile. @@ -55,34 +58,31 @@ public struct SerializedFileLayout /// other SerializedFiles that need to be loaded prior to loading this SerializedFile. public int[] SerializedFileDependencies; - /// ObjectIdHash values for loadable objects referenced from this SerializedFile. - public string[] LoadableDependencies; + /// Indices into for loadable objects + /// referenced from this SerializedFile. + public int[] LoadableDependencies; /// Scene paths for scenes referenced from this SerializedFile. public string[] LoadableSceneDependencies; - /// xxhash3 hash of the content, used for the filename (+".cf") and for lookup into UDS. - /// Matches the of the corresponding entry in - /// . - public string ContentHash; + /// Index into for the artifact holding this + /// SerializedFile's content, or -1 for the built-in entry, which has no artifact. + public int ArtifactIndex; } /// /// Records a loadable object in the build. Listed at the top level of the ContentLayout so that a /// loadable's identity is described independently of the SerializedFile that happens to contain it. + /// Referenced by index from and + /// . /// public class LoadableObjectIdLayout { - /// Hash of the GUID, LFID and IdentifierType. - public string ObjectIdHash; - /// AssetDatabase GUID of the source asset. public string GUID; - /// Path of the source asset. - public string AssetPath; - - /// Local file id of the source object. + /// Local file id of the object in the output SerializedFile when placed, + /// otherwise the source local file id. Identical to the source id except for MonoScripts. public long LFID; /// Identifier type of the source object. @@ -91,10 +91,6 @@ public class LoadableObjectIdLayout /// Index into for the file that contains this /// loadable, or -1 if it was dropped (e.g. server build shader references). public int SerializedFile = -1; - - /// Local file id of the object within its output Content File (the one identified by the - /// index). - public long OutputLFID; } /// @@ -121,7 +117,8 @@ public class BinaryArtifact public int Index; /// Content addressable hash. For ContentFile artifacts, the matching - /// can be found by ContentHash. + /// references this entry via + /// . public string ContentHash; /// One of the strings in . @@ -165,7 +162,7 @@ public string FileExtension /// In-memory representation of the ContentLayout.json file written by the build. /// /// The Layout is a companion to the BuildManifest, recording additional details about the build - /// (including source assets and information about which object an ObjectId hash refers to). + /// (including source assets and information about which source object each loadable refers to). /// It is not shipped with the build; it exists for tools and tests that analyze build output. /// The schema is subject to change and there is currently no backward compatibility. /// @@ -173,7 +170,12 @@ public class ContentLayout { /// The schema version this type represents. // v1 -> v2: added OutputLFID to LoadableObjectIds entries. - public const int CurrentVersion = 2; + // v2 -> v3: ObjectIdHash deleted. LoadableObjectIds entries trimmed to {GUID, LFID, + // IdentifierType, SerializedFile}; LoadableDependencies and RootAssets became + // indices into LoadableObjectIds. SerializedFiles entries carry StableId + // (the identity hash, no extension) instead of ID, and ArtifactIndex (index + // into BinaryArtifacts) instead of ContentHash. + public const int CurrentVersion = 3; /// Schema version of the ContentLayout.json file. public int Version; @@ -184,8 +186,8 @@ public class ContentLayout /// The SerializedFiles in the build output. public SerializedFileLayout[] SerializedFiles; - /// ObjectIdHash values of the root assets; resolve via . - public string[] RootAssets; + /// Indices into of the root assets, in root input order. + public int[] RootAssets; /// Loadable objects in the build. public LoadableObjectIdLayout[] LoadableObjectIds; diff --git a/UnityDataModels/ContentLayoutV2.cs b/UnityDataModels/ContentLayoutV2.cs new file mode 100644 index 0000000..174795b --- /dev/null +++ b/UnityDataModels/ContentLayoutV2.cs @@ -0,0 +1,170 @@ +// The version 2 schema of the ContentLayout.json file, produced by BuildPipeline.BuildContentDirectory +// in Unity 6.6. Preserved as a reference definition for users working with Unity 6.6 build output. +// +// The latest schema is defined in ContentLayout.cs (UnityDataTools.Models.ContentLayout); see +// Documentation/contentlayout.md for the differences between the versions. +namespace UnityDataTools.Models.V2 +{ + /// + /// Category strings used by . + /// + public static class BuildArtifactCategory + { + /// Texture data, written to a streaming resource (.resS) file. + public const string Texture = "texture"; + + /// Mesh data, written to a streaming resource (.resS) file. + public const string Mesh = "mesh"; + + /// Audio data, written to a resource (.resource) file. + public const string Audio = "audio"; + + /// Video data, written to a resource (.resource) file. + public const string Video = "video"; + + /// A SerializedFile / Content File (.cf). + public const string ContentFile = "contentfile"; + + /// A manifest file (.json). + public const string Manifest = "manifest"; + } + + /// + /// Describes a SerializedFile in the build output. + /// + public struct SerializedFileLayout + { + /// Index of this entry inside . + public int Index; + + /// Stable identifier used to reference this SerializedFile from other SerializedFiles + /// in a way that doesn't break when the content changes. Currently based on the cluster or guid of the source. + public string ID; + + /// True for synthetic entries representing built-in Unity resources that are not produced + /// by the build (currently only "Library/unity default resources"). Such entries have no ContentHash. + public bool IsBuiltIn; + + /// The source assets included in this SerializedFile. + public string[] SourceAssets; + + /// Indices into the containing array, identifying + /// other SerializedFiles that need to be loaded prior to loading this SerializedFile. + public int[] SerializedFileDependencies; + + /// ObjectIdHash values for loadable objects referenced from this SerializedFile. + public string[] LoadableDependencies; + + /// Scene paths for scenes referenced from this SerializedFile. + public string[] LoadableSceneDependencies; + + /// xxhash3 hash of the content, used for the filename (+".cf") and for lookup into UDS. + /// Matches the of the corresponding entry in + /// . + public string ContentHash; + } + + /// + /// Records a loadable object in the build. Listed at the top level of the ContentLayout so that a + /// loadable's identity is described independently of the SerializedFile that happens to contain it. + /// + public class LoadableObjectIdLayout + { + /// Hash of the GUID, LFID and IdentifierType. + public string ObjectIdHash; + + /// AssetDatabase GUID of the source asset. + public string GUID; + + /// Path of the source asset. + public string AssetPath; + + /// Local file id of the source object. + public long LFID; + + /// Identifier type of the source object. + public int IdentifierType; + + /// Index into for the file that contains this + /// loadable, or -1 if it was dropped (e.g. server build shader references). + public int SerializedFile = -1; + + /// Local file id of the object within its output Content File (the one identified by the + /// index). + public long OutputLFID; + } + + /// + /// Records a scene exposed as loadable in the build, and the SerializedFile that contains it. + /// + public class LoadableSceneIdLayout + { + /// AssetDatabase GUID of the scene. + public string GUID; + + /// Scene path. + public string Path; + + /// Index into , or -1 if not mapped. + public int SerializedFile = -1; + } + + /// + /// Describes a binary artifact produced by the build (SerializedFile, streaming resource, manifest, etc.). + /// + public class BinaryArtifact + { + /// Index of this entry inside . + public int Index; + + /// Content addressable hash. For ContentFile artifacts, the matching + /// can be found by ContentHash. + public string ContentHash; + + /// One of the strings in . + public string Category; + + /// Size in bytes. + public ulong Size; + + /// Indices into identifying additional artifacts + /// referenced from this one (e.g. mesh, audio). + /// Note: this does not track references to other ContentFiles — those are recorded in + /// . + public int[] ArtifactReferences; + } + + /// + /// In-memory representation of a version 2 ContentLayout.json file (Unity 6.6). + /// + /// The Layout is a companion to the BuildManifest, recording additional details about the build + /// (including source assets and information about which object an ObjectId hash refers to). + /// It is not shipped with the build; it exists for tools and tests that analyze build output. + /// + public class ContentLayout + { + /// The schema version this type represents. + public const int SchemaVersion = 2; + + /// Schema version of the ContentLayout.json file. + public int Version; + + /// Hash of the BuildManifest this layout corresponds to. + public string BuildManifestHash; + + /// The SerializedFiles in the build output. + public SerializedFileLayout[] SerializedFiles; + + /// ObjectIdHash values of the root assets; resolve via . + public string[] RootAssets; + + /// Loadable objects in the build. + public LoadableObjectIdLayout[] LoadableObjectIds; + + /// Loadable scenes in the build. + public LoadableSceneIdLayout[] LoadableSceneIds; + + /// Binary artifacts produced by the build. + public BinaryArtifact[] BinaryArtifacts; + } +} diff --git a/UnityDataTool.Tests/AnalyzeBuildHistoryTests.cs b/UnityDataTool.Tests/AnalyzeBuildHistoryTests.cs index 9ba0137..838b456 100644 --- a/UnityDataTool.Tests/AnalyzeBuildHistoryTests.cs +++ b/UnityDataTool.Tests/AnalyzeBuildHistoryTests.cs @@ -16,7 +16,7 @@ namespace UnityDataTools.UnityDataTool.Tests; // reference build. public class AnalyzeBuildHistoryTests { - private const string BuildHash = "baff06b928d147276f2245dd3b19216a"; + private const string BuildHash = "e320fc78984f8430afa90a591fd02004"; private string m_TestOutputFolder; private string m_ContentDirectory; @@ -68,42 +68,6 @@ private string CreateStaleBuildFolder(string historyRoot, string name) return folder; } - private static async Task<(int exitCode, string stdErr)> RunCapturingStdErr(params string[] args) - { - using var sw = new StringWriter(); - var currentError = Console.Error; - int exitCode; - try - { - Console.SetError(sw); - exitCode = await Program.Main(args); - } - finally - { - Console.SetError(currentError); - } - - return (exitCode, sw.ToString()); - } - - private static async Task<(int exitCode, string stdOut)> RunCapturingStdOut(params string[] args) - { - using var sw = new StringWriter(); - var currentOut = Console.Out; - int exitCode; - try - { - Console.SetOut(sw); - exitCode = await Program.Main(args); - } - finally - { - Console.SetOut(currentOut); - } - - return (exitCode, sw.ToString()); - } - [Test] public async Task Analyze_WithBuildHistory_ImportsLayoutAndBuildReport() { @@ -151,7 +115,7 @@ public async Task Analyze_BuildHistoryWithoutMatch_Fails() CreateStaleBuildFolder(history, "Build-Stale"); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", m_ContentDirectory, "--build-history", history, "-o", databasePath); Assert.AreEqual(1, exitCode, "a build history without the analyzed build must fail"); @@ -170,7 +134,7 @@ public async Task Analyze_BuildHistoryWithoutContentDirectoryInput_Fails() "Data", "LeadingEdgeBuilds", "AssetBundles", "assetbundleroot"); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", bundlePath, "--build-history", history, "-o", databasePath); Assert.AreEqual(1, exitCode); @@ -218,7 +182,7 @@ public async Task Analyze_MultipleMatchingBuilds_UsesMostRecent() File.Delete(Path.Combine(noSummary, "BuildReportSummary.json")); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdOut) = await RunCapturingStdOut( + var (exitCode, stdOut) = await ConsoleTestHelper.RunCapturingStdOut( "analyze", m_ContentDirectory, "--build-history", history, "-o", databasePath); Assert.AreEqual(0, exitCode); @@ -239,7 +203,7 @@ public async Task Analyze_BuildHistoryAndSameLayoutPassedPositionally_UsesOneLay var positionalLayout = Path.Combine(m_FixtureReportFolder, "ContentLayout.json"); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", m_ContentDirectory, positionalLayout, "--build-history", history, "-o", databasePath); Assert.AreEqual(0, exitCode); @@ -262,7 +226,7 @@ public async Task Analyze_ArchiveBuildWithBuildHistory_MatchesThroughArchiveHash "Data", "contentdirectory-zstd"); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", archiveBuild, "--build-history", history, "-o", databasePath); Assert.AreEqual(1, exitCode); diff --git a/UnityDataTool.Tests/AnalyzeContentLayoutTests.cs b/UnityDataTool.Tests/AnalyzeContentLayoutTests.cs index 0ea6b1c..4d82777 100644 --- a/UnityDataTool.Tests/AnalyzeContentLayoutTests.cs +++ b/UnityDataTool.Tests/AnalyzeContentLayoutTests.cs @@ -10,10 +10,11 @@ namespace UnityDataTools.UnityDataTool.Tests; #pragma warning disable NUnit2005, NUnit2006 // Tests the import of ContentLayout.json into the content_layout* tables (issue #99). Runs -// against the ContentLayout.json of the LeadingEdge ContentDirectory reference build, whose -// content is well-known (see UnityProjects/LeadingEdge/Assets/Editor/BuildContentDirectory.cs): +// against the version 3 ContentLayout.json of the LeadingEdge ContentDirectory reference build, +// whose content is well-known (see UnityProjects/LeadingEdge/Assets/Editor/BuildContentDirectory.cs): // 14 serialized files (1 built-in), 3 loadable objects with ContentDirectoryRoot as the single -// root asset, 2 loadable scenes, and 18 binary artifacts. +// root asset, 2 loadable scenes, and 18 binary artifacts. The version 2 import is covered by +// AnalyzeContentLayoutV2Tests. public class AnalyzeContentLayoutTests { private string m_TestOutputFolder; @@ -27,7 +28,7 @@ public void OneTimeSetup() m_ContentLayoutPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "LeadingEdgeBuilds", "BuildReport-ContentDirectory", "ContentLayout.json"); m_BuildReportPath = Path.Combine(TestContext.CurrentContext.TestDirectory, - "Data", "LeadingEdgeBuilds", "BuildReport-ContentDirectory", "f64157fb08bb9f645971d39c1203bd03.buildreport"); + "Data", "LeadingEdgeBuilds", "BuildReport-ContentDirectory", "f096bba0b9ce4c44194edd738d64b9df.buildreport"); Directory.CreateDirectory(m_TestOutputFolder); Directory.SetCurrentDirectory(m_TestOutputFolder); } @@ -52,10 +53,10 @@ public async Task Analyze_ContentLayoutOnly_ImportsLayoutTables() // Header row identifying the imported layout. SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM content_layout", 1, "a single layout should be imported"); - SQLTestHelper.AssertQueryInt(db, "SELECT version FROM content_layout", 2, + SQLTestHelper.AssertQueryInt(db, "SELECT version FROM content_layout", 3, "the layout schema version should be recorded"); SQLTestHelper.AssertQueryString(db, "SELECT build_manifest_hash FROM content_layout", - "baff06b928d147276f2245dd3b19216a", "the BuildManifestHash should be recorded"); + "e320fc78984f8430afa90a591fd02004", "the BuildManifestHash should be recorded"); // Row counts of each table, matching the json content. SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM content_layout_serialized_files", 14, @@ -77,39 +78,58 @@ public async Task Analyze_ContentLayoutOnly_ImportsLayoutTables() SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM content_layout_artifact_references", 17, "one row per artifact reference"); - // The built-in entry keeps its human-readable ID and has no content hash. + // The built-in entry keeps its human-readable path as the stable id and has no artifact. SQLTestHelper.AssertQueryString(db, - "SELECT cfid FROM content_layout_serialized_files WHERE file_index = 0", + "SELECT stable_id FROM content_layout_serialized_files WHERE file_index = 0", "Library/unity default resources", "the built-in entry should be at index 0"); SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM content_layout_serialized_files WHERE is_builtin = 1", 1, "the reference build has a single built-in entry"); SQLTestHelper.AssertQueryInt(db, - "SELECT COUNT(*) FROM content_layout_serialized_files WHERE is_builtin = 1 AND content_hash IS NOT NULL", 0, - "built-in entries have no content hash"); + "SELECT COUNT(*) FROM content_layout_serialized_files WHERE is_builtin = 1 AND artifact_index IS NOT NULL", 0, + "built-in entries have no artifact"); // The ContentDirectoryRoot file and its dependencies, preserving the json array order - // (json SerializedFileDependencies for index 5: [8, 4, 11, 13, 7]). + // (json SerializedFileDependencies for index 10: [4, 5, 2, 9, 3]). SQLTestHelper.AssertQueryString(db, - "SELECT cfid FROM content_layout_serialized_files WHERE file_index = 5", - "52b43dad178849b42ac753005736e7bb.cfid", "cfid of the ContentDirectoryRoot file"); + "SELECT stable_id FROM content_layout_serialized_files WHERE file_index = 10", + "52b43dad178849b42ac753005736e7bb", "stable id of the ContentDirectoryRoot file"); SQLTestHelper.AssertQueryString(db, - "SELECT content_hash FROM content_layout_serialized_files WHERE file_index = 5", - "c0152db4dd710be51b2decb997325f34", "content hash of the ContentDirectoryRoot file"); + @"SELECT ba.content_hash FROM content_layout_serialized_files f + INNER JOIN content_layout_binary_artifacts ba ON ba.artifact_index = f.artifact_index + WHERE f.file_index = 10", + "eb3abd5ab5b0d790980fe9e9df872484", "artifact link of the ContentDirectoryRoot file"); SQLTestHelper.AssertQueryString(db, @"SELECT GROUP_CONCAT(dependency_index) FROM ( SELECT dependency_index FROM content_layout_serialized_file_dependencies - WHERE serialized_file_index = 5 ORDER BY position)", - "8,4,11,13,7", "dependency order must match the json array order"); + WHERE serialized_file_index = 10 ORDER BY position)", + "4,5,2,9,3", "dependency order must match the json array order"); SQLTestHelper.AssertQueryString(db, - "SELECT asset_path FROM content_layout_source_assets WHERE serialized_file_index = 5", + "SELECT asset_path FROM content_layout_source_assets WHERE serialized_file_index = 10", "Assets/ScriptableObjects/ContentDirectoryRoot.asset", "source asset of the root file"); - // RootAssets is folded into the is_root_asset flag. - SQLTestHelper.AssertQueryString(db, - "SELECT asset_path FROM content_layout_loadable_objects WHERE is_root_asset = 1", - "Assets/ScriptableObjects/ContentDirectoryRoot.asset", + // RootAssets is folded into is_root_asset, which holds the 1-based root position + // (json RootAssets: [2]). + SQLTestHelper.AssertQueryInt(db, + "SELECT loadable_index FROM content_layout_loadable_objects WHERE is_root_asset = 1", 2, "ContentDirectoryRoot is the only root asset"); + SQLTestHelper.AssertQueryString(db, + "SELECT guid FROM content_layout_loadable_objects WHERE is_root_asset = 1", + "52b43dad178849b42ac753005736e7bb", "the root loadable records its source asset guid"); + + // The loadable dependencies are indices into the loadables (json file 5: [0, 1]). + SQLTestHelper.AssertQueryString(db, + @"SELECT GROUP_CONCAT(loadable_index) FROM ( + SELECT loadable_index FROM content_layout_loadable_dependencies + WHERE serialized_file_index = 5 ORDER BY loadable_index)", + "0,1", "loadable dependencies reference loadables by index"); + + // v3 layouts do not record the source asset path or source lfid of a loadable, so the + // v2-only columns must not exist in this database. + SQLTestHelper.AssertQueryInt(db, + @"SELECT COUNT(*) FROM pragma_table_info('content_layout_loadable_objects') + WHERE name IN ('asset_path', 'source_lfid')", 0, + "the v2-only columns exist only in databases imported from a v2 layout"); // Even without the build content, every non-built-in entry links to a serialized_files // row: a placeholder holding just the filename (archive NULL, no objects). @@ -117,9 +137,9 @@ SELECT dependency_index FROM content_layout_serialized_file_dependencies "SELECT COUNT(*) FROM content_layout_serialized_files WHERE is_builtin = 0 AND serialized_file IS NULL", 0, "a layout-only analyze links every entry to a placeholder row"); SQLTestHelper.AssertQueryInt(db, - @"SELECT COUNT(*) FROM content_layout_serialized_files f - INNER JOIN serialized_files sf ON sf.id = f.serialized_file - WHERE sf.name != f.content_hash || '.cf' OR sf.archive IS NOT NULL", 0, + @"SELECT COUNT(*) FROM content_layout_serialized_files_view v + INNER JOIN serialized_files sf ON sf.id = v.serialized_file + WHERE sf.name != v.filename OR sf.archive IS NOT NULL", 0, "placeholder rows hold the filename and no archive"); SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM objects", 0, "a layout-only analyze produces no objects"); @@ -131,7 +151,7 @@ SELECT dependency_index FROM content_layout_serialized_file_dependencies 13, "one contentfile artifact per non-built-in serialized file"); SQLTestHelper.AssertQueryString(db, "SELECT filename FROM content_layout_binary_artifacts_view WHERE category = 'manifest'", - "baff06b928d147276f2245dd3b19216a.json", "the artifact filename is derived from the category"); + "e320fc78984f8430afa90a591fd02004.json", "the artifact filename is derived from the category"); // Views are created with the tables and their joins produce the expected rows. SQLTestHelper.AssertViewExists(db, "content_layout_serialized_files_view"); @@ -148,16 +168,15 @@ SELECT dependency_index FROM content_layout_serialized_file_dependencies "Library/unity default resources", "built-in entries show their path as the filename"); SQLTestHelper.AssertQueryString(db, @"SELECT dependency_filename FROM content_layout_serialized_file_dependencies_view - WHERE serialized_file_index = 2 AND position = 1", + WHERE serialized_file_index = 6 AND position = 1", "Library/unity default resources", "dependencies on built-in entries resolve to their path"); SQLTestHelper.AssertQueryString(db, - @"SELECT filename FROM content_layout_loadable_objects_view - WHERE asset_path = 'Assets/ScriptableObjects/ContentDirectoryRoot.asset'", - "c0152db4dd710be51b2decb997325f34.cf", "the loadable view resolves the containing file"); + @"SELECT filename FROM content_layout_loadable_objects_view WHERE loadable_index = 2", + "eb3abd5ab5b0d790980fe9e9df872484.cf", "the loadable view resolves the containing file"); SQLTestHelper.AssertQueryString(db, @"SELECT dependency_filename FROM content_layout_serialized_file_dependencies_view - WHERE serialized_file_index = 5 AND position = 1", - "86d71ff2bb38e064697257d35d6421b8.cf", "the dependencies view resolves target filenames"); + WHERE serialized_file_index = 10 AND position = 1", + "79bdcb3e9bc659d3519594280240cd1b.cf", "the dependencies view resolves target filenames"); } [Test] @@ -188,12 +207,10 @@ AND NOT EXISTS (SELECT 1 FROM objects o WHERE o.serialized_file = f.serialized_f "SELECT COUNT(*) FROM content_layout_loadable_objects_view WHERE object IS NULL", 0, "every loadable should resolve to an analyzed object"); SQLTestHelper.AssertQueryString(db, - @"SELECT name FROM content_layout_loadable_objects_view - WHERE asset_path = 'Assets/ScriptableObjects/ContentDirectoryRoot.asset'", + "SELECT name FROM content_layout_loadable_objects_view WHERE is_root_asset = 1", "ContentDirectoryRoot", "the root loadable resolves to the root ScriptableObject"); SQLTestHelper.AssertQueryString(db, - @"SELECT type FROM content_layout_loadable_objects_view - WHERE asset_path = 'Assets/ScriptableObjects/ContentDirectoryRoot.asset'", + "SELECT type FROM content_layout_loadable_objects_view WHERE is_root_asset = 1", "MonoBehaviour", "ScriptableObjects are serialized as MonoBehaviour"); // The layout resolves the .cfid placeholder references, so the only dangling targets @@ -227,25 +244,6 @@ WHERE EXISTS (SELECT 1 FROM objects o WHERE o.serialized_file = f.serialized_fil "both loadable scenes should link to analyzed content files"); } - // Runs a command capturing stderr, where analyze prints its warnings. - private static async Task<(int exitCode, string stdErr)> RunCapturingStdErr(params string[] args) - { - using var sw = new StringWriter(); - var currentError = Console.Error; - int exitCode; - try - { - Console.SetError(sw); - exitCode = await Program.Main(args); - } - finally - { - Console.SetError(currentError); - } - - return (exitCode, sw.ToString()); - } - [Test] public async Task Analyze_ContentDirectoryWithoutLayout_WarnsAndRecordsDanglingRefs() { @@ -253,7 +251,7 @@ public async Task Analyze_ContentDirectoryWithoutLayout_WarnsAndRecordsDanglingR "Data", "LeadingEdgeBuilds", "ContentDirectory"); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr("analyze", contentDirectory, "-o", databasePath); + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr("analyze", contentDirectory, "-o", databasePath); Assert.AreEqual(0, exitCode, "a ContentDirectory without its layout is still analyzable"); Assert.That(stdErr, Does.Contain("without its ContentLayout.json"), @@ -279,7 +277,7 @@ public async Task Analyze_MultipleContentDirectories_Fails() "Data", "contentdirectory-zstd"); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", contentDirectory, otherBuild, "-o", databasePath); Assert.AreEqual(1, exitCode, "analyzing two different ContentDirectory builds must fail"); @@ -295,7 +293,7 @@ public async Task Analyze_ContentDirectoryWithWrongLayout_Fails() "Data", "contentdirectory-zstd"); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", otherBuild, m_ContentLayoutPath, "-o", databasePath); Assert.AreEqual(1, exitCode, "a layout that does not match the build must not be used"); @@ -310,12 +308,12 @@ public async Task Analyze_MultipleLayoutCandidates_SelectsTheMatchingHash() var staleFolder = Path.Combine(m_TestOutputFolder, "stale_layout"); Directory.CreateDirectory(staleFolder); File.WriteAllText(Path.Combine(staleFolder, "ContentLayout.json"), - "{\"Version\":2,\"BuildManifestHash\":\"deadbeefdeadbeefdeadbeefdeadbeef\"}"); + "{\"Version\":3,\"BuildManifestHash\":\"deadbeefdeadbeefdeadbeefdeadbeef\"}"); var contentDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "LeadingEdgeBuilds", "ContentDirectory"); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", staleFolder, m_ContentLayoutPath, contentDirectory, "-o", databasePath); Assert.AreEqual(0, exitCode); @@ -323,7 +321,7 @@ public async Task Analyze_MultipleLayoutCandidates_SelectsTheMatchingHash() using var db = SQLTestHelper.OpenDatabase(databasePath); SQLTestHelper.AssertQueryString(db, "SELECT build_manifest_hash FROM content_layout", - "baff06b928d147276f2245dd3b19216a", "the matching layout should be the imported one"); + "e320fc78984f8430afa90a591fd02004", "the matching layout should be the imported one"); } [Test] @@ -336,7 +334,7 @@ public async Task Analyze_SubsetOfContentDirectoryWithLayout_ResolvesReferences( "Data", "LeadingEdgeBuilds", "ContentDirectory"); var subsetFolder = Path.Combine(m_TestOutputFolder, "subset"); Directory.CreateDirectory(subsetFolder); - const string rootFile = "c0152db4dd710be51b2decb997325f34.cf"; + const string rootFile = "eb3abd5ab5b0d790980fe9e9df872484.cf"; File.Copy(Path.Combine(contentDirectory, rootFile), Path.Combine(subsetFolder, rootFile)); File.Copy(Path.Combine(contentDirectory, "BuildManifestHash.txt"), Path.Combine(subsetFolder, "BuildManifestHash.txt")); @@ -368,11 +366,11 @@ public async Task Analyze_SubsetWithLayoutButNoHashFile_Fails() "Data", "LeadingEdgeBuilds", "ContentDirectory"); var subsetFolder = Path.Combine(m_TestOutputFolder, "subset_no_hash"); Directory.CreateDirectory(subsetFolder); - const string rootFile = "c0152db4dd710be51b2decb997325f34.cf"; + const string rootFile = "eb3abd5ab5b0d790980fe9e9df872484.cf"; File.Copy(Path.Combine(contentDirectory, rootFile), Path.Combine(subsetFolder, rootFile)); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", subsetFolder, m_ContentLayoutPath, "-o", databasePath); Assert.AreEqual(1, exitCode); @@ -387,22 +385,11 @@ public async Task FindRefs_ContentDirectoryWithLayout_WalksCrossFileChain() var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); Assert.AreEqual(0, await Program.Main(new string[] { "analyze", m_ContentLayoutPath, contentDirectory, "-o", databasePath })); - using var sw = new StringWriter(); - var currentOut = Console.Out; - int exitCode; - try - { - Console.SetOut(sw); - exitCode = await Program.Main(new string[] - { "find-refs", databasePath, "-n", "SerializationDemo", "-t", "MonoBehaviour", "--stdout" }); - } - finally - { - Console.SetOut(currentOut); - } + var (exitCode, stdOut) = await ConsoleTestHelper.RunCapturingStdOut( + "find-refs", databasePath, "-n", "SerializationDemo", "-t", "MonoBehaviour", "--stdout"); Assert.AreEqual(0, exitCode); - Assert.That(sw.ToString(), Does.Contain("ContentDirectoryRoot"), + Assert.That(stdOut, Does.Contain("ContentDirectoryRoot"), "the chain from the root asset should be found across content files"); } @@ -466,7 +453,7 @@ public async Task Analyze_MultipleContentLayoutsWithoutBuild_Fails() File.Copy(m_ContentLayoutPath, Path.Combine(folderB, "ContentLayout.json")); var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); - var (exitCode, stdErr) = await RunCapturingStdErr( + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr( "analyze", folderA, folderB, "-o", databasePath); Assert.AreEqual(1, exitCode, "multiple layouts without build content cannot be disambiguated"); diff --git a/UnityDataTool.Tests/AnalyzeContentLayoutV2Tests.cs b/UnityDataTool.Tests/AnalyzeContentLayoutV2Tests.cs new file mode 100644 index 0000000..2f1ecb9 --- /dev/null +++ b/UnityDataTool.Tests/AnalyzeContentLayoutV2Tests.cs @@ -0,0 +1,151 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Data.Sqlite; +using NUnit.Framework; + +namespace UnityDataTools.UnityDataTool.Tests; + +#pragma warning disable NUnit2005, NUnit2006 + +// Tests the import of a version 2 ContentLayout.json (Unity 6.6) into the content_layout* tables +// (issue #131). v2 files are upgraded to the current model on import: hash-based references become +// indices and the stable ids lose their ".cfid" extension, so both versions produce the same +// schema. The v2-only source data (asset path, source lfid) lands in extra columns that only exist +// in a database imported from a v2 file. Runs against the archived v2 layout of the LeadingEdge +// reference build in TestCommon/Data/ContentLayoutVersions/v2 (its build content is not archived, +// so this is a layout-only analyze). +public class AnalyzeContentLayoutV2Tests +{ + private string m_TestOutputFolder; + private string m_ContentLayoutPath; + + [OneTimeSetUp] + public void OneTimeSetup() + { + m_TestOutputFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "content_layout_v2_test_folder"); + m_ContentLayoutPath = Path.Combine(TestContext.CurrentContext.TestDirectory, + "Data", "ContentLayoutVersions", "v2", "ContentLayout.json"); + Directory.CreateDirectory(m_TestOutputFolder); + Directory.SetCurrentDirectory(m_TestOutputFolder); + } + + [TearDown] + public void Teardown() + { + SqliteConnection.ClearAllPools(); + var outputFolder = new DirectoryInfo(m_TestOutputFolder); + outputFolder.EnumerateFiles().ToList().ForEach(f => f.Delete()); + outputFolder.EnumerateDirectories().ToList().ForEach(d => d.Delete(true)); + } + + [Test] + public async Task Analyze_V2ContentLayout_ImportsUpgradedLayoutTables() + { + var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); + + Assert.AreEqual(0, await Program.Main(new string[] { "analyze", m_ContentLayoutPath, "-o", databasePath })); + using var db = SQLTestHelper.OpenDatabase(databasePath); + + // The original file version is recorded, not the version it was upgraded to. + SQLTestHelper.AssertQueryInt(db, "SELECT version FROM content_layout", 2, + "the original schema version should be recorded"); + SQLTestHelper.AssertQueryString(db, "SELECT build_manifest_hash FROM content_layout", + "baff06b928d147276f2245dd3b19216a", "the BuildManifestHash should be recorded"); + + // Row counts of each table, matching the json content. + SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM content_layout_serialized_files", 14, + "one row per SerializedFiles entry"); + SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM content_layout_source_assets", 16, + "one row per source asset per file"); + SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM content_layout_loadable_objects", 3, + "one row per LoadableObjectIds entry"); + SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM content_layout_binary_artifacts", 18, + "one row per BinaryArtifacts entry"); + + // The v2 ".cfid" extension is stripped so stable_id is uniform across layout versions. + // The built-in entry keeps its human-readable path. + SQLTestHelper.AssertQueryString(db, + "SELECT stable_id FROM content_layout_serialized_files WHERE file_index = 5", + "52b43dad178849b42ac753005736e7bb", "stable_id should not carry the .cfid extension"); + SQLTestHelper.AssertQueryString(db, + "SELECT stable_id FROM content_layout_serialized_files WHERE file_index = 0", + "Library/unity default resources", "the built-in entry should be at index 0"); + + // The v2 ContentHash is resolved to the index of the matching contentfile artifact. + SQLTestHelper.AssertQueryString(db, + @"SELECT ba.content_hash FROM content_layout_serialized_files f + INNER JOIN content_layout_binary_artifacts ba ON ba.artifact_index = f.artifact_index + WHERE f.file_index = 5", + "c0152db4dd710be51b2decb997325f34", "the ContentHash becomes a reference to its artifact"); + SQLTestHelper.AssertQueryInt(db, + "SELECT COUNT(*) FROM content_layout_serialized_files WHERE is_builtin = 1 AND artifact_index IS NOT NULL", 0, + "built-in entries have no artifact"); + + // The v2 ObjectIdHash references are resolved to loadable indices during import + // (json LoadableDependencies for file 4 reference the loadables at indices 0 and 2). + SQLTestHelper.AssertQueryString(db, + @"SELECT GROUP_CONCAT(loadable_index) FROM ( + SELECT loadable_index FROM content_layout_loadable_dependencies + WHERE serialized_file_index = 4 ORDER BY loadable_index)", + "0,2", "hash-based loadable dependencies resolve to loadable indices"); + + // The loadables keep their json array position as the key. lfid is the id of the object + // in its output file (the v2 OutputLFID); the v2 source lfid is preserved separately. + SQLTestHelper.AssertQueryString(db, + "SELECT asset_path FROM content_layout_loadable_objects WHERE loadable_index = 1", + "Assets/ScriptableObjects/ContentDirectoryRoot.asset", "v2 layouts record the loadable's asset path"); + SQLTestHelper.AssertQueryString(db, + "SELECT lfid FROM content_layout_loadable_objects WHERE loadable_index = 1", + "-775554941117088049", "lfid holds the output-file lfid"); + SQLTestHelper.AssertQueryString(db, + "SELECT source_lfid FROM content_layout_loadable_objects WHERE loadable_index = 1", + "11400000", "v2 layouts record the source lfid"); + + // The single RootAssets entry names the ContentDirectoryRoot loadable; its 1-based + // position lands in is_root_asset. + SQLTestHelper.AssertQueryString(db, + "SELECT asset_path FROM content_layout_loadable_objects WHERE is_root_asset = 1", + "Assets/ScriptableObjects/ContentDirectoryRoot.asset", + "ContentDirectoryRoot is the only root asset"); + SQLTestHelper.AssertQueryInt(db, + "SELECT COUNT(*) FROM content_layout_loadable_objects WHERE is_root_asset = 0", 2, + "the other loadables are not roots"); + + // The v2-only columns appear in the loadables view as well. + SQLTestHelper.AssertQueryString(db, + @"SELECT filename FROM content_layout_loadable_objects_view + WHERE asset_path = 'Assets/ScriptableObjects/ContentDirectoryRoot.asset'", + "c0152db4dd710be51b2decb997325f34.cf", "the loadable view resolves the containing file"); + + // Layout-only analyze still links every non-built-in entry to a placeholder row. + SQLTestHelper.AssertQueryInt(db, + "SELECT COUNT(*) FROM content_layout_serialized_files WHERE is_builtin = 0 AND serialized_file IS NULL", 0, + "a layout-only analyze links every entry to a placeholder row"); + + // The views derive filenames through the artifact link. + SQLTestHelper.AssertQueryString(db, + "SELECT filename FROM content_layout_serialized_files_view WHERE file_index = 5", + "c0152db4dd710be51b2decb997325f34.cf", "filenames are derived from the linked artifact"); + SQLTestHelper.AssertQueryString(db, + "SELECT filename FROM content_layout_serialized_files_view WHERE is_builtin = 1", + "Library/unity default resources", "built-in entries show their path as the filename"); + } + + [Test] + public async Task Analyze_V1ContentLayout_ImportsNothing() + { + var layoutFolder = Path.Combine(m_TestOutputFolder, "v1_layout"); + Directory.CreateDirectory(layoutFolder); + File.WriteAllText(Path.Combine(layoutFolder, "ContentLayout.json"), + "{\"Version\":1,\"BuildManifestHash\":\"deadbeefdeadbeefdeadbeefdeadbeef\"}"); + var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); + + var (exitCode, stdErr) = await ConsoleTestHelper.RunCapturingStdErr("analyze", layoutFolder, "-o", databasePath); + + Assert.AreEqual(1, exitCode, "a layout-only analyze of an unsupported version has nothing to import"); + Assert.That(stdErr, Does.Contain("Unsupported ContentLayout.json version 1")); + Assert.That(stdErr, Does.Contain("versions 2 and 3"), "the supported versions should be named"); + } +} diff --git a/UnityDataTool.Tests/BuildReportTests.cs b/UnityDataTool.Tests/BuildReportTests.cs index 396ee0b..4c1e927 100644 --- a/UnityDataTool.Tests/BuildReportTests.cs +++ b/UnityDataTool.Tests/BuildReportTests.cs @@ -24,7 +24,7 @@ public void OneTimeSetup() m_TestOutputFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "test_folder"); m_TestDataFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "BuildReports"); var leadingEdge = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "LeadingEdgeBuilds"); - m_ContentDirectoryReport = Path.Combine(leadingEdge, "BuildReport-ContentDirectory", "f64157fb08bb9f645971d39c1203bd03.buildreport"); + m_ContentDirectoryReport = Path.Combine(leadingEdge, "BuildReport-ContentDirectory", "f096bba0b9ce4c44194edd738d64b9df.buildreport"); m_AssetBundleReport = Path.Combine(leadingEdge, "BuildReport-AssetBundles", "LastBuild.buildreport"); Directory.CreateDirectory(m_TestOutputFolder); Directory.SetCurrentDirectory(m_TestOutputFolder); @@ -622,9 +622,9 @@ public async Task Analyze_BuildReport_ContentDirectory_ContainsNewSummaryColumns "Unexpected build_content_options"); // The session GUID matches the report's GUID-based filename. SQLTestHelper.AssertQueryString(db, "SELECT build_session_guid FROM build_reports", - "f64157fb08bb9f645971d39c1203bd03", "Unexpected build_session_guid"); + "f096bba0b9ce4c44194edd738d64b9df", "Unexpected build_session_guid"); SQLTestHelper.AssertQueryString(db, "SELECT build_manifest_hash FROM build_reports", - "baff06b928d147276f2245dd3b19216a", "Unexpected build_manifest_hash"); + "e320fc78984f8430afa90a591fd02004", "Unexpected build_manifest_hash"); // No build profile was active: the path is present-but-empty, and the all-zero GUID -> NULL. SQLTestHelper.AssertQueryString(db, "SELECT build_profile_path FROM build_reports", "", "Expected empty build_profile_path"); @@ -686,7 +686,7 @@ public async Task Analyze_BuildReport_ContentDirectory_ContainsContentSummary() "Unexpected number of asset stats rows"); // Spot-check the cross-build stats. - SQLTestHelper.AssertQueryInt(db, "SELECT serialized_file_size FROM build_report_content_summary", 158832, + SQLTestHelper.AssertQueryInt(db, "SELECT serialized_file_size FROM build_report_content_summary", 99584, "Unexpected serialized_file_size"); SQLTestHelper.AssertQueryInt(db, "SELECT object_count FROM build_report_content_summary", 28, "Unexpected object_count"); @@ -749,7 +749,7 @@ public async Task Analyze_BuildReport_MultipleReports_ContentSummaryJoinsCorrect // Each report's type stats resolve to a build report in the matching serialized file. SQLTestHelper.AssertQueryInt(db, @"SELECT COUNT(*) FROM build_report_content_type_stats_view - WHERE build_report_filename = 'f64157fb08bb9f645971d39c1203bd03.buildreport' + WHERE build_report_filename = 'f096bba0b9ce4c44194edd738d64b9df.buildreport' AND type_name = 'Cubemap'", 1, "Expected the ContentDirectory report's Cubemap type stat"); diff --git a/UnityDataTool.Tests/ConsoleTestHelper.cs b/UnityDataTool.Tests/ConsoleTestHelper.cs new file mode 100644 index 0000000..f0e13da --- /dev/null +++ b/UnityDataTool.Tests/ConsoleTestHelper.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace UnityDataTools.UnityDataTool.Tests; + +/// +/// Helpers for running UnityDataTool commands while capturing their console output. +/// +public static class ConsoleTestHelper +{ + /// + /// Runs a command capturing stderr, where analyze prints its warnings and errors. + /// + public static async Task<(int exitCode, string stdErr)> RunCapturingStdErr(params string[] args) + { + using var sw = new StringWriter(); + var currentError = Console.Error; + int exitCode; + try + { + Console.SetError(sw); + exitCode = await Program.Main(args); + } + finally + { + Console.SetError(currentError); + } + + return (exitCode, sw.ToString()); + } + + /// + /// Runs a command capturing stdout. + /// + public static async Task<(int exitCode, string stdOut)> RunCapturingStdOut(params string[] args) + { + using var sw = new StringWriter(); + var currentOut = Console.Out; + int exitCode; + try + { + Console.SetOut(sw); + exitCode = await Program.Main(args); + } + finally + { + Console.SetOut(currentOut); + } + + return (exitCode, sw.ToString()); + } +} diff --git a/UnityDataTool.Tests/DumpTests.cs b/UnityDataTool.Tests/DumpTests.cs index d077ab9..4e309d7 100644 --- a/UnityDataTool.Tests/DumpTests.cs +++ b/UnityDataTool.Tests/DumpTests.cs @@ -31,7 +31,7 @@ public void OneTimeSetup() m_NoTypeTreeSerializedFilePath = Path.Combine(m_TestDataFolder, "PlayerNoTypeTree", "level0"); m_NoTypeTreeArchivePath = Path.Combine(m_TestDataFolder, "AssetBundleTypeTreeVariations", "AssetBundle-NoTypeTree", "small.bundle"); m_SerializationDemoBundlePath = Path.Combine(m_TestDataFolder, "LeadingEdgeBuilds", "AssetBundles", "serializationdemo"); - m_ContentDirectoryBuildReportPath = Path.Combine(m_TestDataFolder, "LeadingEdgeBuilds", "BuildReport-ContentDirectory", "f64157fb08bb9f645971d39c1203bd03.buildreport"); + m_ContentDirectoryBuildReportPath = Path.Combine(m_TestDataFolder, "LeadingEdgeBuilds", "BuildReport-ContentDirectory", "f096bba0b9ce4c44194edd738d64b9df.buildreport"); } [Test] @@ -536,9 +536,9 @@ public async Task Dump_Stdout_BuildReport_PrintsGuidAndHash128AsHexStrings() var output = sw.ToString(); - Assert.That(output, Does.Contain("buildSessionGUID (GUID) f64157fb08bb9f645971d39c1203bd03")); + Assert.That(output, Does.Contain("buildSessionGUID (GUID) f096bba0b9ce4c44194edd738d64b9df")); Assert.That(output, Does.Contain("sourceAssetGUID (GUID) 162c015549f8733449ac70ae78ad3aa5")); - Assert.That(output, Does.Contain("buildManifestHash (Hash128) baff06b928d147276f2245dd3b19216a")); + Assert.That(output, Does.Contain("buildManifestHash (Hash128) e320fc78984f8430afa90a591fd02004")); // The individual fields of these compound types are no longer dumped. Assert.That(output, Does.Not.Contain("data[0] (unsigned int)"));