Repository navigation
Maintenance
Almost every "drag-lint gave me a wrong answer" turns out to be a stale or mis-scoped index. This page is about keeping that from happening, and about recognising it when it does.
One database per project, beside the project file:
<folder containing the .dproj>\_D-RAG\<project file base name>.sqlite
Named after the project file, not the folder -- one folder can host several projects.
One database per platform for the library (RTL, VCL, DevExpress, other third-party). These have no owning project folder, so they live together:
C:\Projects\.drag-lint\library-Win64.sqlite
C:\Projects\.drag-lint\library-Win32.sqlite
drag-lint resolve-dbs --platform win64
drag-lint resolve-dbs --project C:\path\MyApp.dproj
drag-lint resolve-dbs --in C:\path\SomeUnit.pas
drag-lint.json, beside the exe, lists every index as a section and lets
consumers (query, lint-all, lsp, serve, graph) pick the right database
with no --db.
A section's type is declared by its target: an include entry ending in
.dpr/.dproj makes a project section; a folder makes a library or
folder section. The mode is chosen per run: --rebuild (from scratch) or
--recompile (incremental, the default).
A project index is exactly the compile closure -- the .dproj members, the
units they transitively use, each unit's sibling .dfm, {$I} includes, and the
project file. Units on the Delphi Library/Browsing path are excluded (they belong
to the library index), and loose unreferenced files in the project folder are
excluded.
The authoritative set is the platform library index plus the one project index -- and nothing else. Another project's database is noise: a name match there is not a caller here. Passing extra databases is how unrelated units once got written into a project's documentation.
Authority is also per question: the library index is right for "which unit declares X" and wrong for "who calls X".
drag-lint index --all --only <SectionName> # one section, incremental
drag-lint index --all --jobs 0 # everything
drag-lint index --all --dry-run # preview the plan
Reindex a project section by name. Do not point a folder scan at a project
database (index <dir> --db <projectdb>) -- a project index is a compile
closure, and a folder walk changes what the section owns.
Reindex after a build that changed symbols, incrementally. Do not full-rescan the tree after every build.
An index records TWO fingerprints, and they owe different remedies.
drag-lint info --json --db <index> prints both and a verdict (current,
resolve-owed, reparse-owed, index-newer) with the command to run.
index-newer means the index was built or resolved by a NEWER engine than the
one answering: reads work, but index with this engine is refused (a writer
never downgrades an index), so the remedy is a newer engine, not a re-resolve.
-
Indexer fingerprint -- EXTRACTOR version (not the product version),
schema, whether preprocessing was on, and the effective platform, e.g.
v=1.18.0-alpha;schema=23;pp=1;plat=win64. When it changes, every file in scope is re-parsed, because a newer extractor may emit symbols the stored parse missed. This is deliberate: the alternative is silently stale parses. -
Resolver fingerprint -- e.g.
r=1.6.0-alpha;schema=23. When only this moves, no parse is wrong; only DERIVED data is (call edges, ancestry, enum-value bindings, purity verdicts). The remedy isindex --all --resolve-only-- minutes for project sections, about an hour per platform library -- not a re-parse.
Consequence: a release whose extractor version moved re-parses every index once (v1.17.0-alpha did: extractor 1.18.0-alpha, for the platform define groups). A release that moves only the product or resolver version does not. For a large library index a re-parse is a long walk. Two things make it survivable:
- Per-file resume -- an interrupted re-parse continues where it stopped instead of restarting.
- The whole-database announce -- a long call-target resolve now says so, with its reason, before it runs.
Set DRAGLINT_PROFILE=1 for a per-phase breakdown on stderr.
set DRAGLINT_PROFILE=1
cd C:\tools\drag-lint
.\drag-lint.exe lint-all --db <index> --quiet
If you see this, the run is working, not hung:
resolve: calls starting WHOLE-DB pass over all 6993 indexed file(s)
resolve: calls ... whole database because this run rewrote more than one file
in three (236 changed, limit 235)
resolve: calls ... this is the expensive shape (~37 min on a 2 GB index)
-- it is running, not hung
The call-target resolve has two shapes: a scoped pass that re-resolves only what changed, and a whole-database pass. The scoped one is several times faster; the message names the reason the scoped one was declined.
An orphaned drag-lint.exe (or drag_lint_graph.exe) still holds it. Check
first, before diagnosing anything else:
Get-Process drag-lint
Note that piping a run through Select-Object -First N does not kill the
process -- it detaches and keeps indexing, and the lock then looks like a broken
tool. The IDE plugin also holds a handle on the project's database for the whole
session.
The index is stale. Reindex that section and retry rather than concluding the tool cannot find it.
drag-lint index --all --only <Section>
A database several engine versions old will answer from an older parser -- quietly, and with fewer results rather than an error.
That is the shell, not the engine. PowerShell holds a native process's stderr
until the process exits. For long runs redirect through cmd.exe, or use
Start-Process -RedirectStandardError, and poll the file.
If a before/after comparison shows an implausible change, check which exe ran.
With NoDefaultCurrentDirectoryInExePath set, a bare drag-lint ... after a
cd resolves from PATH, not the current folder. Use .\drag-lint.exe. The
quickest tell is the DRAGLINT_PROFILE breakdown format, which changes
whenever the profiler does -- an old-format report means an old binary.
Section builds are independent; a section that fails prints ERROR building section <name> and the sweep continues. Read the log for ERROR, do not trust
the exit code alone. If you hit an intermittent
FOREIGN KEY constraint failed, keep the log and the database rather than
re-running -- a re-run usually succeeds, which is why it stays undiagnosed.
Updating a large third-party suite (DevExpress and similar) changes many library units at once, so the library index goes stale and needs a reindex:
drag-lint index --all --only Library --jobs 0
Expect a whole-database call-target resolve: new units bring new type names, which is exactly the condition that declines the scoped pass. It will announce itself with its reason. Budget accordingly, and let it finish.
drag-lint accepts a few verbs that --help does not list on purpose: they
are self-tests and diagnostics driven by the test battery or by another verb,
not part of the user surface. They are listed here so a name seen in a log or
a script can be looked up. The reasons are the ones the docs-sync guard keeps
in its $UndocumentedOnPurpose list.
| Verb | What it is |
|---|---|
selftest |
Umbrella self-test dispatcher (manifest-merge, glob, closure, dbselect, drift, ...) used by runners under tests\
|
contrast-selftest |
Self-test for the hover contrast computation, driven by a test runner |
doc-facts-selftest |
Self-test for the doc-facts renderer, driven by test runners |
test-store-freshness |
Store-freshness probe used by a test runner; it needs --db and does nothing else |
dump-pp-lex |
Preprocessor lexer dump. The documented preprocessor verbs are preprocess-file and pp-profile
|
dump-pp-eval |
Preprocessor expression-evaluation dump, paired with dump-pp-lex
|
resolve-uses |
The diagnostic behind check-unit --resolve-uses; not a verb of its own |
convert-reemit |
The DFM re-emit stage of the conversion pipeline, driven by convert-apply and by test runners |