tcltk: honor CAD_ROOT for the Tcl runtime dir, not just the baked TCL_DIR

magicexec.c, magicdnull.c and tclmagic.c hardwired the compile-time TCL_DIR
(the install location) when locating magic.tcl (tcl_rcFileName) and the Tcl
auto_path.  So even with CAD_ROOT set -- the documented relocation knob, already
honored by the sys-file search and by tcltk/magic.sh's
TCL_MAG_DIR=${CAD_ROOT}/magic/tcl -- the C launchers still looked in the baked
install path, which is why magic could not source its startup from a relocated
install or a build tree.

Add tcltk/tcldir.h with a MagicTclDir() helper: $CAD_ROOT/magic/tcl when CAD_ROOT
is set and non-empty, else the compile-time TCL_DIR.  Use it in all three sites.
This makes the C side consistent with the shell wrappers and with how magic.tcl
itself already derives its own directory (from `info script`).

Verified: default (no CAD_ROOT) resolves to TCL_DIR as before (build rc=0); with
CAD_ROOT pointed at a staged build tree, `magicdnull -dnull` sources magic.tcl
from there and runs (tech load scmos -> 13 planes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Darryl L. Miles 2026-07-23 22:00:55 +00:00 committed by R. Timothy Edwards
parent 293c111bc3
commit aab3361858
4 changed files with 91 additions and 3 deletions

View File

@ -11,6 +11,8 @@
#include <tcl.h>
#include "tcltk/tcldir.h"
/*----------------------------------------------------------------------*/
/* Application initiation. This is exactly like the AppInit routine */
/* for "wish", minus the cruft, but with "tcl_rcFileName" set to */
@ -29,7 +31,13 @@ magic_AppInit(interp)
/* This is where we replace the home ".tclshrc" file with */
/* magic's startup script. */
Tcl_SetVar(interp, "tcl_rcFileName", TCL_DIR "/magic.tcl", TCL_GLOBAL_ONLY);
{
char rc[PATH_MAX];
size_t rlen;
rlen = MagicTclDir(rc, sizeof(rc), "magic.tcl");
assert(rlen < sizeof(rc));
Tcl_SetVar(interp, "tcl_rcFileName", rc, TCL_GLOBAL_ONLY);
}
/* Additional variable can be used to tell if magic is in batch mode */
Tcl_SetVar(interp, "batch_mode", "true", TCL_GLOBAL_ONLY);

View File

@ -37,6 +37,8 @@
#include <tk.h>
#include <tcl.h>
#include "tcltk/tcldir.h"
/*----------------------------------------------------------------------*/
/* Application initiation. This is exactly like the AppInit routine */
/* for "wish", minus the cruft, but with "tcl_rcFileName" set to */
@ -59,7 +61,13 @@ magic_AppInit(interp)
/* This is where we replace the home ".wishrc" file with */
/* magic's startup script. */
Tcl_SetVar(interp, "tcl_rcFileName", TCL_DIR "/magic.tcl", TCL_GLOBAL_ONLY);
{
char rc[PATH_MAX];
size_t rlen;
rlen = MagicTclDir(rc, sizeof(rc), "magic.tcl");
assert(rlen < sizeof(rc));
Tcl_SetVar(interp, "tcl_rcFileName", rc, TCL_GLOBAL_ONLY);
}
return TCL_OK;
}

63
tcltk/tcldir.h Normal file
View File

@ -0,0 +1,63 @@
/*
* tcldir.h --
*
* Resolve magic's Tcl-runtime directory (where magic.tcl, tclmagic.so, and the
* Tcl auto_path packages live) at *run time* instead of hardwiring the
* compile-time install path.
*
* If the environment variable CAD_ROOT is set and non-empty, use
* $CAD_ROOT/magic/tcl -- so magic runs from a relocated install, or straight out
* of a build tree (via a staged CAD_ROOT), the same way the sys files already
* follow CAD_ROOT and the tcltk/magic.sh wrapper already computes
* TCL_MAG_DIR=${CAD_ROOT}/magic/tcl. Otherwise fall back to the compile-time
* TCL_DIR (the default install location).
*
* Must be included in a translation unit compiled with -DTCL_DIR.
*/
#ifndef _MAGIC_TCLDIR_H
#define _MAGIC_TCLDIR_H
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>
/* Platform path-length limit. <limits.h> declares PATH_MAX when the system has
* a fixed limit (4096 on Linux); provide a safe fallback when it does not. */
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
/*
* Write magic's Tcl-runtime directory into buf (of the given size), with the
* optional suffix appended as a path component -- the '/' directory separator is
* supplied automatically, so pass "magic.tcl", not "/magic.tcl" (a NULL or empty
* suffix appends nothing). A caller that wants "<dir>/magic.tcl" thus gets it in
* one call instead of a second snprintf. Return the number of characters written
* -- snprintf semantics, as a size_t -- or 0 on a (very unlikely) snprintf
* encoding error. The assert catches truncation: snprintf returns the length it
* *would* have written, so a return of >= size means the path did not fit.
* Callers should likewise assert the returned length < their buffer size.
*/
static inline size_t
MagicTclDir(char *buf, size_t size, const char *suffix)
{
const char *cad = getenv("CAD_ROOT");
const char *sep;
int n;
if (suffix == NULL) suffix = "";
sep = (*suffix != '\0') ? "/" : ""; /* imply the '/' separator */
if (cad != NULL && *cad != '\0')
n = snprintf(buf, size, "%s/magic/tcl%s%s", cad, sep, suffix);
else
n = snprintf(buf, size, "%s%s%s", TCL_DIR, sep, suffix);
if (n < 0) n = 0; /* coerce encoding error to 0, assert below */
assert((size_t)n < size);
return (size_t)n;
}
#endif /* _MAGIC_TCLDIR_H */

View File

@ -30,6 +30,7 @@
#endif
#include "tcltk/tclmagic.h"
#include "tcltk/tcldir.h"
#include "utils/main.h"
#include "utils/magic.h"
#include "utils/geometry.h"
@ -1528,7 +1529,15 @@ Tclmagic_Init(interp)
/* Add the magic TCL directory to the Tcl library search path */
Tcl_Eval(interp, "lappend auto_path " TCL_DIR );
{
char dir[PATH_MAX], cmd[PATH_MAX];
size_t dlen, clen;
dlen = MagicTclDir(dir, sizeof(dir), NULL);
assert(dlen < sizeof(dir));
clen = snprintf(cmd, sizeof(cmd), "lappend auto_path {%s}", dir);
assert(clen < sizeof(cmd));
Tcl_Eval(interp, cmd);
}
/* Get $CAD_ROOT from a Tcl variable, if it exists, and if not, then */
/* set CAD_ROOT from the environment variable of the same name, if */