7474 CliCommand = enum
7575 cmdRun, cmdMakefile, cmdHelp, cmdVersion
7676
77+ CliOption = enum
78+ Parallel , Force , Verbose
79+
7780proc skipParRi (n: var Cursor ) =
7881 # # Helper to skip a closing parenthesis
7982 if n.kind == ParRi :
@@ -99,17 +102,20 @@ proc findTool(name: string): string =
99102 if fileExists (result ):
100103 discard " ok"
101104 elif not name.isAbsolute:
102- result = toolDir (result )
105+ let t = toolDir (result )
106+ if fileExists (t):
107+ result = t
103108
104109proc addSpace (result: var string ) {.inline .} =
105110 if result .len > 0 and result [^ 1 ] != ' ' : result .add ' '
106111
107112proc addFilename (result: var string ; filename, prefix, suffix: string ) =
108- result .addSpace ()
109- if prefix.len > 0 : result .add prefix
110- # This is not a bug, a suffix is always assumed to be part of the filename
111- # and so also subject to quoting:
112- result .add (suffix & filename).quoteShell
113+ if filename.len > 0 :
114+ result .addSpace ()
115+ if prefix.len > 0 : result .add prefix
116+ # This is not a bug, a suffix is always assumed to be part of the filename
117+ # and so also subject to quoting:
118+ result .add (suffix & filename).quoteShell
113119
114120proc expandCommand (cmd: Command ; inputs, outputs: seq [string ]): string =
115121 result = " "
@@ -131,6 +137,7 @@ proc expandCommand(cmd: Command; inputs, outputs: seq[string]): string =
131137 inc n
132138 of ParLe :
133139 let tag = pool.tags[n.tag]
140+ let L = if tag == " output" : outputs.len else : inputs.len
134141 var a = 0
135142 var b = 0
136143 inc n
@@ -140,12 +147,12 @@ proc expandCommand(cmd: Command; inputs, outputs: seq[string]): string =
140147 inc n
141148 if n.kind == IntLit :
142149 a = pool.integers[n.intId]
143- if a < 0 : a = inputs.len + a
150+ if a < 0 : a = L + a
144151 b = a
145152 inc n
146153 if n.kind == IntLit :
147154 b = pool.integers[n.intId]
148- if b < 0 : b = outputs.len + b
155+ if b < 0 : b = L + b
149156 inc n
150157 var suffix = " "
151158 if n.kind == StringLit :
@@ -214,7 +221,18 @@ proc getFileTime(dag: var Dag; filename: string): Time =
214221 result = getTime () # Use current time for non-existent files
215222 dag.timestampCache[filename] = result
216223
217- proc needsRebuild (dag: var Dag ; node: Node ): bool =
224+ proc removeOutdatedArtifacts (dag: var Dag ; node: Node ; opt: set [CliOption ]) =
225+ # # Remove outdated build artifacts for a node
226+ for output in node.outputs:
227+ if fileExists (output):
228+ try :
229+ removeFile (output)
230+ if Verbose in opt:
231+ echo " Removed outdated artifact: " , output
232+ except :
233+ stderr.writeLine " Warning: Could not remove outdated artifact: " , output
234+
235+ proc needsRebuild (dag: var Dag ; node: Node ; opt: set [CliOption ]): bool =
218236 # # Check if a node needs to be rebuilt
219237 result = false
220238
@@ -234,6 +252,8 @@ proc needsRebuild(dag: var Dag; node: Node): bool =
234252 if fileExists (input):
235253 let inputTime = dag.getFileTime (input)
236254 if inputTime >= oldestOutput:
255+ # Remove outdated artifacts before rebuilding
256+ dag.removeOutdatedArtifacts (node, opt)
237257 return true
238258
239259proc visit (nodes: var seq [Node ]; nodeId: int ; sortedNodes: var seq [int ]; maxDepth: var int ): bool =
@@ -275,16 +295,20 @@ proc executeCommand(command: string): bool =
275295 except :
276296 result = false
277297
298+ proc failed (arg: string ) =
299+ stdout.write " make: "
300+ stdout.writeLine arg
301+
278302type
279303 CmdStatus = enum
280304 Enqueued , Running , Finished
281305
282- proc runDag (dag: var Dag ; parallel: bool ): bool =
306+ proc runDag (dag: var Dag ; opt: set [ CliOption ] ): bool =
283307 # # Execute the DAG in topological order
284308 result = true
285309 let sortedNodes = topologicalSort (dag)
286310
287- if parallel :
311+ if Parallel in opt :
288312 var i = 0
289313 while i < sortedNodes.len:
290314 let currentDepth = dag.nodes[sortedNodes[i]].depth
@@ -294,10 +318,12 @@ proc runDag(dag: var Dag; parallel: bool): bool =
294318 # Collect all commands at the current depth
295319 while i < sortedNodes.len and dag.nodes[sortedNodes[i]].depth == currentDepth:
296320 let node = addr dag.nodes[sortedNodes[i]]
297- if dag.needsRebuild (node[]):
298- echo " Building: " , node.outputs.join (" , " )
321+ if Force in opt or dag.needsRebuild (node[], opt):
322+ if Verbose in opt:
323+ echo " Building: " , node.outputs.join (" , " )
299324 let expandedCmd = expandCommand (dag.commands[node.cmdIdx], node.inputs, node.outputs)
300- echo " Command: " , expandedCmd
325+ if Verbose in opt:
326+ echo " Command: " , expandedCmd
301327 commands.add (expandedCmd)
302328 nodeIds.add (sortedNodes[i])
303329 inc i
@@ -312,21 +338,24 @@ proc runDag(dag: var Dag; parallel: bool): bool =
312338 if maxExitCode != 0 :
313339 for i, p in pairs (progress):
314340 if p == Running :
315- echo " Error: Command failed: " , commands[i]
341+ failed commands[i]
316342 return false
317343 else :
318344 # Sequential execution
319345 for nodeId in sortedNodes:
320346 let node = addr dag.nodes[nodeId]
321- if dag.needsRebuild (node[]):
322- echo " Building: " , node.outputs.join (" , " )
347+ if Force in opt or dag.needsRebuild (node[], opt):
348+ if Verbose in opt:
349+ echo " Building: " , node.outputs.join (" , " )
323350 let expandedCmd = expandCommand (dag.commands[node.cmdIdx], node.inputs, node.outputs)
324- echo " Command: " , expandedCmd
351+ if Verbose in opt:
352+ echo " Command: " , expandedCmd
325353 if not executeCommand (expandedCmd):
326- echo " Error: Command failed: " , expandedCmd
354+ failed expandedCmd
327355 return false
328356 else :
329- echo " Up to date: " , node.outputs.join (" , " )
357+ if Verbose in opt:
358+ echo " Up to date: " , node.outputs.join (" , " )
330359
331360proc mescape (p: string ): string =
332361 when defined (windows):
@@ -504,6 +533,8 @@ Commands:
504533Options:
505534 -j, --parallel Enable parallel builds (for 'run' command)
506535 --makefile <name> Output Makefile name (default: Makefile)
536+ --force Force rebuild of all targets
537+ --verbose Show verbose output
507538
508539Examples:
509540 nifmake run build.nif
@@ -521,7 +552,7 @@ proc main() =
521552 cmd = cmdHelp
522553 inputFile = " "
523554 outputMakefile = " Makefile"
524- parallel = false
555+ opt: set [ CliOption ] = {}
525556
526557 for kind, key, val in getopt ():
527558 case kind
@@ -541,8 +572,10 @@ proc main() =
541572 case key.normalize
542573 of " help" , " h" : writeHelp ()
543574 of " version" , " v" : writeVersion ()
544- of " parallel" , " j" : parallel = true
575+ of " parallel" , " j" : opt. incl Parallel
545576 of " makefile" : outputMakefile = val
577+ of " force" : opt.incl Force
578+ of " verbose" : opt.incl Verbose
546579 else :
547580 echo " Unknown option: --" , key
548581 quit (1 )
@@ -557,8 +590,8 @@ proc main() =
557590 quit " Input file required for 'run' command"
558591
559592 var dag = parseNifFile (inputFile)
560- if not runDag (dag, parallel ):
561- quit " Build failed "
593+ if not runDag (dag, opt ):
594+ quit 1
562595
563596 of cmdMakefile:
564597 if inputFile == " " :
0 commit comments