Repository navigation
Description
Why
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.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 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
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. - The symbolic
.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. - The new tables/views must be created only when a
ContentLayout.jsonis actually imported, so users analyzing AssetBundles or Player builds don't see a pile of empty ContentDirectory tables. - The initial version supports importing one
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 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.
.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. - Exact-match guarantee. A
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. - Multiple layouts on input are OK if one matches. If several
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. - 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.jsonpath included. The unresolvable cross-file references land indangling_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.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. - Unsupported schema version is a hard error. If the layout's
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:
| 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 |
| ContentLayout.json only | OK — layout tables only |
| Non-ContentDirectory content + ContentDirectory (+ optional layout) | OK (warning if layout missing) |
ContentLayout.json with unsupported Version |
Error |
Out of scope
- Importing multiple
ContentLayout.jsonfiles into one database (future work, for build comparison). - Backward compatibility with older
ContentLayout.jsonschema versions — the C# model tracks the latest version only (currently v2). - Solving the general multiple-builds-in-one-database problem (issue Provide a clear error message when more than 1 SerializedFile with the same name is analyzed #51).
- Analyzing the raw build manifest (
<hash>.json) directly;ContentLayout.jsonis the supported tooling input, the manifest schema is internal.
Activity
[#99] Address review: builtin filenames in views, contentfile join fi…
[#99] Link layout entries to analyzed serialized files; document layo…
[#99] Restore reference ContentLayout.json to its original build outp…
[#99] Move ContentLayout database schema docs to a dedicated page
[#99] Import ContentLayout.json into the analyze database (#101)
[#99] Address review: handle BuildManifestHash.txt read errors, dispo…
[#99] Resolve ContentDirectory references via ContentLayout.json and …
[#99] Fix grammar in content_layout_resource_files_view doc
[#99] Create build report schema on demand; view rename and Content D…
[#99] Resolve ContentDirectory refs when content files lack the .cf e…
[#99] Resolve ContentDirectory refs when content files lack the .cf e…
[#99] Resolve ContentDirectory refs when content files lack the .cf e…
[#99] Resolve ContentDirectory refs when content files lack the .cf e…
[#99] Normalize BuildManifestHash casing when reading it for matching
[#99] Add --build-history option to analyze (#109)