mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-03 12:20:27 +00:00
`nim ic` was a command of its own, which made it the C backend only and cut it
off from everything the ordinary compile commands accept. `--ic:on` is a switch
on `nim c` / `nim cpp` / `nim objc` instead, so `-r`, `-d:release`,
`--exceptions:`, and a project-wide opt-in from `nim.cfg` / `config.nims` all
work. `nim ic` still resolves to the same driver; `koch bootic`, `koch ic` and
`testament --ic` now go through the switch.
The switch was already parsed into a `conf.ic` nobody read. It is now read in
`passCmd1` as well, because `nim.nim` has to decide whether this run is an IC
DRIVER before config loading (`ensureIcConfig` produces the precompiled config
the driver itself replays); when the switch comes from a config file instead,
`main.nim` produces it late.
**C++.** `tests/cpp` passes under `--ic:on`, matching its classic result. Four
fixes, three of them the shape of every "C++ needs the whole program" problem the
per-module backend has:
* The driver DECLARES each module's translation unit to nifmake without loading a
module, so it cannot ask `cgen.getCFile` — and it hardcoded `.nim.c`, so the
merge stage went looking for `.c.nif` next to the `.cpp.nif` the children had
written. `options.icCFileExt` mirrors the formula at backend granularity.
* C++ has no designated initializers, so the RTTI record is a bare variable that
`DatInit` fills field by field. A bare `TNimTypeV2 x;` is a tentative
definition — C's linker merges those, C++'s does not — so every TU that
demanded the type defined it ("multiple definition of NTIv2__…"). It now gets
the same extern-declaration + owned-`'d'`-definition split the C flavour has.
* `memberProcsPerType` / `initializersPerType` live only in the sem process, so
the backend emitted a struct WITHOUT its in-class member declarations and the
out-of-class definitions did not match ("no declaration matches
'void Doo::memberProc()'"). They are replayed from a new `(repcppmember …)`
log entry; `replayCppMember` re-derives the type from the routine's signature
exactly as `semCppMember` does, so no type key has to survive the round trip.
* Two follow-ons for members: `loc.snippet` is a CALL PATTERN (`#->salute(@)`),
and only `genMemberProcHeader` derives it — whole-program cgen got it for free
by generating the defining module first, but the per-module backend emits that
body in another process, leaving the caller with the mangled Nim name
(`loo->salute_u0__vireouyks1()`). And that pattern is not a linker name: every
`salute` member in every class mints the same one, so the merge stage handed
them all to one artifact and dropped the rest (undefined vtable at link).
Member definitions are keyed by their NIF name there instead.
`--run` is now dropped when re-invoking for the config artifact: the producer has
no output binary and `nim.nim`'s run step asserted on the empty `outFile`.
testament's `--ic` appends the switch rather than rewriting the compile verb, so
a test that overrides `cmd:` wholesale keeps its verb, and the C++ corpus is
covered too (it never was — the old rewrite only matched `nim c `).
`icFormatVersion` 36 -> 37 for the new log entry. `koch bootic` reaches its
byte-identical fixed point through the new entry point; `koch ic` passes;
`tests/ic`, `tests/destructor` (3 known `--newruntime` failures) and the classic
categories are unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
194 lines
6.9 KiB
Nim
194 lines
6.9 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2015 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
import std/[os, strutils, parseopt]
|
|
|
|
when defined(nimPreviewSlimSystem):
|
|
import std/assertions
|
|
|
|
when defined(windows):
|
|
when defined(gcc):
|
|
when defined(x86):
|
|
{.link: "../icons/nim.res".}
|
|
else:
|
|
{.link: "../icons/nim_icon.o".}
|
|
|
|
when defined(amd64) and defined(vcc):
|
|
{.link: "../icons/nim-amd64-windows-vcc.res".}
|
|
when defined(i386) and defined(vcc):
|
|
{.link: "../icons/nim-i386-windows-vcc.res".}
|
|
|
|
import
|
|
commands, options, msgs, extccomp, main, idents, lineinfos, cmdlinehelper,
|
|
pathutils, modulegraphs
|
|
|
|
from ast2nif import registerNifAstTags
|
|
from icconfig import ensureIcConfig
|
|
|
|
from std/browsers import openDefaultBrowser
|
|
from nodejs import findNodeJs
|
|
|
|
when defined(tinyc): # == hasTinyCBackend; spelled out for the IC dep scanner
|
|
import tccgen
|
|
|
|
when defined(profiler) or defined(memProfiler):
|
|
{.hint: "Profiling support is turned on!".}
|
|
import nimprof
|
|
|
|
proc nimbleLockExists(config: ConfigRef): bool =
|
|
const nimbleLock = "nimble.lock"
|
|
let pd = if not config.projectPath.isEmpty: config.projectPath else: AbsoluteDir(getCurrentDir())
|
|
if optSkipParentConfigFiles notin config.globalOptions:
|
|
for dir in parentDirs(pd.string, fromRoot=true, inclusive=false):
|
|
if fileExists(dir / nimbleLock):
|
|
return true
|
|
return fileExists(pd.string / nimbleLock)
|
|
|
|
proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
|
|
var p = parseopt.initOptParser(cmd)
|
|
var argsCount = 0
|
|
|
|
config.commandLine.setLen 0
|
|
# bugfix: otherwise, config.commandLine ends up duplicated
|
|
|
|
while true:
|
|
parseopt.next(p)
|
|
case p.kind
|
|
of cmdEnd: break
|
|
of cmdLongOption, cmdShortOption:
|
|
config.commandLine.add " "
|
|
config.commandLine.addCmdPrefix p.kind
|
|
config.commandLine.add p.key.quoteShell # quoteShell to be future proof
|
|
if p.val.len > 0:
|
|
config.commandLine.add ':'
|
|
config.commandLine.add p.val.quoteShell
|
|
|
|
if p.key == "": # `-` was passed to indicate main project is stdin
|
|
p.key = "-"
|
|
if processArgument(pass, p, argsCount, config): break
|
|
else:
|
|
processSwitch(pass, p, config)
|
|
of cmdArgument:
|
|
config.commandLine.add " "
|
|
config.commandLine.add p.key.quoteShell
|
|
if processArgument(pass, p, argsCount, config): break
|
|
if pass == passCmd2:
|
|
if {optRun, optWasNimscript} * config.globalOptions == {} and
|
|
config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript, cmdCrun}:
|
|
rawMessage(config, errGenerated, errArgsNeedRunOption)
|
|
|
|
if config.nimbleLockExists:
|
|
# disable nimble path if nimble.lock is present.
|
|
# see https://github.com/nim-lang/nimble/issues/1004
|
|
disableNimblePath(config)
|
|
|
|
proc getNimRunExe(conf: ConfigRef): string =
|
|
# xxx consider defining `conf.getConfigVar("nimrun.exe")` to allow users to
|
|
# customize the binary to run the command with, e.g. for custom `nodejs` or `wine`.
|
|
if conf.isDefined("mingw"):
|
|
if conf.isDefined("i386"): result = "wine"
|
|
elif conf.isDefined("amd64"): result = "wine64"
|
|
else: result = ""
|
|
else:
|
|
result = ""
|
|
|
|
proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
|
# NIF tag registration must not depend on module init order — the IC-built
|
|
# compiler orders module init calls differently and the top-level
|
|
# `registerTag` initializers then ran against a not-yet-initialized pool,
|
|
# corrupting every written NIF (see registerNifAstTags).
|
|
registerNifAstTags()
|
|
let self = NimProg(
|
|
supportsStdinFile: true,
|
|
processCmdLine: processCmdLine
|
|
)
|
|
self.initDefinesProg(conf, "nim_compiler")
|
|
if paramCount() == 0:
|
|
writeCommandLineUsage(conf)
|
|
return
|
|
|
|
self.processCmdLineAndProjectPath(conf)
|
|
|
|
# `nim ic` driver: ensure the precompiled config exists (produced by a separate
|
|
# `nim icconfig` process, skipped when nothing changed) BEFORE config loading,
|
|
# 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 in {cmdIc, cmdTrack} or isIcDriver(conf):
|
|
ensureIcConfig(conf)
|
|
|
|
var graph = newModuleGraph(cache, conf)
|
|
if not self.loadConfigsAndProcessCmdLine(cache, conf, graph):
|
|
return
|
|
|
|
if conf.cmd == cmdCheck and optWasNimscript notin conf.globalOptions and
|
|
conf.backend == backendInvalid:
|
|
conf.backend = backendC
|
|
|
|
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, cmdTrack}:
|
|
initOrcDefines(conf)
|
|
|
|
if conf.selectedStrings == stringSso and
|
|
conf.selectedGC notin {gcArc, gcOrc, gcYrc, gcAtomicArc}:
|
|
rawMessage(conf, errGenerated,
|
|
"--strings:sso requires --mm:arc, --mm:orc, --mm:yrc, or --mm:atomicArc")
|
|
|
|
mainCommand(graph)
|
|
if conf.hasHint(hintGCStats): echo(GC_getStatistics())
|
|
#echo(GC_getStatistics())
|
|
if conf.errorCounter != 0: return
|
|
when hasTinyCBackend:
|
|
if conf.cmd == cmdTcc:
|
|
tccgen.run(conf, conf.arguments)
|
|
if optRun in conf.globalOptions:
|
|
let output = conf.absOutFile
|
|
case conf.cmd
|
|
of cmdBackends, cmdTcc:
|
|
let nimRunExe = getNimRunExe(conf)
|
|
var cmdPrefix = ""
|
|
if nimRunExe.len > 0: cmdPrefix.add nimRunExe.quoteShell
|
|
case conf.backend
|
|
of backendC, backendCpp, backendObjc: discard
|
|
of backendJs:
|
|
# D20210217T215950:here this flag is needed for node < v15.0.0, otherwise
|
|
# tasyncjs_fail` would fail, refs https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode
|
|
if cmdPrefix.len == 0: cmdPrefix = findNodeJs().quoteShell
|
|
cmdPrefix.add " --unhandled-rejections=strict"
|
|
else: raiseAssert $conf.backend
|
|
if cmdPrefix.len > 0: cmdPrefix.add " "
|
|
# without the `cmdPrefix.len > 0` check, on windows you'd get a cryptic:
|
|
# `The parameter is incorrect`
|
|
let cmd = cmdPrefix & output.quoteShell & ' ' & conf.arguments
|
|
execExternalProgram(conf, cmd.strip(leading=false,trailing=true))
|
|
of cmdDocLike, cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex: # bugfix(cmdRst2tex was missing)
|
|
if conf.arguments.len > 0:
|
|
# reserved for future use
|
|
rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd])
|
|
openDefaultBrowser($output)
|
|
else:
|
|
# support as needed
|
|
rawMessage(conf, errGenerated, "'$1 cannot handle --run" % [$conf.cmd])
|
|
|
|
when declared(GC_setMaxPause):
|
|
GC_setMaxPause 2_000
|
|
|
|
when compileOption("gc", "refc"):
|
|
# the new correct mark&sweet collector is too slow :-/
|
|
GC_disableMarkAndSweep()
|
|
|
|
when not defined(selftest):
|
|
let conf = newConfigRef()
|
|
handleCmdLine(newIdentCache(), conf)
|
|
when declared(GC_setMaxPause):
|
|
echo GC_getStatistics()
|
|
msgQuit(int8(conf.errorCounter > 0))
|