mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-03 22:18:40 +00:00
much better implementation behind 'nim track' now
This commit is contained in:
@@ -509,6 +509,7 @@ proc parseCommand*(command: string): Command =
|
||||
of "nifc": cmdNifC # generate C from NIF files
|
||||
of "ic": cmdIc # generate .build.nif for nifmake
|
||||
of "icconfig": cmdIcConfig # produce the precompiled config artifact
|
||||
of "track": cmdTrack # IDE goto-def / find-usages over `nim ic`'s NIF output
|
||||
else: cmdUnknown
|
||||
|
||||
proc setCmd*(conf: ConfigRef, cmd: Command) =
|
||||
|
||||
@@ -1134,8 +1134,12 @@ proc generateBackendBuildFile(c: DepContext; forwardedArgs: seq[string]): string
|
||||
|
||||
b.endTree() # stmts
|
||||
|
||||
proc commandIc*(conf: ConfigRef) =
|
||||
## Main entry point for `nim ic`
|
||||
proc commandIc*(conf: ConfigRef; frontendOnly = false) =
|
||||
## Main entry point for `nim ic`. With `frontendOnly` (used by `nim track` for
|
||||
## IDE queries) it runs only Phase 1 — the incremental nifler + `nim m`
|
||||
## frontend that writes every module's `.s.bif` — and skips the whole-program
|
||||
## backend (`nim nifc` -> C -> link), which a goto-def / find-usages scan does
|
||||
## not need.
|
||||
when not defined(nimKochBootstrap):
|
||||
let nifler = findNifler()
|
||||
if nifler.len == 0:
|
||||
@@ -1275,10 +1279,12 @@ proc commandIc*(conf: ConfigRef) =
|
||||
if nifmake.len == 0:
|
||||
rawMessage(conf, hintSuccess, "run:" & " nifmake run" & parallel & " " & buildFile)
|
||||
# without nifmake we can only print the manual commands; emit the
|
||||
# backend's too (best effort — discovery cannot run) and stop.
|
||||
let backendFile = generateBackendBuildFile(c, forwardedArgs)
|
||||
rawMessage(conf, hintSuccess, "generated: " & backendFile)
|
||||
rawMessage(conf, hintSuccess, "run:" & " nifmake run" & parallel & " " & backendFile)
|
||||
# backend's too (best effort — discovery cannot run) and stop. An IDE
|
||||
# query (`frontendOnly`) needs no backend, so skip it there.
|
||||
if not frontendOnly:
|
||||
let backendFile = generateBackendBuildFile(c, forwardedArgs)
|
||||
rawMessage(conf, hintSuccess, "generated: " & backendFile)
|
||||
rawMessage(conf, hintSuccess, "run:" & " nifmake run" & parallel & " " & backendFile)
|
||||
return
|
||||
let cmd = quoteShell(nifmake) & " run" & parallel & " " & quoteShell(buildFile)
|
||||
rawMessage(conf, hintExecuting, cmd)
|
||||
@@ -1322,7 +1328,9 @@ proc commandIc*(conf: ConfigRef) =
|
||||
# Phase 2 — backend (whole-program `nim nifc`), run once over the now-final
|
||||
# graph. Kept a separate nifmake run so backend rebuilds are decided purely
|
||||
# by nifmake's input mtimes, independent of frontend discovery.
|
||||
if frontendOk:
|
||||
# An IDE query (`frontendOnly`) stops after Phase 1: the `.s.bif` it scans
|
||||
# are all produced by the frontend; codegen + link would be wasted work.
|
||||
if frontendOk and not frontendOnly:
|
||||
let backendFile = generateBackendBuildFile(c, forwardedArgs)
|
||||
rawMessage(conf, hintSuccess, "generated: " & backendFile)
|
||||
let cmd = quoteShell(nifmake) & " run" & parallel & " " & quoteShell(backendFile)
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## NIF-based goto-definition / find-all-usages for `nim check`.
|
||||
## NIF-based goto-definition / find-all-usages for `nim track`.
|
||||
##
|
||||
## This is the mainline-Nim port of nimony's `idetools.nim`. It answers a
|
||||
## `--def:FILE,LINE,COL` / `--usages:FILE,LINE,COL` query by *scanning the
|
||||
## `.s.bif` files* (binary NIF, see `dist/nimony/src/lib/bif.nim`) that the
|
||||
## preceding `nim check` compile emitted into the nimcache directory — NOT by
|
||||
## re-running sem. NIF distinguishes a definition (`SymbolDef` token) from a use
|
||||
## preceding `nim ic` frontend (`nim track`) emitted into the nimcache directory
|
||||
## — NOT by re-running sem. NIF distinguishes a definition (`SymbolDef` token) from a use
|
||||
## (`Symbol` token) syntactically, so goto-def / find-uses become plain token
|
||||
## scans over type-checked NIF, which is more reliable than the classic PSym
|
||||
## engine because generics and macros are type-checked in the NIF too.
|
||||
|
||||
@@ -416,26 +416,21 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
|
||||
for it in conf.searchPaths: msgWriteln(conf, it.string)
|
||||
of cmdCheck:
|
||||
commandCheck(graph)
|
||||
of cmdTrack:
|
||||
# `nim track --def:/--usages:/--track:` — IDE goto-definition / find-usages.
|
||||
# Runs `nim ic`'s incremental frontend (nifler + per-module `nim m`, so only
|
||||
# changed modules recompile and each writes a faithful, VM-executed `.s.bif`
|
||||
# — covering stdlib too), then scans those NIF files (idetools.runIdeQuery).
|
||||
# Shares the `nim ic` nimcache dir, so a prior `nim ic` build is reused.
|
||||
setUseIc(true)
|
||||
wantMainModule(conf)
|
||||
setOutFile(conf)
|
||||
when not defined(nimKochBootstrap):
|
||||
if conf.ideCmd in {ideDef, ideUse}:
|
||||
# `nim check --def:`/`--usages:`: run a whole-project check that also
|
||||
# emits each cleanly-compiled module's `.s.bif` (see
|
||||
# pipelines.shouldWriteNif), then scan those NIF files to answer the
|
||||
# goto-definition / find-usages query. The NIF writer needs the IC setup
|
||||
# `cmdM` uses (disabled rod files + `useIc`) to serialize full module
|
||||
# content; we keep `cmd == cmdCheck` for its error-tolerant sem and the
|
||||
# VM guards that run compile-time code faithfully for this mode (see
|
||||
# vm.nim/vmgen.nim), so the emitted NIF reflects macro/gorge symbols.
|
||||
graph.config.symbolFiles = disabledSf
|
||||
setUseIc(true)
|
||||
excl conf.features, Feature.vtables
|
||||
createDir(getNimcacheDir(conf).string)
|
||||
commandCheck(graph)
|
||||
runIdeQuery(conf)
|
||||
else:
|
||||
commandCheck(graph)
|
||||
commandIc(conf, frontendOnly = true)
|
||||
runIdeQuery(conf)
|
||||
else:
|
||||
commandCheck(graph)
|
||||
rawMessage(conf, errGenerated, "nim track not available in bootstrap build")
|
||||
of cmdM:
|
||||
# cmdM uses NIF files, not ROD files
|
||||
graph.config.symbolFiles = disabledSf
|
||||
|
||||
@@ -120,7 +120,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
||||
# so `loadConfigs` replays it instead of re-parsing the `nim.cfg` chain — the
|
||||
# driver runs on the exact same config its children will. See icconfig.nim.
|
||||
when not defined(nimKochBootstrap):
|
||||
if conf.cmd == cmdIc:
|
||||
if conf.cmd in {cmdIc, cmdTrack}:
|
||||
ensureIcConfig(conf)
|
||||
|
||||
var graph = newModuleGraph(cache, conf)
|
||||
@@ -134,7 +134,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
||||
if conf.selectedGC == gcUnselected:
|
||||
if conf.backend in {backendC, backendCpp, backendObjc} or
|
||||
(conf.cmd in cmdDocLike and conf.backend != backendJs) or
|
||||
conf.cmd in {cmdGendepend, cmdNifC, cmdIc, cmdM}:
|
||||
conf.cmd in {cmdGendepend, cmdNifC, cmdIc, cmdM, cmdTrack}:
|
||||
initOrcDefines(conf)
|
||||
|
||||
if conf.selectedStrings == stringSso and
|
||||
|
||||
@@ -205,6 +205,7 @@ type
|
||||
cmdNifC # generate C code from NIF files
|
||||
cmdIc # generate .build.nif for nifmake
|
||||
cmdIcConfig # `nim ic`'s precompiled-config producer (writes ic_config.cfg.nif)
|
||||
cmdTrack # `nim track --def/--usages`: IC frontend build + NIF scan for IDE queries
|
||||
|
||||
const
|
||||
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
|
||||
|
||||
@@ -40,10 +40,9 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
|
||||
of GenDependPass:
|
||||
result = addDotDependency(bModule, semNode)
|
||||
of SemPass:
|
||||
# Return the semantic node for cmdM (NIF generation needs it), and likewise
|
||||
# for a `--def`/`--usages` query under cmdCheck which emits `.s.bif` too.
|
||||
# For regular check, we don't need the result.
|
||||
if graph.config.cmd == cmdM or graph.config.ideCmd in {ideDef, ideUse}:
|
||||
# Return the semantic node for cmdM (NIF generation needs it)
|
||||
# For regular check, we don't need the result
|
||||
if graph.config.cmd == cmdM:
|
||||
result = semNode
|
||||
else:
|
||||
result = graph.emptyNode
|
||||
@@ -168,10 +167,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
s = stream
|
||||
graph.interactive = stream.kind == llsStdIn
|
||||
var topLevelStmts =
|
||||
if optCompress in graph.config.globalOptions or graph.config.cmd == cmdM or
|
||||
graph.config.ideCmd in {ideDef, ideUse}:
|
||||
# A `--def`/`--usages` query emits every module's `.s.bif` under cmdCheck
|
||||
# (see shouldWriteNif below), which needs the collected top-level stmts.
|
||||
if optCompress in graph.config.globalOptions or graph.config.cmd == cmdM:
|
||||
newNodeI(nkStmtList, module.info)
|
||||
else:
|
||||
nil
|
||||
@@ -251,18 +247,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
# current strongly-connected import group (`--icGroup`) are the exception:
|
||||
# they are compiled from source here, so each must write its own NIF.
|
||||
let shouldWriteNif =
|
||||
if graph.config.ideCmd in {ideDef, ideUse}:
|
||||
# `nim check --def:`/`--usages:` query: this whole-project check runs
|
||||
# under cmdCheck and emits each cleanly-compiled, non-dirty PROJECT
|
||||
# module's `.s.bif` so `idetools.runIdeQuery` can scan them afterwards.
|
||||
# Stdlib modules are skipped: they serialize robustly only via `nim ic`'s
|
||||
# per-module `cmdM` build, and the common query targets a project symbol
|
||||
# (whose def + uses all live in project modules). Skip the edited buffer
|
||||
# and any module reached after an error (incomplete NIF).
|
||||
graph.config.errorCounter == 0 and
|
||||
not belongsToStdlib(graph, module) and
|
||||
graph.config.m.fileInfos[module.position].dirtyFile.isEmpty
|
||||
elif graph.config.ideActive:
|
||||
if graph.config.ideActive:
|
||||
# nimsuggest (cmdM): persist NIF for cleanly-compiled, SAVED modules so
|
||||
# later queries load them instead of recompiling. Never persist the
|
||||
# actively edited buffer (it may hold unsaved/incomplete code) nor a
|
||||
|
||||
@@ -926,7 +926,7 @@ proc semWithPContext*(c: PContext, n: PNode): PNode =
|
||||
#if c.config.ideActive: findSuggest(c, n)
|
||||
|
||||
proc reportUnusedModules(c: PContext) =
|
||||
if c.config.cmd == cmdM or c.config.ideCmd in {ideDef, ideUse}: return
|
||||
if c.config.cmd == cmdM: return
|
||||
for (s, info) in c.unusedImports:
|
||||
if sfUsed notin s.flags:
|
||||
message(c.config, info, warnUnusedImportX, s.name.s)
|
||||
|
||||
@@ -2892,10 +2892,9 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult, resolvedIncStmt
|
||||
proc evalInclude(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
var resolvedIncStmt: PNode = nil
|
||||
if optCompress in c.config.globalOptions or c.config.cmd == cmdM or
|
||||
c.config.ideCmd in {ideDef, ideUse}:
|
||||
if optCompress in c.config.globalOptions or c.config.cmd == cmdM:
|
||||
# New resolve the include filenames to string literals that contain absolute paths,
|
||||
# nicer for IC (also for a `--def`/`--usages` query, which emits `.s.bif`):
|
||||
# nicer for IC:
|
||||
resolvedIncStmt = newNodeI(nkIncludeStmt, n.info)
|
||||
result.add resolvedIncStmt
|
||||
else:
|
||||
|
||||
@@ -2033,11 +2033,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
inc pc
|
||||
let rd = c.code[pc].regA
|
||||
createStr regs[ra]
|
||||
if defined(nimsuggest) or (c.config.cmd == cmdCheck and c.config.ideCmd == ideNone):
|
||||
# Don't run staticExec for plain `nim check` / `nim suggest`. A
|
||||
# `--def`/`--usages` query (ideCmd != ideNone) is the exception: it wants
|
||||
# faithful compile-time execution so the emitted `.bif` — which the query
|
||||
# then scans — reflects macro/gorge-produced symbols. See idetools.nim.
|
||||
if defined(nimsuggest) or c.config.cmd == cmdCheck:
|
||||
discard "don't run staticExec for 'nim suggest'"
|
||||
regs[ra].node.strVal = ""
|
||||
else:
|
||||
|
||||
@@ -359,9 +359,7 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
||||
#if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}:
|
||||
if i != dest:
|
||||
when not defined(release):
|
||||
# A `--def`/`--usages` query runs the VM faithfully (ideCmd != ideNone),
|
||||
# so re-arm the leaking-temporary assert for it as in a real compile.
|
||||
if c.config.cmd != cmdCheck or c.config.ideCmd != ideNone:
|
||||
if c.config.cmd != cmdCheck:
|
||||
if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
|
||||
slotTempInt,
|
||||
slotTempFloat,
|
||||
@@ -1600,11 +1598,9 @@ proc setSlot(c: PCtx; v: PSym) =
|
||||
v.positionImpl = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
|
||||
|
||||
template cannotEval(c: PCtx; n: PNode) =
|
||||
if c.config.cmd == cmdCheck and c.config.ideCmd == ideNone and c.config.m.errorOutputs != {}:
|
||||
if c.config.cmd == cmdCheck and c.config.m.errorOutputs != {}:
|
||||
# nim check command with no error outputs doesn't need to cascade here,
|
||||
# includes `tryConstExpr` case which should not continue generating code.
|
||||
# A `--def`/`--usages` query (ideCmd != ideNone) falls through to the hard
|
||||
# `globalError` below so its compile-time evaluation is faithful.
|
||||
# includes `tryConstExpr` case which should not continue generating code
|
||||
localError(c.config, n.info, "cannot evaluate at compile time: " & n.renderTree)
|
||||
c.cannotEval = true
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user