You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
UnityDataTool analyze can already process a ContentDirectory build (Unity 6.6+) into a SQLite database, but the result is incomplete in two important ways, and the build report directory contains a file — ContentLayout.json — that fills exactly those gaps.
The analyzed data can't be traced back to source assets. ContentDirectory output contains no source information: every file is named by a content hash (e.g. c0152db4….cf), so the database records objects and files that a user cannot connect to anything in their project. ContentLayout.json carries the mapping from each built serialized file back to the source assets it came from, which is what makes the database actually meaningful for build analysis.
References between files resolve incorrectly. In ContentDirectory builds the external-references table inside each SerializedFile holds symbolic .cfid placeholders rather than real filenames; the true file-to-file wiring lives in the build manifest (and its tool-friendly superset, ContentLayout.json). See Documentation/contentdirectory-format.md. Without that information, analyze records every cross-file reference as dangling, and features built on references — find-refs, the script_object_view — are broken for ContentDirectory builds.
Large layouts deserve SQL access. For big builds ContentLayout.json gets large, and querying big JSON directly from scripts is inefficient. Its schema is intentionally SQL-friendly (arrays of homogeneous objects that map cleanly to relational tables), so importing it into the analyze database gives efficient, ad-hoc access from scripts or the command line — consistent with how the tool already treats Addressables build layout files. This is valuable even without the build content: analyzing just a ContentLayout.json on its own is a supported way to query a large layout (e.g. computing recursive dependencies from a source asset path).
What
When the input paths given to analyze include a ContentLayout.json (the C# model already exists in UnityDataModels/ContentLayout.cs), the tool should use it in two ways:
1. Import its content into the database
The information in ContentLayout.json becomes queryable tables/views in the analyze database, connected to the existing core data (serialized files, objects, references). The import is full-fidelity: every section of the file is represented — the per-serialized-file information (source assets, content hash, dependencies on other serialized files), the loadables information (loadable objects, loadable scenes, root assets), and the BinaryArtifacts graph.
Notes on scope of the import:
The default is to mirror the file's own structure, in particular keeping BinaryArtifacts in its generic form rather than post-processing it into a .resS/.resource-specific shape. The generic table is the standard place to record artifact sizes (including serialized-file sizes, which the core tables don't carry) and stays valid if future builds reference new kinds of data files. The schema design may still deviate if the generic shape turns out to make typical queries awkward — the built-in convenience views and documented example queries are the test for that.
The symbolic .cfid ID of each serialized file is preserved in the imported data, so the placeholder strings seen inside the SerializedFiles remain traceable after references are resolved.
The new tables/views must be created only when a ContentLayout.json is actually imported, so users analyzing AssetBundles or Player builds don't see a pile of empty ContentDirectory tables.
The initial version supports importing oneContentLayout.json per database. Multi-layout import (e.g. for comparing builds, like the existing multi-BuildReport support) is a possible future extension and is explicitly out of scope.
Alongside the schema, a new documentation page (e.g. Documentation/analyze-examples-contentlayout.md, linked from Documentation/analyze-examples.md) should give typical SQL queries for ContentDirectory builds: some that need only the layout tables (e.g. recursive dependencies from a source asset path), and some that combine layout and content tables (e.g. list all serialized files and objects that are the build output of a specific source asset such as an FBX, non-recursive).
2. Use it to resolve references
While analyzing the build's serialized files, the layout's dependency information is used to resolve each external reference to the actual .cf file it points at, instead of the .cfid placeholder recorded in the file itself. Files without layout coverage keep the current behavior (use the external-table path as-is).
The observable result: analyzing a ContentDirectory build together with its ContentLayout.json produces correct cross-file references — an empty dangling_refs table (aside from references to built-in files like unity default resources), and working find-refs chains such as the root ScriptableObject → dependent ScriptableObjects scenario in the LeadingEdge reference build.
3. Input validation and matching
analyze already accepts multiple file/directory paths, so passing the build output plus the ContentLayout.json (or the build report directory containing it) requires no new command-line surface. The rules for combining inputs:
Recognizing ContentDirectory content..cf files (or a content<N>.archive) in the input identify ContentDirectory content. ContentDirectory output is flat, so no parent-directory searching is needed: if BuildManifestHash.txt is not already in the input, the tool automatically picks it up from the directory containing the ContentDirectory content (works for both loose builds and .archive files), if it exists there.
Exact-match guarantee. A ContentLayout.json is only used if its top-level BuildManifestHash matches the hash in the build output's BuildManifestHash.txt (a file guaranteed to exist in every ContentDirectory output folder). This prevents silently producing misleading results from a stale or unrelated layout. If a layout is on the input alongside ContentDirectory content but no BuildManifestHash.txt can be found, the analyze fails — an unverifiable layout is not used.
Multiple layouts on input are OK if one matches. If several ContentLayout.json files are found (e.g. the user pointed at their whole Library/BuildHistory folder — an intentionally supported convenience), pick the one whose hash matches and ignore the rest. If at least one layout is found but none match the build, fail the analyze with a clear "build does not match layout" error.
A layout alone is valid input. Analyzing only a ContentLayout.json (no build content) imports the layout tables and nothing else — the efficient-queries use case from the "Why" section.
ContentDirectory without a layout is allowed, with a warning. Partial analysis is still useful (e.g. inspecting shaders), so it proceeds, but prints a warning that the analysis is incomplete — specifically that reference information is broken and source-asset mapping is absent — and encourages rerunning with the ContentLayout.json path included. The unresolvable cross-file references land in dangling_refs, which exists precisely to catch such references without treating them as errors.
Only one ContentDirectory at a time. Analyzing two or more distinct ContentDirectory builds in one run is an error (detected via multiple BuildManifestHash.txt files). This avoids the same-filename collision problems of issue Provide a clear error message when more than 1 SerializedFile with the same name is analyzed #51 without trying to solve them. Mixing one ContentDirectory with non-ContentDirectory content (a Player build, AssetBundles) remains allowed, e.g. for duplicate-content detection.
Unsupported schema version is a hard error. If the layout's Version field is not the supported one, fail with a clear error naming the version. (v1 was not widely used, so no backward-compatible parsing is written until a real need appears.)
Scenario summary:
Input
Result
ContentDirectory only
OK + warning: missing ContentLayout.json
Subset of files from a ContentDirectory
OK + same warning (recognized via the .cf extension)
Multiple ContentDirectories
Error
ContentDirectory + matching ContentLayout.json
OK
Subset of ContentDirectory files + matching ContentLayout.json
OK if a BuildManifestHash.txt can be found for successful hash match; error if the layout cannot be validated
ContentDirectory + multiple ContentLayout.json
OK if at least one matches the manifest hash; error if none match
Why
UnityDataTool analyzecan already process a ContentDirectory build (Unity 6.6+) into a SQLite database, but the result is incomplete in two important ways, and the build report directory contains a file —ContentLayout.json— that fills exactly those gaps.The analyzed data can't be traced back to source assets. ContentDirectory output contains no source information: every file is named by a content hash (e.g.
c0152db4….cf), so the database records objects and files that a user cannot connect to anything in their project.ContentLayout.jsoncarries the mapping from each built serialized file back to the source assets it came from, which is what makes the database actually meaningful for build analysis.References between files resolve incorrectly. In ContentDirectory builds the external-references table inside each SerializedFile holds symbolic
.cfidplaceholders rather than real filenames; the true file-to-file wiring lives in the build manifest (and its tool-friendly superset,ContentLayout.json). See Documentation/contentdirectory-format.md. Without that information,analyzerecords every cross-file reference as dangling, and features built on references —find-refs, the script_object_view — are broken for ContentDirectory builds.Large layouts deserve SQL access. For big builds
ContentLayout.jsongets large, and querying big JSON directly from scripts is inefficient. Its schema is intentionally SQL-friendly (arrays of homogeneous objects that map cleanly to relational tables), so importing it into the analyze database gives efficient, ad-hoc access from scripts or the command line — consistent with how the tool already treats Addressables build layout files. This is valuable even without the build content: analyzing just aContentLayout.jsonon its own is a supported way to query a large layout (e.g. computing recursive dependencies from a source asset path).What
When the input paths given to
analyzeinclude aContentLayout.json(the C# model already exists inUnityDataModels/ContentLayout.cs), the tool should use it in two ways:1. Import its content into the database
The information in
ContentLayout.jsonbecomes queryable tables/views in the analyze database, connected to the existing core data (serialized files, objects, references). The import is full-fidelity: every section of the file is represented — the per-serialized-file information (source assets, content hash, dependencies on other serialized files), the loadables information (loadable objects, loadable scenes, root assets), and theBinaryArtifactsgraph.Notes on scope of the import:
BinaryArtifactsin its generic form rather than post-processing it into a.resS/.resource-specific shape. The generic table is the standard place to record artifact sizes (including serialized-file sizes, which the core tables don't carry) and stays valid if future builds reference new kinds of data files. The schema design may still deviate if the generic shape turns out to make typical queries awkward — the built-in convenience views and documented example queries are the test for that..cfidID of each serialized file is preserved in the imported data, so the placeholder strings seen inside the SerializedFiles remain traceable after references are resolved.ContentLayout.jsonis actually imported, so users analyzing AssetBundles or Player builds don't see a pile of empty ContentDirectory tables.ContentLayout.jsonper database. Multi-layout import (e.g. for comparing builds, like the existing multi-BuildReport support) is a possible future extension and is explicitly out of scope.Alongside the schema, a new documentation page (e.g.
Documentation/analyze-examples-contentlayout.md, linked fromDocumentation/analyze-examples.md) should give typical SQL queries for ContentDirectory builds: some that need only the layout tables (e.g. recursive dependencies from a source asset path), and some that combine layout and content tables (e.g. list all serialized files and objects that are the build output of a specific source asset such as an FBX, non-recursive).2. Use it to resolve references
While analyzing the build's serialized files, the layout's dependency information is used to resolve each external reference to the actual
.cffile it points at, instead of the.cfidplaceholder recorded in the file itself. Files without layout coverage keep the current behavior (use the external-table path as-is).The observable result: analyzing a ContentDirectory build together with its
ContentLayout.jsonproduces correct cross-file references — an emptydangling_refstable (aside from references to built-in files likeunity default resources), and workingfind-refschains such as the root ScriptableObject → dependent ScriptableObjects scenario in the LeadingEdge reference build.3. Input validation and matching
analyzealready accepts multiple file/directory paths, so passing the build output plus theContentLayout.json(or the build report directory containing it) requires no new command-line surface. The rules for combining inputs:.cffiles (or acontent<N>.archive) in the input identify ContentDirectory content. ContentDirectory output is flat, so no parent-directory searching is needed: ifBuildManifestHash.txtis not already in the input, the tool automatically picks it up from the directory containing the ContentDirectory content (works for both loose builds and.archivefiles), if it exists there.ContentLayout.jsonis only used if its top-levelBuildManifestHashmatches the hash in the build output'sBuildManifestHash.txt(a file guaranteed to exist in every ContentDirectory output folder). This prevents silently producing misleading results from a stale or unrelated layout. If a layout is on the input alongside ContentDirectory content but noBuildManifestHash.txtcan be found, the analyze fails — an unverifiable layout is not used.ContentLayout.jsonfiles are found (e.g. the user pointed at their wholeLibrary/BuildHistoryfolder — an intentionally supported convenience), pick the one whose hash matches and ignore the rest. If at least one layout is found but none match the build, fail the analyze with a clear "build does not match layout" error.ContentLayout.json(no build content) imports the layout tables and nothing else — the efficient-queries use case from the "Why" section.ContentLayout.jsonpath included. The unresolvable cross-file references land indangling_refs, which exists precisely to catch such references without treating them as errors.BuildManifestHash.txtfiles). This avoids the same-filename collision problems of issue Provide a clear error message when more than 1 SerializedFile with the same name is analyzed #51 without trying to solve them. Mixing one ContentDirectory with non-ContentDirectory content (a Player build, AssetBundles) remains allowed, e.g. for duplicate-content detection.Versionfield is not the supported one, fail with a clear error naming the version. (v1 was not widely used, so no backward-compatible parsing is written until a real need appears.)Scenario summary:
.cfextension)BuildManifestHash.txtcan be found for successful hash match; error if the layout cannot be validatedVersionOut of scope
ContentLayout.jsonfiles into one database (future work, for build comparison).ContentLayout.jsonschema versions — the C# model tracks the latest version only (currently v2).<hash>.json) directly;ContentLayout.jsonis the supported tooling input, the manifest schema is internal.