nomen-lang 0.6.0 → 0.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nomen-lang",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "The CLI for the Nomen programming language.",
5
5
  "keywords": [],
6
6
  "license": "ISC",
package/src/test.ts CHANGED
@@ -3,7 +3,7 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
6
- import build from "../../src/build.ts";
6
+ import build, { default_platform } from "../../src/build.ts";
7
7
  import join from "../../src/join.ts";
8
8
  import { get_library } from "../../src/lib.ts";
9
9
  import parse from "../../src/parse.ts";
@@ -40,6 +40,12 @@ export interface TestFileResult {
40
40
  // Set when the compiled binary crashed before finishing (e.g. a segfault
41
41
  // in a test). We still surface whatever records it managed to emit.
42
42
  crashed?: string;
43
+ /**
44
+ * The pipeline stage that produced `crashed`, so the report can name it
45
+ * accurately — a binary that BUILT fine and then segfaulted before
46
+ * emitting any record is not a build failure (see report_file).
47
+ */
48
+ phase?: "parse" | "build" | "setup" | "link" | "run";
43
49
  ms: number;
44
50
  }
45
51
 
@@ -289,6 +295,7 @@ export function run_test_file(
289
295
  const parsed = parse(source, library, resolved);
290
296
  if (parsed.errors.length) {
291
297
  result.ok = false;
298
+ result.phase = "parse";
292
299
  result.crashed = parsed.errors
293
300
  .map((e) => `${e.message} (${e.line ?? "?"}:${e.column ?? "?"})`)
294
301
  .join("\n");
@@ -302,6 +309,7 @@ export function run_test_file(
302
309
  });
303
310
  if (buildResult.errors && buildResult.errors.length) {
304
311
  result.ok = false;
312
+ result.phase = "build";
305
313
  result.crashed = buildResult.errors
306
314
  .map((e) => `${e.message} (${e.line ?? "?"}:${e.column ?? "?"})`)
307
315
  .join("\n");
@@ -321,6 +329,7 @@ export function run_test_file(
321
329
  : find_audit_runtime(path.dirname(resolved));
322
330
  if (!runtime_src || !fs.existsSync(runtime_src)) {
323
331
  result.ok = false;
332
+ result.phase = "setup";
324
333
  result.crashed =
325
334
  "Audit enabled but audit_runtime.c was not found. Pass --audit-runtime <path>.";
326
335
  result.ms = performance.now() - start;
@@ -329,7 +338,12 @@ export function run_test_file(
329
338
  audit_obj = path.join(buildDir, "audit_runtime.o");
330
339
  execFileSync("clang", ["-c", runtime_src, "-o", audit_obj]);
331
340
  }
332
- const ext = arch === "aarch64" ? ".s" : ".c";
341
+ const platform = default_platform();
342
+ // Match the run/build command's emission: ObjC-bearing sources (GUI code
343
+ // pulled in through the library graph) must land in a `.m` TU on Apple
344
+ // platforms — the ObjC framework headers are Objective-C, and including
345
+ // them from a plain `.c` TU fails with `@class NSString` syntax errors.
346
+ const ext = arch === "aarch64" ? ".s" : platform === "macos" || platform === "ios" ? ".m" : ".c";
333
347
  const codefile = path.join(buildDir, path.basename(entry_path, ".nm") + ext);
334
348
  const outfile = path.join(buildDir, path.basename(entry_path, ".nm"));
335
349
  // The C backend's generated source does `#include "main.h"`, so write the
@@ -337,10 +351,23 @@ export function run_test_file(
337
351
  fs.writeFileSync(path.join(buildDir, "main.h"), buildResult.headers ?? "");
338
352
  fs.writeFileSync(codefile, buildResult.code);
339
353
 
340
- // Link the harness binary. The harness uses only Console/Time (printf,
341
- // clock_gettime), so no platform frameworks are required.
354
+ // Link the harness binary. Frameworks are passed on Apple platforms for
355
+ // the same reason as the run/build command: the joined source may reference
356
+ // ObjC runtime symbols even when the test body doesn't, and unused
357
+ // frameworks cost nothing at link time.
342
358
  const link_args = ["-o", outfile, codefile];
343
359
  if (audit_obj) link_args.push(audit_obj);
360
+ if (platform === "macos" || platform === "ios") {
361
+ link_args.push(
362
+ "-framework",
363
+ "CoreGraphics",
364
+ "-framework",
365
+ "Foundation",
366
+ "-framework",
367
+ "AppKit",
368
+ "-lobjc",
369
+ );
370
+ }
344
371
  try {
345
372
  execFileSync("clang", link_args, {
346
373
  encoding: "utf8",
@@ -349,6 +376,7 @@ export function run_test_file(
349
376
  } catch (err: any) {
350
377
  const stderr = err.stderr ? err.stderr.toString() : (err.message ?? "");
351
378
  result.ok = false;
379
+ result.phase = "link";
352
380
  result.crashed = `link failed: ${stderr.trim() || "clang error"}`;
353
381
  result.ms = performance.now() - start;
354
382
  return result;
@@ -381,6 +409,7 @@ export function run_test_file(
381
409
  result.benches = records.benches;
382
410
  result.other = records.other;
383
411
  result.crashed = crashed;
412
+ if (crashed) result.phase = "run";
384
413
  // A file "passes" when it reported no failed asserts and didn't crash.
385
414
  const failed = result.fails.length > 0 || result.tests.some((t) => t.failed > 0);
386
415
  result.ok = !failed && crashed === undefined;
@@ -417,7 +446,18 @@ const C = {
417
446
  export function report_file(result: TestFileResult): void {
418
447
  const rel = path.relative(process.cwd(), result.file);
419
448
  if (result.crashed && result.tests.length === 0 && result.fails.length === 0) {
420
- console.log(` ${C.red("✗")} ${rel} ${C.red("(failed to build)")}`);
449
+ // Name the stage that actually failed. A binary that linked fine and
450
+ // then died before emitting any record (e.g. SIGSEGV) is NOT a build
451
+ // failure — conflating the two sent debugging to the wrong layer.
452
+ const label =
453
+ result.phase === "run"
454
+ ? "(crashed before records)"
455
+ : result.phase === "link"
456
+ ? "(link failed)"
457
+ : result.phase === "setup"
458
+ ? "(setup failed)"
459
+ : "(failed to build)";
460
+ console.log(` ${C.red("✗")} ${rel} ${C.red(label)}`);
421
461
  console.log(C.red(result.crashed));
422
462
  return;
423
463
  }