From 7084ad69a4e53d3ad1b0617373c03bae665dfcbb Mon Sep 17 00:00:00 2001 From: haxscramper Date: Mon, 14 Feb 2022 21:01:43 +0300 Subject: [PATCH] Finalize 'active' configuration subset split * Changes - Move missing 'input' configuration parameters to the ~CurrentConf~ - Added documentation for most of the configuration variables like ~git.url~, ~doc.item~, ~.always~, module search logic - Add tests for the CLI parser. - Add ~[FileIndex]~ implementation for the ~ConfigRef~ to avoid constantly writing ~conf.m.fileInfos[index.int32]~ as if we don't have templates/procedures to take care of repetitive code. - Closes https://github.com/nim-works/nimskull/issues/210 - it is hardly possible to gain anything by refactoring this part of the compiler. I updated documentation on some of the relevant pieces I found, as well as some minor tests, but otherwise there is nothing to gain here. Current implementation is usable for the purposes of https://github.com/nim-works/nimskull/issues/211 - it is possible to call into ~parseCommand()~ and get self-contained chunk of data for all the flags, that we can also do roundtrips on (using https://github.com/nim-works/nimskull/issues/213). * Tried to do but failed Due to large number of dependencies between data storage, reporting etc. it was not possible to really separate CLI parser logic from everything else. I tried separating them out into standalone files, but it was a lot more troubles than it worth: - Moving CLI parser would've require separating 'external' reports into different file, but this would still pull in ~ast_types.nim~, which is a pretty large dependency on its own. - CLI parser can do external reports - but it is possible to have multiple failures before processing terminates if there is a ~--errormax=N~ before. So logical idea of - "CLI flag is either valid or invalid" does not really cut here, since "invalid" can be postponed. - Because there is still a need to drag around whole suite of configuration state, reporting "deprecated" warnings can stay as it is now. --- compiler/ast/reports.nim | 3 + compiler/backend/extccomp.nim | 9 +- compiler/front/commands.nim | 10 +- compiler/front/in_options.nim | 34 ++++- compiler/front/msgs.nim | 41 +++--- compiler/front/options.nim | 125 ++++++++++++------ compiler/modules/nimblecmd.nim | 1 + compiler/nimfix/prettybase.nim | 13 +- compiler/vm/nimeval.nim | 2 +- doc/nimc.rst | 48 ++++++- .../simplePkgs/pkgA-0.0.1/pkgA/module.nim | 1 + .../pkgA-0.0.1/pkgA/srcDir/submodule.nim | 0 .../simplePkgs/pkgB-0.0.1/pkgB/module.nim | 1 + .../pkgB-0.0.1/pkgB/srcDir/submodule.nim | 0 .../simplePkgs/pkgB-0.0.2/pkgB/module.nim | 1 + .../pkgB-0.0.2/pkgB/srcDir/submodule.nim | 0 tests/compilerunits/confread/tcli_parsing.nim | 81 ++++++++++++ 17 files changed, 284 insertions(+), 86 deletions(-) create mode 100644 tests/compilerunits/confread/nimbleDir/simplePkgs/pkgA-0.0.1/pkgA/module.nim create mode 100644 tests/compilerunits/confread/nimbleDir/simplePkgs/pkgA-0.0.1/pkgA/srcDir/submodule.nim create mode 100644 tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.1/pkgB/module.nim create mode 100644 tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.1/pkgB/srcDir/submodule.nim create mode 100644 tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.2/pkgB/module.nim create mode 100644 tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.2/pkgB/srcDir/submodule.nim create mode 100644 tests/compilerunits/confread/tcli_parsing.nim diff --git a/compiler/ast/reports.nim b/compiler/ast/reports.nim index e6474478cf0..b47b0e73e1a 100644 --- a/compiler/ast/reports.nim +++ b/compiler/ast/reports.nim @@ -33,6 +33,9 @@ export from front/in_options import TOption, TOptions type InstantiationInfo* = typeof(instantiationInfo()) +# Importing and reexporting enums and 'external' reports in order to avoid +# needlessly cluttering the import lists of all modules that have to report +# something (and that would be almost all modules) import report_enums export report_enums diff --git a/compiler/backend/extccomp.nim b/compiler/backend/extccomp.nim index 462d42cb26a..d16e5e9863c 100644 --- a/compiler/backend/extccomp.nim +++ b/compiler/backend/extccomp.nim @@ -428,6 +428,7 @@ proc resetCompilationLists*(conf: ConfigRef) = conf.externalToLink.setLen 0 proc addExternalFileToLink*(conf: ConfigRef; filename: AbsoluteFile) = + ## Add new file to be linked with every compiled target. conf.externalToLink.insert(filename.string, 0) proc execWithEcho(conf: ConfigRef; cmd: string, execKind: ReportKind): int = @@ -471,6 +472,10 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} = result = conf.globalOptions * {optGenScript, optGenMapping} != {} proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string = + ## Construct CLI options to to compile a specific nim file. Options are + ## added in the following order: + ## `[global(--passc)][opt=speed/size/debug][file-local][.always]`. + ## `.always` is a configuration variable. result = conf.compileOptions for option in conf.compileOptionsCmd: @@ -912,7 +917,9 @@ proc callCCompiler*(conf: ConfigRef) = for idx, it in conf.toCompile: # call the C compiler for the .c file: if CfileFlag.Cached in it.flags: continue - let compileCmd = getCompileCFileCmd(conf, it, idx == conf.toCompile.len - 1, produceOutput=true) + let compileCmd = getCompileCFileCmd( + conf, it, idx == conf.toCompile.len - 1, produceOutput=true) + if optCompileOnly notin conf.globalOptions: cmds.add(compileCmd) prettyCmds.add displayProgressCC(conf, $it.cname, compileCmd) diff --git a/compiler/front/commands.nim b/compiler/front/commands.nim index 6c4d7443987..5a15e361d6e 100644 --- a/compiler/front/commands.nim +++ b/compiler/front/commands.nim @@ -588,7 +588,7 @@ proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, in expectArg(conf, switch, arg, pass, info) options.inclDynlibOverride(conf, arg) -template handleStdinOrCmdInput = +proc handleStdinOrCmdInput(conf: ConfigRef) = conf.projectFull = conf.projectName.AbsoluteFile conf.projectPath = AbsoluteDir getCurrentDir() if conf.outDir.isEmpty: @@ -597,11 +597,11 @@ template handleStdinOrCmdInput = proc handleStdinInput*(conf: ConfigRef) = conf.projectName = "stdinfile" conf.projectIsStdin = true - handleStdinOrCmdInput() + handleStdinOrCmdInput(conf) proc handleCmdInput*(conf: ConfigRef) = conf.projectName = "cmdfile" - handleStdinOrCmdInput() + handleStdinOrCmdInput(conf) proc parseCommand*(command: string): Command = # NOTE when adding elements to this list, sync with `cmdNames` const @@ -1373,7 +1373,9 @@ proc addCmdPrefix*(result: var string, kind: CmdLineKind) = of cmdArgument, cmdEnd: discard proc processCmdLine*(pass: TCmdLinePass, cmd: string; config: ConfigRef) = - ## Process input command-line parameters into `config` settings + ## Process input command-line parameters into `config` settings. Input is + ## a joined list of command-line arguments with multiple options and/or + ## configurations. var p = parseopt.initOptParser(cmd) var argsCount = 0 diff --git a/compiler/front/in_options.nim b/compiler/front/in_options.nim index cc2a4b7a39d..9e1ab05ea44 100644 --- a/compiler/front/in_options.nim +++ b/compiler/front/in_options.nim @@ -254,19 +254,33 @@ type ## system. backend*: TBackend ## set via `nim x` or `nim --backend:x` target*: Target # (+) - localOptions*: TOptions # (+) - globalOptions*: TGlobalOptions # (+) + localOptions*: TOptions ## Localized configuration options - they can + ## be set via command-line or using region-local pragmas. + globalOptions*: TGlobalOptions ## Global configuration options that can + ## only be supplied from the command line or the configuration files. cppDefines*: HashSet[string] #[ (*) ]# ## `--cppdefine` ?? features*: set[Feature] legacyFeatures*: set[LegacyFeature] + symbolFiles*: SymbolFilesOption symbols*: StringTableRef ## We need to use a StringTableRef here as ## defined symbols are always guaranteed to be style insensitive. ## Otherwise hell would break lose. + prefixDir*: AbsoluteDir + nimcacheDir*: AbsoluteDir ## Directory to write temporary generated + ## files to. + + libpath*: AbsoluteDir ## Path to the standard library + nimblePaths*: seq[AbsoluteDir] ## List of provided `--nimblePath` + ## directories + searchPaths*: seq[AbsoluteDir] ## Explicitly added list of the search + ## paths for modules. Those are queried first. + lazyPaths*: seq[AbsoluteDir] ## Implicitly constructed list of the + ## search paths for modules. Updated when `--nimblePath` option is + ## provided, and consists of explicitly provided nimble paths to the + ## found package directories. Last part allows to specify directory for + ## packages and avoid specifying `--path` for every single one of them. - nimblePaths*: seq[AbsoluteDir] - searchPaths*: seq[AbsoluteDir] - lazyPaths*: seq[AbsoluteDir] macrosToExpand*: StringTableRef ## Table of the target macros to expand. # Used as set for some reason, probably should actually be a set. @@ -313,6 +327,14 @@ type linkOptionsCmd*: seq[string] ## options passed from `passl` on the ## command line. - compileOptionsCmd*: seq[string] ## `passc` on the command line + compileOptionsCmd*: seq[string] ## `passc` on the command line. + ## Compilation options that would be used for every single file. They + ## are placed in front of the file-specific options. cppCustomNamespace*: string + + configVars*: StringTableRef ## Additional configuration variables for + ## providing extra options for different compiler subsystems. + +func flip*[I](s: var set[I], it: I, val: bool) = + if val: s.incl it else: s.excl it diff --git a/compiler/front/msgs.nim b/compiler/front/msgs.nim index f97d11d0721..95ef9ef28c5 100644 --- a/compiler/front/msgs.nim +++ b/compiler/front/msgs.nim @@ -89,7 +89,7 @@ when defined(nimpretty): # Wrapped in `when defined()` because `.fullContent` is not defined # without it. proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string = - substr(conf.m.fileInfos[fid.int].fullContent, a, b) + substr(conf[fid].fullContent, a, b) proc canonicalCase(path: var string) = ## the idea is to only use this for checking whether a path is already in @@ -211,18 +211,23 @@ template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0 or conf == nil: (if fileIdx == commandLineIdx: commandLineDesc else: "???") else: - conf.m.fileInfos[fileIdx.int32].shortName + conf[fileIdx].shortName proc toProjPath*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0 or conf == nil: - (if fileIdx == commandLineIdx: commandLineDesc else: "???") - else: conf.m.fileInfos[fileIdx.int32].projPath.string + if fileIdx == commandLineIdx: + commandLineDesc + else: + "???" + + else: + conf[fileIdx].projPath.string proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0 or conf == nil: result = (if fileIdx == commandLineIdx: commandLineDesc else: "???") else: - result = conf.m.fileInfos[fileIdx.int32].fullPath.string + result = conf[fileIdx].fullPath.string proc toReportLineInfo*(conf: ConfigRef, info: TLineInfo): ReportLineInfo = ReportLineInfo( @@ -231,24 +236,24 @@ proc toReportLineInfo*(conf: ConfigRef, info: TLineInfo): ReportLineInfo = proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile) = assert fileIdx.int32 >= 0 - conf.m.fileInfos[fileIdx.int32].dirtyFile = filename - setLen conf.m.fileInfos[fileIdx.int32].lines, 0 + conf[fileIdx].dirtyFile = filename + setLen conf[fileIdx].lines, 0 proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) = assert fileIdx.int32 >= 0 - shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash) + shallowCopy(conf[fileIdx].hash, hash) proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string = assert fileIdx.int32 >= 0 - shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash) + shallowCopy(result, conf[fileIdx].hash) proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile = if fileIdx.int32 < 0: result = AbsoluteFile(if fileIdx == commandLineIdx: commandLineDesc else: "???") - elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isEmpty: - result = conf.m.fileInfos[fileIdx.int32].dirtyFile + elif not conf[fileIdx].dirtyFile.isEmpty: + result = conf[fileIdx].dirtyFile else: - result = conf.m.fileInfos[fileIdx.int32].fullPath + result = conf[fileIdx].fullPath template toFilename*(conf: ConfigRef; info: TLineInfo): string = toFilename(conf, info.fileIndex) @@ -461,19 +466,19 @@ proc getContext*(conf: ConfigRef; lastinfo: TLineInfo): seq[ReportContext] = info = context.info proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) = - conf.m.fileInfos[fileIdx.int32].lines.add line + conf[fileIdx].lines.add line proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int = ## xxx there's an off by 1 error that should be fixed; if a file ends with "foo" or "foo\n" ## it will return same number of lines (ie, a trailing empty line is discounted) - result = conf.m.fileInfos[fileIdx.int32].lines.len + result = conf[fileIdx].lines.len if result == 0: try: for line in lines(toFullPathConsiderDirty(conf, fileIdx).string): addSourceLine conf, fileIdx, line except IOError: discard - result = conf.m.fileInfos[fileIdx.int32].lines.len + result = conf[fileIdx].lines.len proc sourceLine*(conf: ConfigRef; i: TLineInfo): string = ## 1-based index (matches editor line numbers); 1st line is for i.line = 1 @@ -483,7 +488,7 @@ proc sourceLine*(conf: ConfigRef; i: TLineInfo): string = # can happen if the error points to EOF: if i.line.int > num: return "" - result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1] + result = conf[i.fileIndex].lines[i.line.int - 1] proc getSurroundingSrc(conf: ConfigRef; info: TLineInfo): string = if conf.hasHint(rintSource) and info != unknownLineInfo: @@ -700,10 +705,10 @@ proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = result = makeCString "???" elif optExcessiveStackTrace in conf.globalOptions: - result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName + result = conf[i.fileIndex].quotedFullName else: - result = conf.m.fileInfos[i.fileIndex.int32].quotedName + result = conf[i.fileIndex].quotedName proc listWarnings*(conf: ConfigRef) = conf.localReport(InternalReport( diff --git a/compiler/front/options.nim b/compiler/front/options.nim index 4d232ca361d..5d773debbf2 100644 --- a/compiler/front/options.nim +++ b/compiler/front/options.nim @@ -65,10 +65,12 @@ type External ## file was introduced via .compile pragma Cfile* = object - nimname*: string + nimname*: string ## Original name of the nim file, constructed from the + ## module name. cname*, obj*: AbsoluteFile flags*: set[CfileFlag] customArgs*: string + CfileList* = seq[Cfile] Suggest* = ref object @@ -147,16 +149,6 @@ type # 'active' configuration verbosity*: int ## how verbose the compiler is - # Additional 'configuration variables', apparenly it was too hard to - # actually enumerate all the things that compiler uses, so there are - # random `dump.format` strings in the compiler, and of course they are - # not documented anywhere really. Processing is just done with 'if has - # dot then it is a config variable' - # - # `if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)` - configVars*: StringTableRef - - # 'arguments' aka a single string aka 'joining strings for external # program is bad' arguments*: string ## the arguments to be passed to the program that @@ -185,7 +177,6 @@ type lastCmdTime*: float ## Start of the last compiler commmand - set ## in the `main.mainCommand` and then read to generate 'successX' ## message - symbolFiles*: SymbolFilesOption headerFile*: string ideCmd*: IdeCmd @@ -202,7 +193,6 @@ type packageCache*: StringTableRef jsonBuildFile*: AbsoluteFile - prefixDir*, libpath*, nimcacheDir*: AbsoluteDir nimStdlibVersion*: NimVer moduleOverrides*: StringTableRef cfileSpecificOptions*: StringTableRef ## File specific compilation options for C backend. @@ -226,10 +216,11 @@ type ## processed during compilation. externalToLink*: seq[string] ## files to link in addition to the file - ## we compiled. Modified by the `{.link.}` pragma + ## we compiled. Modified by the `{.link.}` pragma and `--link` + ## command-line flag. linkOptions*: string ## Additional linking options, modified by the ## `{.passl.}` pragma - compileOptions*: string ## Additional compilation optinos, modified by + compileOptions*: string ## Additional compilation options, modified by ## the `{.passc.}` pragma cCompilerPath*: string toCompile*: CfileList # (*) @@ -253,6 +244,9 @@ type debugUtilsStack*: seq[string] ## which proc name to stop trace output ## len is also used for output indent level +template `[]`*(conf: ConfigRef, idx: FileIndex): TFileInfo = + conf.m.fileInfos[idx.uint32] + template passField(fieldname, fieldtype: untyped): untyped = proc `fieldname`*(conf: ConfigRef): fieldtype = conf.active.fieldname @@ -289,6 +283,10 @@ template passSeqField(fieldname, itemtype: untyped): untyped = passField backend, TBackend +passField symbolFiles, SymbolFilesOption +passField prefixDir, AbsoluteDir +passField libpath, AbsoluteDir +passField nimcacheDir, AbsoluteDir passField target, Target passField cppDefines, HashSet[string] passField cmd, Command @@ -321,6 +319,7 @@ passSetField features, set[Feature], Feature passSetField legacyFeatures, set[LegacyFeature], LegacyFeature passStrTableField dllOverrides +passStrTableField configVars passStrTableField symbols passStrTableField macrosToExpand passStrTableField arcToExpand @@ -845,10 +844,7 @@ proc newConfigRef*(hook: ReportHook): ConfigRef = structuredReportHook: hook, m: initMsgConfig(), headerFile: "", - configVars: newStringTable(modeStyleInsensitive), packageCache: newPackageCache(), - prefixDir: AbsoluteDir"", - libpath: AbsoluteDir"", nimcacheDir: AbsoluteDir"", moduleOverrides: newStringTable(modeStyleInsensitive), cfileSpecificOptions: newStringTable(modeCaseSensitive), projectIsStdin: false, # whether we're compiling from stdin @@ -866,9 +862,10 @@ proc newConfigRef*(hook: ReportHook): ConfigRef = cCompiler: ccGcc, macrosToExpand: newStringTable(modeStyleInsensitive), arcToExpand: newStringTable(modeStyleInsensitive), + configVars: newStringTable(modeStyleInsensitive), + dllOverrides: newStringTable(modeCaseInsensitive), outFile: RelativeFile"", outDir: AbsoluteDir"", - dllOverrides: newStringTable(modeCaseInsensitive), ), suggestMaxResults: 10_000, maxLoopIterationsVM: 10_000_000, @@ -897,7 +894,7 @@ proc getStdlibVersion*(conf: ConfigRef): NimVer = conf.nimStdlibVersion = s.parseNimVersion result = conf.nimStdlibVersion -proc isDefined*(conf: ConfigRef; symbol: string): bool = +proc isDefined*(conf: CurrentConf; symbol: string): bool = if conf.symbols.hasKey(symbol): result = true elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0: @@ -945,6 +942,9 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osDragonfly, osMacosx} else: discard +proc isDefined*(conf: ConfigRef, symbol: string): bool = + conf.active.isDefined(symbol) + proc getDefined*(conf: ConfigRef, sym: string): string = conf.symbols[sym] @@ -1003,15 +1003,17 @@ proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile = createDir result.string.parentDir proc getPrefixDir*(conf: ConfigRef): AbsoluteDir = - ## Gets the prefix dir, usually the parent directory where the binary resides. + ## Gets the prefix dir, usually the parent directory where the binary + ## resides. ## - ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir`` - ## field. - ## This should resolve to root of nim sources, whether running nim from a local - ## clone or using installed nim, so that these exist: `result/doc/advopt.txt` - ## and `result/lib/system.nim` - if not conf.prefixDir.isEmpty: result = conf.prefixDir - else: result = AbsoluteDir splitPath(getAppDir()).head + ## This is overridden by some tools (namely nimsuggest) via the + ## ``conf.prefixDir`` field. This should resolve to root of nim sources, + ## whether running nim from a local clone or using installed nim, so that + ## these exist: `result/doc/advopt.txt` and `result/lib/system.nim` + if not conf.prefixDir.isEmpty: + result = conf.prefixDir + else: + result = AbsoluteDir splitPath(getAppDir()).head proc setDefaultLibpath*(conf: ConfigRef) = ## set default value (can be overwritten): @@ -1073,8 +1075,8 @@ proc getOsCacheDir(): string = else: result = getHomeDir() / genSubDir.string -proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = - proc nimcacheSuffix(conf: ConfigRef): string = +proc getNimcacheDir*(conf: CurrentConf): AbsoluteDir = + proc nimcacheSuffix(conf: CurrentConf): string = if conf.cmd == cmdCheck: "_check" elif isDefined(conf, "release") or isDefined(conf, "danger"): "_r" else: "_d" @@ -1091,7 +1093,13 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name & nimcacheSuffix(conf)) +proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = + conf.active.getNimcacheDir() + proc pathSubs*(conf: ConfigRef; p, config: string): string = + ## Substitute text `p` with configuration paths, such as project name, + ## nim cache directory, project directory etc. `config` is an argument of + ## the configuration file path in case `$config` template is used. let home = removeTrailingDirSep(os.getHomeDir()) result = unixToNativePath(p % [ "nim", getPrefixDir(conf).string, @@ -1104,6 +1112,8 @@ proc pathSubs*(conf: ConfigRef; p, config: string): string = "nimcache", getNimcacheDir(conf).string]).expandTilde iterator nimbleSubs*(conf: ConfigRef; p: string): string = + ## Iterate over possible interpolations of the path string `p` and known + ## package directories. let pl = p.toLowerAscii if "$nimblepath" in pl or "$nimbledir" in pl: for i in countdown(conf.nimblePaths.len-1, 0): @@ -1112,14 +1122,18 @@ iterator nimbleSubs*(conf: ConfigRef; p: string): string = else: yield p -proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile, - ext: string): AbsoluteFile = +proc toGeneratedFile*( + conf: CurrentConf | ConfigRef, + path: AbsoluteFile, + ext: string + ): AbsoluteFile = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" - result = getNimcacheDir(conf) / RelativeFile path.string.splitPath.tail.changeFileExt(ext) + result = getNimcacheDir(conf) / RelativeFile( + path.string.splitPath.tail.changeFileExt(ext)) proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, createSubDir: bool = true): AbsoluteFile = - let subdir = getNimcacheDir(conf) + let subdir = getNimcacheDir(conf.active) if createSubDir: try: createDir(subdir.string) @@ -1133,6 +1147,7 @@ proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile = withPackageName(conf, f)), ext) proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile = + ## Find file using list of explicit search paths for it in conf.searchPaths: if suppressStdlib and it.string.startsWith(conf.libpath.string): continue @@ -1142,6 +1157,10 @@ proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): Absolu result = AbsoluteFile"" proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile = + ## Find file using list of lazy paths. If relative file is found bring + ## lazy path forward. TODO - lazy path reordering appears to be an + ## 'optimization' feature, but it might have some implicit dependencies + ## elsewhere. for i, it in conf.lazyPaths: result = it / f if fileExists(result): @@ -1152,7 +1171,9 @@ proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile = return canonicalizePath(conf, result) result = AbsoluteFile"" -template patchModule(conf: ConfigRef) {.dirty.} = +proc patchModule(conf: ConfigRef, result: var AbsoluteFile) = + ## If there is a known module override for a given + ## `package/` replace result with new module override. if not result.isEmpty and conf.moduleOverrides.len > 0: let key = getPackageName(conf, result.string) & "_" & splitFile(result).name if conf.moduleOverrides.hasKey(key): @@ -1195,6 +1216,11 @@ proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile, isTitle = search(conf.lazyPaths) proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile = + ## Find module file using search paths or lazy search paths (in that + ## order). If suppress stdlib is used - do not try to return files that + ## start with current `conf.libpath` prefix. First explicit search paths + ## are queried, and then lazy load paths (generated from directories) are + ## used. if f.isAbsolute: result = if f.fileExists: AbsoluteFile(f) else: AbsoluteFile"" else: @@ -1205,10 +1231,22 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile result = rawFindFile2(conf, RelativeFile f) if result.isEmpty: result = rawFindFile2(conf, RelativeFile f.toLowerAscii) - patchModule(conf) + patchModule(conf, result) proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile = - # returns path to module + ## Return absolute path to the imported module `modulename`. Imported + ## path can be relative to the `currentModule`, absolute one, `std/` or + ## `pkg/`-prefixed. In case of `pkg/` prefix it is dropped and search is + ## performed again, while ignoring stdlib. + ## + ## Search priority is + ## + ## 1. `pkg/` prefix + ## 2. Stdlib prefix + ## 3. Relative to the current file + ## 4. Search in the `--path` (see `findFile` and `rawFindFile`) + ## + ## If the module is found and exists module override, apply it last. var m = addFileExt(modulename, NimExt) if m.startsWith(pkgPrefix): result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true) @@ -1225,9 +1263,10 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi result = AbsoluteFile currentPath / m if not fileExists(result): result = findFile(conf, m) - patchModule(conf) + patchModule(conf, result) proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = + ## Find configuration file for a current project const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"] var candidates: seq[string] = @[] @@ -1268,10 +1307,8 @@ proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = return "" proc canonicalImportAux*(conf: ConfigRef, file: AbsoluteFile): string = - ##[ - Shows the canonical module import, e.g.: - system, std/tables, fusion/pointers, system/assertions, std/private/asciitables - ]## + ## Shows the canonical module import, e.g.: system, std/tables, + ## fusion/pointers, system/assertions, std/private/asciitables var ret = getRelativePathFromConfigPath(conf, file, isTitle = true) let dir = getNimbleFile(conf, $file).parentDir.AbsoluteDir if not dir.isEmpty: @@ -1286,7 +1323,9 @@ proc canonicalImport*(conf: ConfigRef, file: AbsoluteFile): string = let ret = canonicalImportAux(conf, file) result = ret.nativeToUnixPath.changeFileExt("") -proc canonDynlibName(s: string): string = +proc canonDynlibName*(s: string): string = + ## Get 'canonical' dynamic library name - without optional `lib` prefix + ## on linux and optional version pattern or extension. `libgit2.so -> git2` let start = if s.startsWith("lib"): 3 else: 0 let ende = strutils.find(s, {'(', ')', '.'}) if ende >= 0: diff --git a/compiler/modules/nimblecmd.nim b/compiler/modules/nimblecmd.nim index fa24114e66a..abd242ca39a 100644 --- a/compiler/modules/nimblecmd.nim +++ b/compiler/modules/nimblecmd.nim @@ -144,6 +144,7 @@ iterator chosen(packages: PackageInfo): string = yield res proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) = + ## Add paths from the var path = p let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link")) if nimbleLinks.len > 0: diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index 64d25aa0296..9ab6f28cb57 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -37,8 +37,8 @@ proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) - system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) - conf.m.fileInfos[info.fileIndex.int32].dirty = true + system.shallowCopy(conf[info.fileIndex].lines[info.line.int-1], x) + conf[info.fileIndex].dirty = true #if newSym.s == "File": writeStackTrace() proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) = @@ -47,8 +47,9 @@ proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) proc replaceComment*(conf: ConfigRef; info: TLineInfo) = let line = sourceLine(conf, info) var first = info.col.int - if line[first] != '#': inc first + if line[first] != '#': + inc first - var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape - system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) - conf.m.fileInfos[info.fileIndex.int32].dirty = true + var x = line.substr(0, first-1) & "discard " & line.substr(first + 1).escape + system.shallowCopy(conf[info.fileIndex].lines[info.line.int - 1], x) + conf[info.fileIndex].dirty = true diff --git a/compiler/vm/nimeval.nim b/compiler/vm/nimeval.nim index 42cde2a7688..b5f35fb46d4 100644 --- a/compiler/vm/nimeval.nim +++ b/compiler/vm/nimeval.nim @@ -126,7 +126,7 @@ proc findNimStdLib*(): string = proc findNimStdLibCompileTime*(): string = ## Same as `findNimStdLib` but uses source files used at compile time, ## and asserts on error. - result = querySetting(libPath) + result = querySetting(SingleValueSetting.libPath) doAssert fileExists(result / "system.nim"), "result:" & result proc createInterpreter*( diff --git a/doc/nimc.rst b/doc/nimc.rst index 988880f5e7d..e32624da3ea 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -527,7 +527,36 @@ Define Effect loaded libraries. ====================== ========================================================= +Configuration variables +======================= +Configuration variables can be either supplied via command-line or using +`.cfg` files. In configuration file you can use multiline strings for +options like `doc.item` or `doc.file` + +=========================== ============================================ +Variable Effect +=========================== ============================================ +`.always` Pass compilation flag to the specific module +`doc.toc` Documentation generator table of contents text +`.options.always` Flags for a specific C compiler +`.path` Path to the compiler executable +`.exe` Compiler executable +`.options.debug` Flags for debug compilation +`.options.size` Flags for 'size' optimization +`.options.speed` Flags for 'speed' compiler optimization +`.options.linker` Linker exe +`dump.format` Format of the compiler state dump output +`doc.file` Generated documentation template body +`doc.item` Template for a documentation entry item +`doc.listing_end` HTML end of the code listing +`doc.listing_start` HTML start of the code listing +`doc.listing_button` +`doc.smiley_format` +`git.url` Github repository URL +`git.devel` Git branch +`git.commit` Current git commit +=========================== ============================================ Additional Features =================== @@ -587,9 +616,9 @@ can be read in the `Nim Backend Integration document `_. Nim documentation tools ======================= -Nim provides the `doc`:idx: command to generate HTML -documentation from ``.nim`` source files. Only exported symbols will appear in -the output. For more details `see the docgen documentation `_. +Nim provides the `doc`:idx: command to generate HTML documentation from +``.nim`` source files. Only exported symbols will appear in the output. For +more details `see the docgen documentation `_. Nim idetools integration ======================== @@ -645,10 +674,10 @@ A good start is to use the `any` operating target together with the If your platform does not provide these functions it should be trivial to provide an implementation for them and link these to your program. -For targets with very restricted memory, it might be beneficial to pass some -additional flags to both the Nim compiler and the C compiler and/or linker -to optimize the build for size. For example, the following flags can be used -when targeting a gcc compiler: +For targets with a very restricted memory, it might be beneficial to pass +some additional flags to both the Nim compiler and the C compiler and/or +linker to optimize the build for size. For example, the following flags can +be used when targeting a gcc compiler: `--opt:size --passC:-flto --passL:-flto`:option: @@ -719,17 +748,20 @@ a procedure call, because the callee returns a new string anyway. Thus it is efficient to do: .. code-block:: Nim + var s = procA() # assignment will not copy the string; procA allocates a new # string already However, it is not efficient to do: .. code-block:: Nim + var s = varA # assignment has to copy the whole string into a new buffer! For `let` symbols a copy is not always necessary: .. code-block:: Nim + let s = varA # may only copy a pointer if it safe to do so @@ -737,6 +769,7 @@ If you know what you're doing, you can also mark single-string (or sequence) objects as `shallow`:idx:\: .. code-block:: Nim + var s = "abc" shallow(s) # mark 's' as a shallow string var x = s # now might not copy the string! @@ -750,6 +783,7 @@ if several different string constants are used. So code like this is reasonably efficient: .. code-block:: Nim + case normalize(k.key) of "name": c.name = v of "displayname": c.displayName = v diff --git a/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgA-0.0.1/pkgA/module.nim b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgA-0.0.1/pkgA/module.nim new file mode 100644 index 00000000000..8c134ad91ca --- /dev/null +++ b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgA-0.0.1/pkgA/module.nim @@ -0,0 +1 @@ +discard 0 diff --git a/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgA-0.0.1/pkgA/srcDir/submodule.nim b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgA-0.0.1/pkgA/srcDir/submodule.nim new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.1/pkgB/module.nim b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.1/pkgB/module.nim new file mode 100644 index 00000000000..8c134ad91ca --- /dev/null +++ b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.1/pkgB/module.nim @@ -0,0 +1 @@ +discard 0 diff --git a/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.1/pkgB/srcDir/submodule.nim b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.1/pkgB/srcDir/submodule.nim new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.2/pkgB/module.nim b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.2/pkgB/module.nim new file mode 100644 index 00000000000..8c134ad91ca --- /dev/null +++ b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.2/pkgB/module.nim @@ -0,0 +1 @@ +discard 0 diff --git a/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.2/pkgB/srcDir/submodule.nim b/tests/compilerunits/confread/nimbleDir/simplePkgs/pkgB-0.0.2/pkgB/srcDir/submodule.nim new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/compilerunits/confread/tcli_parsing.nim b/tests/compilerunits/confread/tcli_parsing.nim new file mode 100644 index 00000000000..d7dc7cfffa9 --- /dev/null +++ b/tests/compilerunits/confread/tcli_parsing.nim @@ -0,0 +1,81 @@ +discard """ +description: "" +joinable: false +""" + +import + ast/[ + reports + ], + front/[ + options, + commands + ], + std/[ + unittest, + strtabs, + strutils, + os, + algorithm, + sequtils + ] + + +var reported: seq[Report] + +proc hook(conf: ConfigRef, report: Report): TErrorHandling = + reported.add report + return doNothing + +proc getReports(): seq[Report] = + result = reported + reported = @[] + + +proc parse(cmd: string, pass: TCmdLinePass = passCmd1): tuple[reports: seq[Report], conf: CurrentConf] = + var tmp = newConfigRef(hook) + processCmdLine(pass, cmd, tmp) + result.reports = getReports() + result.conf = tmp.active + +proc parse(cmds: seq[string], pass: TCmdLinePass = passCmd1): auto = + parse(cmds.join(" "), pass) + +suite "Basic command parsing": + test "Valid commands": + let conf = parse("compile file.nim").conf + + check conf.projectName == "file.nim" + check conf.cmd == cmdCompileToC + + test "Backend resetting": + let conf = parse("c --backend:cpp --backend:js file.nim").conf + check conf.projectName == "file.nim" + check conf.backend == backendJs + + test "Hint configuration": + let conf = parse("--hint=all:off --hint=Processing:on").conf + check(conf.noteSets[cnCurrent] * repHintKinds == {rsemProcessing}) + + test "Warning configuration": + let conf = parse("--warning=all:off --warning=UnusedImport:on").conf + check(conf.noteSets[cnCurrent] * repWarningKinds == {rsemUnusedImport}) + +# import hmisc/other/hpprint + +let csd = currentSourcePath().parentDir() + +suite "Path options specification": + test "Inferring lazy paths for packages": + let conf = parse( + "--nimblePath=$#/nimbleDir/simplePkgs" % csd, + passCmd2).conf + + let lazy = conf.lazyPaths.mapIt(it.string).sorted() + let pkg = csd / "nimbleDir/simplePkgs" + let expect = sorted(@[pkg, pkg / "pkgA-0.0.1", pkg / "pkgB-0.0.2"]) + check lazy[0] == expect[0] + check lazy[1] == expect[1] + check lazy[2] == expect[2] + + # pprint(conf, ignore = matchField("noteSets"))