Skip to content

Commit 91f28e7

Browse files
authored
import v2 (#1170)
1 parent 6f3fdeb commit 91f28e7

File tree

12 files changed

+201
-160
lines changed

12 files changed

+201
-160
lines changed

src/lib/tooldirs.nim

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## This module provides functions to find tools in the Nimony bin directory.
2+
3+
import std / [os, strutils]
4+
5+
proc binDir*(): string =
6+
let appDir = getAppDir()
7+
let (_, tail) = splitPath(appDir)
8+
if tail == "bin":
9+
result = appDir
10+
else:
11+
result = appDir / "bin"
12+
13+
proc toolDir*(f: string): string =
14+
result = binDir() / f
15+
16+
proc findTool*(name: string): string =
17+
result = name.addFileExt(ExeExt)
18+
if fileExists(result):
19+
discard "ok"
20+
elif not name.isAbsolute:
21+
let t = toolDir(result)
22+
if fileExists(t):
23+
result = t

src/nifmake/nifmake.nim

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
## by a .nif file or it can translate this file to a Makefile.
1010

1111
import std/[assertions, os, strutils, sequtils, tables, hashes, times, sets, parseopt, syncio, osproc]
12-
import ".." / lib / [nifstreams, nifcursors, bitabs, lineinfos, nifreader]
12+
import ".." / lib / [nifstreams, nifcursors, bitabs, lineinfos, nifreader, tooldirs]
1313

1414
# Inspired by https://gittup.org/tup/build_system_rules_and_algorithms.pdf
1515
#[
@@ -87,26 +87,6 @@ proc skipParRi(n: var Cursor) =
8787
#echo toString([n.load()])
8888
quit "Expected ')' but found: " & $n.kind
8989

90-
proc binDir*(): string =
91-
let appDir = getAppDir()
92-
let (_, tail) = splitPath(appDir)
93-
if tail == "bin":
94-
result = appDir
95-
else:
96-
result = appDir / "bin"
97-
98-
proc toolDir*(f: string): string =
99-
result = binDir() / f
100-
101-
proc findTool(name: string): string =
102-
result = name.addFileExt(ExeExt)
103-
if fileExists(result):
104-
discard "ok"
105-
elif not name.isAbsolute:
106-
let t = toolDir(result)
107-
if fileExists(t):
108-
result = t
109-
11090
proc addSpace(result: var string) {.inline.} =
11191
if result.len > 0 and result[^1] != ' ': result.add ' '
11292

src/nifmake/readme.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,6 @@
22

33
Nifmake is a make-like tool used by Nimony to implement parallel and incremental compilation. It can either run a dependency graph directly or translate it to a Makefile.
44

5-
## Features
6-
7-
- **Incremental builds**: Only rebuilds files when dependencies change
8-
- **Dependency tracking**: Automatically tracks file dependencies
9-
- **Makefile generation**: Can generate standard Makefiles
10-
- **Cycle detection**: Detects circular dependencies in build graphs
11-
- **Declarative build descriptions using NIF**
12-
- **Parallel build execution**
13-
- **Reusable command definitions**
145

156
## Usage
167

src/nimony/deps.nim

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import std/[os, tables, sets, syncio, assertions, strutils, times]
1818
import semos, nifconfig, nimony_model, nifindexes
1919
import ".." / gear2 / modnames, semdata
20+
import ".." / lib / tooldirs
2021

2122
include nifprelude
2223

@@ -25,11 +26,14 @@ type
2526
nimFile: string
2627
modname: string
2728

28-
proc indexFile(config: NifConfig; f: FilePair): string = config.nifcachePath / f.modname & ".2.idx.nif"
29+
proc indexFile(config: NifConfig; f: FilePair; bundle: string): string =
30+
config.nifcachePath / bundle / f.modname & ".2.idx.nif"
31+
2932
proc parsedFile(config: NifConfig; f: FilePair): string = config.nifcachePath / f.modname & ".1.nif"
3033
proc depsFile(config: NifConfig; f: FilePair): string = config.nifcachePath / f.modname & ".1.deps.nif"
3134
proc deps2File(config: NifConfig; f: FilePair): string = config.nifcachePath / f.modname & ".2.deps.nif"
32-
proc semmedFile(config: NifConfig; f: FilePair): string = config.nifcachePath / f.modname & ".2.nif"
35+
proc semmedFile(config: NifConfig; f: FilePair; bundle: string): string =
36+
config.nifcachePath / bundle / f.modname & ".2.nif"
3337
proc nifcFile(config: NifConfig; f: FilePair): string = config.nifcachePath / f.modname & ".c.nif"
3438
proc cFile(config: NifConfig; f: FilePair): string = config.nifcachePath / f.modname & ".c"
3539
proc objFile(config: NifConfig; f: FilePair): string = config.nifcachePath / f.modname & ".o"
@@ -46,7 +50,7 @@ proc resolveFileWrapper(paths: openArray[string]; origin: string; toResolve: str
4650
type
4751
Node = ref object
4852
files: seq[FilePair]
49-
deps: seq[FilePair]
53+
deps: seq[int] # index into c.nodes
5054
id, parent: int
5155
active: int
5256
isSystem: bool
@@ -66,7 +70,7 @@ type
6670
nodes: seq[Node]
6771
rootNode: Node
6872
includeStack: seq[string]
69-
processedModules: HashSet[string]
73+
processedModules: Table[string, int] # modname -> index to c.nodes
7074
moduleFlags: set[ModuleFlag]
7175
isGeneratingFinal: bool
7276
foundPlugins: HashSet[string]
@@ -125,9 +129,12 @@ proc importSingleFile(c: var DepContext; f1: string; info: PackedLineInfo;
125129
let f2 = resolveFileWrapper(c.config.paths, current.files[current.active].nimFile, f1)
126130
if not semos.fileExists(f2): return
127131
let p = c.toPair(f2)
128-
if not c.processedModules.containsOrIncl(p.modname):
129-
current.deps.add p
130-
var imported = Node(files: @[p], id: c.nodes.len, parent: current.id, isSystem: isSystem)
132+
let existingNode = c.processedModules.getOrDefault(p.modname, -1)
133+
if existingNode == -1:
134+
var imported = Node(files: @[p], id: c.nodes.len, parent: current.id, isSystem: isSystem,
135+
plugin: current.plugin)
136+
current.deps.add imported.id
137+
c.processedModules[p.modname] = imported.id
131138
c.nodes.add imported
132139
parseDeps c, p, imported
133140
else:
@@ -136,17 +143,22 @@ proc importSingleFile(c: var DepContext; f1: string; info: PackedLineInfo;
136143
discard "ignore cycle"
137144
echo "cycle detected: ", current.files[0].nimFile, " <-> ", p.nimFile
138145
else:
139-
current.deps.add p
146+
current.deps.add existingNode
140147

141148
proc processPluginImport(c: var DepContext; f: ImportedFilename; info: PackedLineInfo; current: Node) =
142149
let f2 = resolveFileWrapper(c.config.paths, current.files[current.active].nimFile, f.path)
143150
if not semos.fileExists(f2): return
144151
let p = c.toPair(f2)
145-
if not c.processedModules.containsOrIncl(p.modname):
146-
current.deps.add p
147-
c.nodes.add Node(files: @[p], id: c.nodes.len,
148-
parent: current.id, isSystem: false, plugin: f.plugin)
152+
let existingNode = c.processedModules.getOrDefault(p.modname, -1)
153+
if existingNode == -1:
154+
var imported = Node(files: @[p], id: c.nodes.len,
155+
parent: current.id, isSystem: false, plugin: f.plugin)
156+
current.deps.add imported.id
157+
c.processedModules[p.modname] = imported.id
158+
c.nodes.add imported
149159
c.foundPlugins.incl f.plugin
160+
else:
161+
current.deps.add existingNode
150162

151163
proc processImport(c: var DepContext; it: var Cursor; current: Node) =
152164
let info = it.info
@@ -235,18 +247,25 @@ proc execNifler(c: var DepContext; f: FilePair) =
235247

236248
proc importSystem(c: var DepContext; current: Node) =
237249
let p = c.toPair(stdlibFile("std/system.nim"))
238-
current.deps.add p
239-
if not c.processedModules.containsOrIncl(p.modname):
250+
var existingNode = c.processedModules.getOrDefault(p.modname, -1)
251+
if existingNode == -1:
240252
#echo "NIFLING ", p.nimFile, " -> ", c.config.parsedFile(p)
241253
execNifler c, p
242254
var imported = Node(files: @[p], id: c.nodes.len, parent: current.id, isSystem: true)
243255
c.nodes.add imported
256+
c.processedModules[p.modname] = imported.id
244257
parseDeps c, p, imported
258+
existingNode = imported.id
259+
current.deps.add existingNode
245260

246261
proc parseDeps(c: var DepContext; p: FilePair; current: Node) =
247-
execNifler c, p
262+
let depsFile: string
263+
if not c.isGeneratingFinal:
264+
execNifler c, p
265+
depsFile = c.config.depsFile(p)
266+
else:
267+
depsFile = c.config.deps2File(p)
248268

249-
let depsFile = if c.isGeneratingFinal: c.config.deps2File(p) else: c.config.depsFile(p)
250269
var stream = nifstreams.open(depsFile)
251270
try:
252271
discard processDirectives(stream.r)
@@ -269,7 +288,8 @@ proc rootPath(c: DepContext): string =
269288
proc toBuildList(c: DepContext): seq[CFile] =
270289
result = @[]
271290
for v in c.nodes:
272-
let index = readIndex(c.config.indexFile(v.files[0]))
291+
#if v.plugin.len > 0: continue
292+
let index = readIndex(c.config.indexFile(v.files[0], v.plugin))
273293
for i in index.toBuild:
274294
let path = i[1]
275295
let obj = splitFile(path).name & ".o"
@@ -399,9 +419,9 @@ proc generateFinalBuildFile(c: DepContext; commandLineArgsNifc: string; passC, p
399419
b.withTree "do":
400420
b.addIdent "hexer"
401421
b.withTree "input":
402-
b.addStrLit c.config.semmedFile(v.files[0])
422+
b.addStrLit c.config.semmedFile(v.files[0], v.plugin)
403423
b.withTree "input":
404-
b.addStrLit c.config.indexFile(v.files[0])
424+
b.addStrLit c.config.indexFile(v.files[0], v.plugin)
405425
b.withTree "output":
406426
b.addStrLit c.config.nifcFile(v.files[0])
407427

@@ -424,8 +444,8 @@ proc generateSemInstructions(c: DepContext; v: Node; b: var Builder; isMain: boo
424444
b.withTree "input":
425445
b.addStrLit pf
426446
# Input: dependencies
427-
for f in v.deps:
428-
let idxFile = c.config.indexFile(f)
447+
for i in v.deps:
448+
let idxFile = c.config.indexFile(c.nodes[i].files[0], c.nodes[i].plugin)
429449
if not seenDeps.containsOrIncl(idxFile):
430450
b.withTree "input":
431451
b.addStrLit idxFile
@@ -434,19 +454,26 @@ proc generateSemInstructions(c: DepContext; v: Node; b: var Builder; isMain: boo
434454
b.addStrLit c.config.cachedConfigFile()
435455
# Outputs: semmed file and index file
436456
b.withTree "output":
437-
b.addStrLit c.config.semmedFile(v.files[0])
457+
b.addStrLit c.config.semmedFile(v.files[0], v.plugin)
438458
b.withTree "output":
439-
b.addStrLit c.config.indexFile(v.files[0])
459+
b.addStrLit c.config.indexFile(v.files[0], v.plugin)
440460

441461
proc generatePluginSemInstructions(c: DepContext; v: Node; b: var Builder) =
462+
#[ An import plugin fills `nimcache/<plugin>` for us. It is our job to
463+
generate index files for all `.nif` files in there. Both the frontend and
464+
the backend needs these files. But we want the index generation to happen
465+
in parallel. We cannot iterate over the files in the plugin directory as
466+
it is empty until the plugin has run. So unfortunately this logic lives in
467+
the v2 plugin.
468+
]#
442469
b.withTree "do":
443470
b.addIdent v.plugin
444471
b.withTree "input":
445472
b.addStrLit v.files[0].nimFile
446473
b.withTree "output":
447-
b.addStrLit c.config.semmedFile(v.files[0])
474+
b.addStrLit c.config.semmedFile(v.files[0], v.plugin)
448475
b.withTree "output":
449-
b.addStrLit c.config.indexFile(v.files[0])
476+
b.addStrLit c.config.indexFile(v.files[0], v.plugin)
450477

451478
proc generateFrontendBuildFile(c: DepContext; commandLineArgs: string): string =
452479
result = c.config.nifcachePath / c.rootNode.files[0].modname & ".build.nif"
@@ -490,8 +517,9 @@ proc generateFrontendBuildFile(c: DepContext; commandLineArgs: string): string =
490517
b.addIntLit 0 # main parsed file
491518
b.withTree "output":
492519
b.addIntLit 0 # semmed file output
493-
b.withTree "output":
494-
b.addIntLit 1 # index file output
520+
# index file output is not explicitly passed to the plugin!
521+
#b.withTree "output":
522+
# b.addIntLit 1 # index file output
495523

496524
# Build rules for semantic checking
497525
var i = 0
@@ -537,7 +565,7 @@ proc initDepContext(config: sink NifConfig; project, nifler: string; isFinal, fo
537565
let p = result.toPair(project)
538566
result.rootNode = Node(files: @[p], id: 0, parent: -1, active: 0, isSystem: IsSystem in moduleFlags)
539567
result.nodes.add result.rootNode
540-
result.processedModules.incl p.modname
568+
result.processedModules[p.modname] = 0
541569
parseDeps result, p, result.rootNode
542570

543571
proc buildGraph*(config: sink NifConfig; project: string; forceRebuild, silentMake: bool;

src/nimony/indexgen.nim

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Nimony
2+
# (c) Copyright 2024 Andreas Rumpf
3+
#
4+
# See the file "license.txt", included in this
5+
# distribution, for details about the copyright.
6+
7+
## Nimony index generator.
8+
9+
import std / [os, assertions]
10+
include ".." / lib / nifprelude
11+
import ".." / lib / [nifindexes, symparser]
12+
import decls, nimony_model, programs
13+
14+
proc getAttachedOp(symId: SymId, attackedOp: var AttachedOp): bool =
15+
var name = pool.syms[symId]
16+
extractBasename(name)
17+
18+
attackedOp = case name
19+
of "=destroy": attachedDestroy
20+
of "=wasMoved": attachedWasMoved
21+
of "=trace": attachedTrace
22+
of "=copy": attachedCopy
23+
of "=sink": attachedSink
24+
of "=dup": attachedDup
25+
else: return false
26+
27+
return true
28+
29+
proc indexFromNif*(infile: string) =
30+
## Extract index from `infile` Nif file and write it to `*.idx.nif` file.
31+
##
32+
## See https://github.com/nim-lang/nimony/issues/1162
33+
var stream = nifstreams.open(infile)
34+
discard processDirectives(stream.r)
35+
var buf = fromStream(stream)
36+
stream.close
37+
38+
var n = beginRead buf
39+
let root = n.info
40+
var hookIndexLog = default array[AttachedOp, seq[HookIndexEntry]]
41+
var converterIndexMap = default seq[(SymId, SymId)]
42+
var classIndexMap = default seq[ClassIndexEntry]
43+
44+
assert n.stmtKind == StmtsS
45+
inc n
46+
while n.kind != ParRi:
47+
if n.kind == ParLe:
48+
case n.stmtKind:
49+
of ProcS, FuncS, ConverterS, MethodS:
50+
let routine = takeRoutine(n, SkipFinalParRi)
51+
let symId = routine.name.symId
52+
var op = default AttachedOp
53+
if getAttachedOp(symId, op):
54+
var param = routine.params
55+
assert param.substructureKind == ParamsU
56+
inc param
57+
assert param.substructureKind == ParamU
58+
let typ = takeLocal(param, SkipExclBody).typ.skipModifier
59+
# this assertion fails when got generics proc as generics parameters are not supported yet.
60+
if typ.kind == Symbol:
61+
let obj = typ.symId
62+
let isGeneric = routine.typevars.substructureKind == TypevarsU
63+
hookIndexLog[op].add HookIndexEntry(typ: obj, hook: symId, isGeneric: isGeneric)
64+
else:
65+
skip n
66+
else:
67+
skip n
68+
69+
endRead buf
70+
71+
createIndex infile, root, true,
72+
IndexSections(hooks: move hookIndexLog,
73+
converters: move converterIndexMap,
74+
classes: move classIndexMap)

src/nimony/nimony.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ when defined(windows):
1414
{.link: "../../icons/nimony_icon.o".}
1515

1616
import std / [parseopt, sets, strutils, os, assertions, syncio]
17+
import ".." / lib / tooldirs
1718

1819
import ".." / hexer / hexer # only imported to ensure it keeps compiling
1920
import ".." / gear2 / modnames

0 commit comments

Comments
 (0)