@naanlang/naan 1.7.0 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -208,6 +208,7 @@ closure APIClient(imp, options, local client, url, guid) {
208
208
  closure WebSocketClient(options, imp, local wesc, ws, pending) {
209
209
  global(js)
210
210
  wesc = new(object, this)
211
+
211
212
 
212
213
  // reconnect
213
214
  //
@@ -226,40 +227,29 @@ closure WebSocketClient(options, imp, local wesc, ws, pending) {
226
227
  //
227
228
  // Establish a websocket connection with the host.
228
229
  //
229
- wesc.connect = closure connect(local url, result) {
230
+ wesc.connect = closure connect(local url, result, error) {
230
231
  if ws {
231
232
  if pending
232
233
  return (pending.wait()) // connect in process
233
234
  if !wesc.fatal
234
235
  return(list(false, { connected: true })) // already open
235
236
  }
236
- try {
237
- url = xnew(js.w.URL, options.url)
238
- } catch {
239
- return(list(Error("invalid URL ${options.url}", exception)))
240
- }
241
- if url.protocol == "https:"
242
- url.protocol = "wss:"
243
- else
244
- url.protocol = "ws:"
245
- ws = xnew(js.w.WebSocket, url.href)
246
- if wesc.fatal
247
- debuglog("WebSocketClient reopen") // ### not pretty to see this
248
- wesc.fatal = false
249
- pending = new(nonce)
250
237
 
251
238
  // onopen
252
239
  // The WebSocket connection is now open.
253
- ws.onopen = function onopen(e) {
240
+ function onopen(e) {
254
241
  if wesc.impconn {
255
242
  result = wesc.impconn.reopen()
256
243
  pending.signal(result)
257
244
  return
258
245
  }
259
- wesc.impconn = imp.requester(function(data) {
246
+ wesc.impconn = imp.requester(function(data, local error, text) {
260
247
  if !pending && reconnect() // !pending means not opening/reopening
261
248
  return // failed to reconnect
262
- ws.send(js.w.JSON.stringify(data))
249
+ `(error, text) = JsonStringify(data)
250
+ if error
251
+ return (list(error))
252
+ ws.send(text)
263
253
  list(false, { sent: true })
264
254
  }, {
265
255
  secret: options.guid
@@ -274,7 +264,7 @@ closure WebSocketClient(options, imp, local wesc, ws, pending) {
274
264
 
275
265
  // onmessage
276
266
  // A message was received via WebSockets.
277
- ws.onmessage = function onmessage(e, local error, message) {
267
+ function onmessage(e, local error, message) {
278
268
  `(error, message) = JsonParse(e.data)
279
269
  if !error {
280
270
  message = new(message)
@@ -289,7 +279,7 @@ closure WebSocketClient(options, imp, local wesc, ws, pending) {
289
279
  // onerror
290
280
  // An error occurred on the WebSocket connection.
291
281
  // This can happen in normal operation when the connection times out.
292
- ws.onerror = function onerror(e) {
282
+ function onerror(e) {
293
283
  if !wesc.fatal
294
284
  wesc.fatal = Error("websocket failed", e)
295
285
  pending.signal(list(wesc.fatal))
@@ -298,7 +288,7 @@ closure WebSocketClient(options, imp, local wesc, ws, pending) {
298
288
 
299
289
  // onclose
300
290
  // The WebSocket connection was closed.
301
- ws.onclose = function onclose(e) {
291
+ function onclose(e) {
302
292
  // if e.code != 1000 && e.code != 1001 && e.code != 1005 && e.code != 1006
303
293
  debuglog("WebSocketClient: closed:", e.code, e.reason) // ### not pretty to see this
304
294
  ws = false
@@ -308,6 +298,19 @@ closure WebSocketClient(options, imp, local wesc, ws, pending) {
308
298
  pending = false
309
299
  }
310
300
 
301
+ `(error, ws) = call(clirWebsocket(), options.url, {
302
+ onopen: onopen.proc
303
+ onmessage: onmessage.proc
304
+ onerror: onerror.proc
305
+ onclose: onclose.proc
306
+ })
307
+ if error
308
+ return (list(wesc.fatal = error)) // failed to even get started
309
+
310
+ if wesc.fatal
311
+ debuglog("WebSocketClient reopen") // ### not pretty to see this
312
+ wesc.fatal = false
313
+ pending = new(nonce)
311
314
  result = pending.wait(10000, list(Error("IMP connection timeout")))
312
315
  pending = false
313
316
  result
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2020-2025 by Richard C. Zulch
9
+ * Copyright (c) 2020-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -31,6 +31,26 @@ function clirRequester() {
31
31
  };
32
32
 
33
33
 
34
+ /*
35
+ * clirWebsocket
36
+ *
37
+ * Return an appropriate Websocket client based on our environment.
38
+ *
39
+ */
40
+
41
+ clirWebsocketApi = false;
42
+
43
+ function clirWebsocket() {
44
+ if !clirWebsocketApi {
45
+ if js.w
46
+ clirWebsocketApi = require("naanlib:frameworks/browser/ws_client.nlg").WebSocket
47
+ else
48
+ clirWebsocketApi = require("naanlib:frameworks/node/ws_client.nlg").WebSocket
49
+ }
50
+ clirWebsocketApi
51
+ };
52
+
53
+
34
54
  /*
35
55
  * clirInit
36
56
  *
@@ -40,7 +60,7 @@ function clirRequester() {
40
60
  */
41
61
 
42
62
  function clirInit(local manifest) {
43
- manifest = `(clirRequester, clirInit)
63
+ manifest = `(clirRequester, clirWebsocket, clirInit)
44
64
 
45
65
  Naan.module.build(module.id, "client", function(modobj, compobj) {
46
66
  require("../common/").LiveImport()
@@ -52,7 +52,7 @@ closure RelayCon(imp, options, local relay, nlregex)
52
52
  serverID: "Workers"
53
53
  workerID: "NideServer"
54
54
  }, options.config)
55
- relay.instanceID = UUID()
55
+ relay.instanceID = imp.getUUID("RelayCon(${options.name}) instanceID")
56
56
  relay.exec = execsMod.ExecutorBase(false, "Host", options.name)
57
57
  nlregex = RegExp("\\n", "g")
58
58
 
@@ -252,14 +252,16 @@ macro LiveExport() {
252
252
  importList = []
253
253
  //
254
254
  // LiveImport
255
- // Return a new LiveImport function to be exported by the module who called MakeLiveImport.
255
+ // Return a new LiveImport function to be exported by the module who called LiveExport.
256
256
  //
257
- macro LiveImport(fixproc, local importingMod, mdex, updater) {
257
+ macro LiveImport(fixproc, local importingMod, item, updater) {
258
258
  importingMod = car(nsrights().lookup("module"))
259
- mdex = importList.indexOf(importingMod)
260
- if mdex >= 0
261
- updater = importList[mdex] // already registered
262
- else {
259
+ for item in importList
260
+ if item.proc.1.namespace === importingMod.namespace {
261
+ updater = item
262
+ break
263
+ }
264
+ if !updater {
263
265
  updater = closure(importingMod, fixproc) { // create new updater
264
266
  putproc(compress(updateImports), function updateImports(local goback) {
265
267
  if importingMod !== exporterMod {
@@ -269,10 +271,10 @@ macro LiveExport() {
269
271
  try {
270
272
  call(fixproc, exporterMod) // let caller do any fixups
271
273
  } catch {
272
- if (true)
273
- debuglog("LiveImport fixup failed in", nsrights(), "exception:", exception)
274
+ debuglog("LiveImport fixup failed in", nsrights(), "exception:", exception)
275
+ } finally {
276
+ goback()
274
277
  }
275
- goback()
276
278
  })
277
279
  updateImports
278
280
  } (importingMod, eval(fixproc))
@@ -44,7 +44,7 @@ closure APIServer(options, local server, chroot) {
44
44
  server.guid = options.guid
45
45
  server.notifyq = []
46
46
  server.responseq = []
47
- server.instanceID = UUID()
47
+ server.instanceID = App.imp.getUUID("APIServer instanceID")
48
48
  options = merge({
49
49
  relpath: ""
50
50
  name: "NaanServer"
@@ -44,7 +44,7 @@ closure CurBot(options, local curbot) {
44
44
  curbot.guid = 1
45
45
  curbot.host = js.t // our magic Naan controller reference
46
46
 
47
- curbot.instanceID = UUID()
47
+ curbot.instanceID = App.imp.getUUID("CurBot(${options.name}) instanceID")
48
48
  curbot.sender = App.imp.register(curbot, {
49
49
  name: curbot.name
50
50
  type: curbot.type
@@ -266,7 +266,7 @@ closure NowBot(options, local nowbot, nodeops, subChannel, ptySpawned) {
266
266
  nowbot.thread.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1])
267
267
  nowbot.msgport = subChannel.port2
268
268
 
269
- nowbot.instanceID = UUID()
269
+ nowbot.instanceID = App.imp.getUUID("NowBot(${options.name}) instanceID")
270
270
  nowbot.sender = App.imp.register(nowbot, {
271
271
  name: options.name
272
272
  type: options.type
@@ -428,6 +428,16 @@ closure NowBot(options, local nowbot, nodeops, subChannel, ptySpawned) {
428
428
  nowbot.thread = false
429
429
  destroy()
430
430
  })
431
+
432
+ // close
433
+ //
434
+ // Close and remove our IMP resources, but leave the worker alone.
435
+ //
436
+ nowbot.close = function close() {
437
+ nowbot.impconn.destroy()
438
+ nowbot.impconn = false
439
+ App.imp.unregister(nowbot.instanceID)
440
+ }
431
441
 
432
442
  // destroy
433
443
  //
@@ -436,9 +446,7 @@ closure NowBot(options, local nowbot, nodeops, subChannel, ptySpawned) {
436
446
  nowbot.destroy = function destroy() {
437
447
  terminate()
438
448
  nowbot.thread = false
439
- nowbot.impconn.destroy()
440
- nowbot.impconn = false
441
- App.imp.unregister(nowbot.instanceID)
449
+ close()
442
450
  }
443
451
 
444
452
  // NaaN message
@@ -446,7 +454,7 @@ closure NowBot(options, local nowbot, nodeops, subChannel, ptySpawned) {
446
454
  // A message has been received from NaaN running on the worker thread.
447
455
  //
448
456
  nowbot.msgport.on("message", function (msg, local startmsg) {
449
- if msg.id == "naan-imp-node" {
457
+ if msg.id == "naan-imp-out" {
450
458
  nowbot.impconn.dispatch(new(msg.data))
451
459
  return
452
460
  } else if msg.id == "loaded" {
@@ -542,7 +550,7 @@ closure NobBot(options, local nobbot)
542
550
 
543
551
  nobbot.impconn = App.imp.responder(function(data) {
544
552
  nobbot.msgport.postMessage({ // send to worker
545
- id: "naan-imp-node" // scoped for NodeWorkerNub
553
+ id: "naan-imp-in" // scoped for NodeWorkerNub
546
554
  data: data
547
555
  })
548
556
  list(false, { sent: true })
@@ -13,106 +13,47 @@
13
13
  /*
14
14
  * WebSocket
15
15
  *
16
- * Create a WebSocket client connection object.
16
+ * Create a WebSocket client connection object, returning a tuple.
17
17
  *
18
18
  * Required:
19
- * host: <string> // host:port
19
+ * urlstr: <string>
20
20
  *
21
21
  * Options:
22
22
  * {
23
23
  * wss: <boolean> // true to use secure connection
24
+ * query: <string> // query string starting with ?
24
25
  * onopen: <proc>(e) // connection has opened
25
26
  * onmessage: <proc>(e, message) // message received
26
27
  * onerror: <proc>(e) // error occurred
27
28
  * onclose: <proc>(e) // connection has closed
28
- * query: <string> // query string starting with ? for insecure connections
29
- * querys: <string> // query string starting with ? for secure connections
30
29
  * }
31
30
  *
32
31
  */
33
32
 
34
- closure WebSocket(host, options, local wsock) {
35
- global(js)
36
- wsock = new(object, this)
37
- options = new(options) || { }
33
+ closure WebSocket(urlstr, options, local error, wslib, url, ws) {
34
+ `(error, wslib) = await(js.i("ws"))
35
+ if error
36
+ return (list(error))
38
37
 
39
- // connect
40
- //
41
- // Connect with the host if not already connected, returning a result tuple.
42
- //
43
- wsock.connect = closure connect(local error, ws, url, pending) {
44
- if wsock.ws
45
- return (list(false, { open: true }))
46
- `(error, ws) = await(js.i("ws"))
47
- if error
48
- return (list(error))
49
- if options.wss
50
- url = "wss://${host}${options.querys||''}"
51
- else
52
- url = "ws://${host}${options.query||''}"
53
- wsock.ws = xnew(ws.default, url)
54
- pending = new(nonce)
55
-
56
- // onopen
57
- // The WebSocket connection is now open.
58
- wsock.ws.onopen = closure onopen(e) {
59
- pending.signal(list(false, { ok: true }))
60
- options.onopen(e, e.target, e.type)
61
- }
62
-
63
- // onmessage
64
- // A message was received from the worker via WebSockets.
65
- wsock.ws.onmessage = closure onmessage(e, local message) {
66
- try {
67
- message = new(JSONparse(e.data))
68
- } catch {
69
- debuglog("can't decode incoming ws message:", exception)
70
- options.onerror(Error("can't decode incoming message:", exception))
71
- return
72
- }
73
- options.onmessage(e, message)
74
- }
75
-
76
- // onerror
77
- // An error occurred on the WebSocket connection.
78
- wsock.ws.onerror = closure onerror(e, local error) {
79
- error = Error("websocket failed", e)
80
- options.onerror(e, error)
81
- pending.signal(list(error)) // ensure connect completes
82
- }
83
-
84
- // onclose
85
- // The WebSocket connection was closed.
86
- wsock.ws.onclose = closure onclose(e) {
87
- options.onclose(e, e.code, e.reason)
88
- pending.signal(list(Error("connection closed"))) // ensure connect completes
89
- wsock.ws = false
90
- }
91
-
92
- pending.wait()
38
+ try {
39
+ url = xnew(js.g.URL, urlstr)
40
+ } catch {
41
+ return (list(Error("invalid URL ${urlstr}", exception)))
93
42
  }
94
-
95
- // send
96
- //
97
- // Send a message dictionary.
98
- //
99
- wsock.send = function send(message) {
100
- wsock.ws.send(JSONstringify(message))
101
- }
102
-
103
- // close
104
- //
105
- // Close the connection.
106
- //
107
- wsock.close = function close(reason) {
108
- if !reason
109
- reason = 1000 // normal close
110
- wsock.ws.close(reason)
111
- }
112
-
113
- // finis
114
-
115
- wsock
43
+ options = merge({
44
+ query: url.search || ''
45
+ wss: url.protocol == "https:"
46
+ }, options)
47
+ if options.wss
48
+ url = "wss://${url.host}${options.query}"
49
+ else
50
+ url = "ws://${url.host}${options.query}"
51
+ ws = xnew(wslib.default, url)
52
+ ws.onopen = options.onopen
53
+ ws.onmessage = options.onmessage
54
+ ws.onerror = options.onerror
55
+ ws.onclose = options.onclose
56
+ list(false, ws)
116
57
  };
117
58
 
118
59
 
@@ -304,18 +304,26 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
304
304
  //
305
305
  // Apply substitutions from readSubstitutionFile to string, returning a tuple of the resulting
306
306
  // text and the number of substitution patterns actually used from the supplied dictionary.
307
- //
308
- function applySubstitutions(text, subs, local subbed) {
309
- subbed = { }
310
- list(text.replace(RegExp("[$][^$\\s]+[$]", "g"), function(match, local subst) {
311
- subst = subs[match.slice(1,-1)]
312
- if subst {
313
- subbed[match] = true
314
- subst
315
- }
316
- else
317
- match
318
- }), length(subbed))
307
+ // Specify iterative to repeat the substitution on the result until no more substitutions occur.
308
+ //
309
+ function applySubstitutions(text, subs, iterative, local count, subbed) {
310
+ count = 0
311
+ loop {
312
+ subbed = { }
313
+ text = text.replace(RegExp("[$][^$\\s]+[$]", "g"), function(match, local subst) {
314
+ subst = subs[match.slice(1,-1)]
315
+ if subst {
316
+ subbed[match] = true
317
+ subst
318
+ }
319
+ else
320
+ match
321
+ })
322
+ count += length(subbed)
323
+ if length(subbed) == 0 || !iterative
324
+ break
325
+ }
326
+ list(text, count)
319
327
  }
320
328
 
321
329
  //
@@ -418,6 +426,16 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
418
426
  result
419
427
  }
420
428
 
429
+ //
430
+ // init1
431
+ //
432
+ // Initialize a build worker.
433
+ //
434
+
435
+ function init1() {
436
+ require("../common").LiveImport()
437
+ }
438
+
421
439
  //
422
440
  // parseAndPack1
423
441
  //
@@ -426,7 +444,8 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
426
444
  // autoquoted to itself, a symbol. This can be used to detect build time vs. runtime.
427
445
 
428
446
  function parseAndPack1(text, fpath, options, operation,
429
- local util, lib, penv, naansym, dlogsym, pmod, pcomp, ournn, output, textout, annerror, annotations, package)
447
+ local util, lib, modid, penv, naansym, dlogsym, pmod, pcomp, ournn,
448
+ output, textout, annotations, package)
430
449
  {
431
450
  //
432
451
  // textline
@@ -445,6 +464,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
445
464
  lib = Naan.module.modlist().Lib.exports
446
465
  if !util || !lib
447
466
  return (list("Build packager cannot access support functions"))
467
+ modid = JSpath.basename(JSpath.dirname(fpath))
448
468
  //
449
469
  // The following crazy rigmarole is about customizing the ParsEnvironment so we can glean
450
470
  // enough information to build a package without actually executing the code. To do this we
@@ -461,7 +481,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
461
481
  pcomp = { } // our component object
462
482
  ournn.module = {
463
483
  build: function(modname, compname, proc) {
464
- pmod.id = module.id
484
+ pmod.id = modid
465
485
  pmod.exports = { }
466
486
  pcomp.name = compname
467
487
  call(proc, pmod, pcomp) // obtain manifest
@@ -505,23 +525,39 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
505
525
  //
506
526
  textout = ""
507
527
  try {
508
- if !pcomp.name
509
- annerror = "Component required to build package"
510
- else if output.errors.length > 0
511
- annerror = output.errors[0]
512
- annotations = output.annotations
513
- if annotations {
514
- if annotations.length == 0 { // empty annotations list
515
- if annerror
516
- annotations = [{ // first error becomes annotation
517
- row: 0
518
- column: 0
519
- text: annerror
520
- type: "error"
521
- }]
522
- else
523
- annotations = false } // no errors or annotations
528
+ if output.annotations.length == 0 && output.errors.length == 0
529
+ && (options.naanpack || options.jspack) { // actually building a package
530
+ if !pcomp.name {
531
+ //
532
+ // Some components cannot use a build function because they are too early in the
533
+ // boot process, but we still need a component. We look for NaanBuildComponent
534
+ // to define a dictionary with filename, name and manifest fields. For example,
535
+ // see Util:Module.nlg
536
+ //
537
+ pcomp = penv.evalq(car(compress(NaanBuildComponent)))
538
+ if !pcomp || !fpath.endsWith("/".concat(pcomp.filename))
539
+ output.errors.push("Build cannot package without component, use NaanBuildComponent?")
540
+ }
541
+ if output.errors.length == 0 {
542
+ package = util.pkgSave(compress(pcomp.name), pcomp.manifest)
543
+ if string(package) {
544
+ output.errors.push("Build cannot generate package: ".concat(package))
545
+ package = false
546
+ }
547
+ }
524
548
  }
549
+
550
+ annotations = output.annotations
551
+ if annotations.length == 0 { // empty annotations list
552
+ if output.errors.length > 0
553
+ annotations.push({ // first error becomes annotation
554
+ row: 0
555
+ column: 0
556
+ text: output.errors[0]
557
+ type: "error"
558
+ })
559
+ else
560
+ annotations = false } // no errors or annotations
525
561
  if output.errors.length > 0 || output.text.length > 0 {
526
562
  textline("-".repeat(79))
527
563
  textline("While processing ", fpath)
@@ -540,22 +576,6 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
540
576
  //
541
577
  // Build the package, if not just doing syntax checking
542
578
  //
543
- if options.naanpack || options.jspack { // actually building a package?
544
- if !pcomp.name {
545
- //
546
- // Some components cannot use a build function because they are too early in the
547
- // boot process, but we still need a component. We look for NaanBuildComponent
548
- // to define a dictionary with filename, name and manifest fields. For example,
549
- // see Util:Module.nlg
550
- //
551
- pcomp = penv.evalq(car(compress(NaanBuildComponent)))
552
- if !pcomp || !fpath.endsWith("/".concat(pcomp.filename))
553
- return (list("Build cannot package without component in ".concat(fpath), false, annotations, textout))
554
- }
555
- package = util.pkgSave(compress(pcomp.name), pcomp.manifest)
556
- if string(package)
557
- return (list("Build cannot generate package for ".concat(fpath), false, annotations, textout))
558
- }
559
579
  } catch {
560
580
  if true
561
581
  return (list("Packaging exception in ".concat(fpath, ": ", tostring(exception)), false, annotations, textout))
@@ -585,7 +605,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
585
605
  if !build.parallel
586
606
  build.parallel = ExecutorParallel(track, { // create worker pool
587
607
  basename: "build"
588
- initeval: `(false, (parseAndPack1))
608
+ initeval: list(quote(init1()), `(init1, parseAndPack1))
589
609
  })
590
610
  result = build.parallel.rpc(parseAndPack1, list(text, fpath, options, operation))
591
611
  if !result.0 {
@@ -1044,9 +1064,9 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1044
1064
  }
1045
1065
  else if ((`(error, content) = fs.readFile(entry.readpath, readOptions)) && !error) {
1046
1066
  if entry.license
1047
- `(content) = applySubstitutions(content, licFileDefs)
1067
+ `(content) = applySubstitutions(content, licFileDefs, true)
1048
1068
  if entry.version
1049
- `(content) = applySubstitutions(content, verFileDefs)
1069
+ `(content) = applySubstitutions(content, verFileDefs, true)
1050
1070
  if entry.naanpack
1051
1071
  `(error, content) = parseAndPack(content, entry.readpath, { naanpack: true })
1052
1072
  else if entry.jspack
@@ -1251,7 +1271,8 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1251
1271
  //
1252
1272
  // Run the current build product by spawning a new worker.
1253
1273
  //
1254
- build.runSpawn = function runSpawn(local error, maintext, executor, vpath, site, dirpath) {
1274
+ build.runSpawn = function runSpawn(stage,
1275
+ local error, maintext, executor, vpath, site, dirpath) {
1255
1276
  `(error, maintext) = fs.readFile(JSpath.join(build.destpath, build.runway.main))
1256
1277
  if error
1257
1278
  error = Error("Builder: cannot read main file", error)
@@ -1291,13 +1312,12 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1291
1312
  //
1292
1313
  // Run the current build product by opening a new window.
1293
1314
  //
1294
- build.runWindow = function runWindow(
1315
+ build.runWindow = function runWindow(stage,
1295
1316
  local title, main, url, features, vpath, error, window, site) {
1296
1317
  if !js.w
1297
1318
  return (list(Error('Run method "window" requires browser host')))
1298
1319
  if !build.runway."v-site"
1299
1320
  return (list(Error('Run method "window" requires v-site specification')))
1300
- title = rules.name.concat("-", build.buildnum)
1301
1321
  main = build.runway.main
1302
1322
  if !main
1303
1323
  main = "index.html"
@@ -1313,6 +1333,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1313
1333
  if error
1314
1334
  return (list(Error("Builder: cannot create virtual site", error)))
1315
1335
  sleep(1) // needed on Windows or it won't open, fiik why
1336
+ title = rules.name.concat("-", stage, "-", operation)
1316
1337
  window = js.w.open(url, title, features)
1317
1338
  list(false, { ok: true }) // we don't know the executor
1318
1339
  }
@@ -1323,13 +1344,13 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1323
1344
  // Run the current build product, which would be after the make call. Returns a standard result
1324
1345
  // tuple.
1325
1346
  //
1326
- build.run = function run() {
1347
+ build.run = function run(stage) {
1327
1348
  if build.runway.open
1328
1349
  fs.shellOpen(JSpath.join(build.destpath, build.runway.open), build.runway.args)
1329
1350
  else if build.runway.spawn
1330
- build.runSpawn()
1351
+ build.runSpawn(stage)
1331
1352
  else if build.runway.window
1332
- build.runWindow()
1353
+ build.runWindow(stage)
1333
1354
  else
1334
1355
  list(false, { ok: true })
1335
1356
  }
@@ -296,7 +296,7 @@ closure projConnector(projman, projtype, local connector, watch) {
296
296
  *
297
297
  * Nide.proj/
298
298
  * nide_cliser.cfg - JSON definition of the project (see below)
299
- * NaanIDE_version.txt - [default] substitution file defining 1.7.0+1 etc.
299
+ * NaanIDE_version.txt - [default] substitution file defining 1.7.2+1 etc.
300
300
  * NaanIDE_version_builds.txt - [default] contains current build number as decimal string
301
301
  * build/ - [optional] default build output location
302
302
  * NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
@@ -646,7 +646,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
646
646
  if builder.refresh()
647
647
  buildonly = !buildonly // reverse if refresh
648
648
  if !buildonly
649
- `(error) = builder.run()
649
+ `(error) = builder.run(stage)
650
650
  }
651
651
  builder.destroy()
652
652
  procon.builder = false