From b3d6540f1381fc06743ffb24f76513a2e7d16612 Mon Sep 17 00:00:00 2001 From: "Darryl L. Miles" Date: Thu, 23 Jul 2026 13:55:20 +0000 Subject: [PATCH] build: isolate build-info defines into utils/buildinfo.c (ccache-friendly) MAGIC_VERSION / MAGIC_REVISION / MAGIC_COMMIT / MAGIC_BUILDDATE were on the global DFLAGS, so every object's compile command line carried them. Because the command line is part of ccache/sccache's hash key and MAGIC_BUILDDATE changes every second, that meant a cache miss on every unit on every build; it also smeared the baked-in date/commit across "whenever each file last recompiled". Move the four values into a single translation unit: * utils/magic_buildinfo.h -- extern MagicVersion/MagicRevision/MagicCommit/ MagicCompileTime (MagicCommit is new; the other three moved here from utils/magic.h, which now includes this header). * utils/buildinfo.c -- the ONLY unit compiled with the version defines (target-specific `buildinfo.o: DFLAGS += ${DFLAGS_MAGICVERSION}` in utils/Makefile.in); it defines the four globals. ${DFLAGS_MAGICVERSION} is removed from the global DFLAGS/DFLAGS_NOSTUB. Consumers updated to read the runtime symbol instead of the compile-time macro: * magicTop.c / tclmagic.c no longer define the globals (the MAGIC_WRAPPER duplicate-symbol guard is gone -- buildinfo.o owns them for every variant, including WASM, which links both mains). * Tcl_PkgProvide/PkgRequire in router/ext2spice/lef/ext2sim/plot/tclmagic now pass MagicVersion. * extflat: EFVersion (a static-initialized copy of MAGIC_VERSION) is dropped; EFread.c compares the .ext version against MagicVersion directly -- the same value ExtCell.c already *writes* into .ext files, so read and write are now consistent. Verified: only buildinfo.o carries -DMAGIC_* (hash/DBio/windCmdSZ carry none); Tcl (375 files) and --without-tcl builds rc=0, version/commit/date embedded in tclmagic.so, magicTop.o no longer defines MagicVersion; in-tree source clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- ext2sim/ext2sim.c | 4 ++-- ext2spice/ext2spice.c | 4 ++-- extflat/EFargs.c | 1 - extflat/EFread.c | 2 +- extflat/extflat.h | 1 - lef/tcllef.c | 4 ++-- magic/magicTop.c | 20 ++++++-------------- plot/tclplot.c | 4 ++-- router/tclroute.c | 4 ++-- scripts/defs.mak.in | 4 ++-- tcltk/tclmagic.c | 11 ++++------- utils/Makefile.in | 8 +++++++- utils/buildinfo.c | 31 +++++++++++++++++++++++++++++++ utils/magic.h | 7 ++++--- utils/magic_buildinfo.h | 24 ++++++++++++++++++++++++ 15 files changed, 89 insertions(+), 40 deletions(-) create mode 100644 utils/buildinfo.c create mode 100644 utils/magic_buildinfo.h diff --git a/ext2sim/ext2sim.c b/ext2sim/ext2sim.c index d5b96c5a..985c6ddd 100644 --- a/ext2sim/ext2sim.c +++ b/ext2sim/ext2sim.c @@ -208,7 +208,7 @@ Exttosim_Init( { /* Sanity checks! */ if (interp == NULL) return TCL_ERROR; - if (Tcl_PkgRequire(interp, "Tclmagic", MAGIC_VERSION, 0) == NULL) + if (Tcl_PkgRequire(interp, "Tclmagic", MagicVersion, 0) == NULL) return TCL_ERROR; if (Tcl_InitStubs(interp, Tclmagic_InitStubsVersion, 0) == NULL) return TCL_ERROR; @@ -225,7 +225,7 @@ Exttosim_Init( if (WindReplaceCommand(DBWclientID, "ext2sim", CmdExtToSim) < 0) return TCL_ERROR; - Tcl_PkgProvide(interp, "Exttosim", MAGIC_VERSION); + Tcl_PkgProvide(interp, "Exttosim", MagicVersion); return TCL_OK; } diff --git a/ext2spice/ext2spice.c b/ext2spice/ext2spice.c index d048215f..fd128e52 100644 --- a/ext2spice/ext2spice.c +++ b/ext2spice/ext2spice.c @@ -206,7 +206,7 @@ Exttospice_Init( { /* Sanity checks! */ if (interp == NULL) return TCL_ERROR; - if (Tcl_PkgRequire(interp, "Tclmagic", MAGIC_VERSION, 0) == NULL) + if (Tcl_PkgRequire(interp, "Tclmagic", MagicVersion, 0) == NULL) return TCL_ERROR; if (Tcl_InitStubs(interp, Tclmagic_InitStubsVersion, 0) == NULL) return TCL_ERROR; @@ -223,7 +223,7 @@ Exttospice_Init( if (WindReplaceCommand(DBWclientID, "ext2spice", CmdExtToSpice) < 0) return TCL_ERROR; - Tcl_PkgProvide(interp, "Exttospice", MAGIC_VERSION); + Tcl_PkgProvide(interp, "Exttospice", MagicVersion); return TCL_OK; } #endif /* EXT2SPICE_AUTO */ diff --git a/extflat/EFargs.c b/extflat/EFargs.c index bcdb6c66..e0e8743e 100644 --- a/extflat/EFargs.c +++ b/extflat/EFargs.c @@ -52,7 +52,6 @@ char *EFArgTech = NULL; /* -T: Tech specified on command line */ /* Misc globals */ float EFScale = 0.0; /* Uninitialized scale factor */ -char *EFVersion = MAGIC_VERSION;/* Version number of .ext format we read */ char *EFLibPath = NULL; /* Library search path for .ext files */ char *EFTech = NULL; char *EFStyle = NULL; /* Start with no extraction style */ diff --git a/extflat/EFread.c b/extflat/EFread.c index fbd77096..8bdc5b58 100644 --- a/extflat/EFread.c +++ b/extflat/EFread.c @@ -575,7 +575,7 @@ resistChanged: /* version version-number */ case VERSION: - if (strcmp(argv[1], EFVersion) != 0) + if (strcmp(argv[1], MagicVersion) != 0) { efReadError( "Cell was extracted using version %s of the extractor.\n", argv[1]); diff --git a/extflat/extflat.h b/extflat/extflat.h index b85b858c..16c2f6de 100644 --- a/extflat/extflat.h +++ b/extflat/extflat.h @@ -32,7 +32,6 @@ extern char *EFTech; /* Technology of extracted circuit */ extern char *EFStyle; /* Extraction style of extracted circuit */ extern char *EFSearchPath; /* Path to search for .ext files */ extern char *EFLibPath; /* Library search path */ -extern char *EFVersion; /* Version of extractor we work with */ extern char *EFArgTech; /* Tech file given as command line argument */ extern bool EFCompat; /* Subtrate backwards-compatibility mode */ diff --git a/lef/tcllef.c b/lef/tcllef.c index 87534c64..3fe42d63 100644 --- a/lef/tcllef.c +++ b/lef/tcllef.c @@ -41,7 +41,7 @@ Magiclef_Init( { /* Sanity checks! */ if (interp == NULL) return TCL_ERROR; - if (Tcl_PkgRequire(interp, "Tclmagic", MAGIC_VERSION, 0) == NULL) + if (Tcl_PkgRequire(interp, "Tclmagic", MagicVersion, 0) == NULL) return TCL_ERROR; if (Tcl_InitStubs(interp, Tclmagic_InitStubsVersion, 0) == NULL) return TCL_ERROR; @@ -57,7 +57,7 @@ Magiclef_Init( if (WindReplaceCommand(DBWclientID, "def", CmdLef) < 0) return TCL_ERROR; - Tcl_PkgProvide(interp, "MagicLEF", MAGIC_VERSION); + Tcl_PkgProvide(interp, "MagicLEF", MagicVersion); return TCL_OK; } diff --git a/magic/magicTop.c b/magic/magicTop.c index 8573f98a..1a020635 100644 --- a/magic/magicTop.c +++ b/magic/magicTop.c @@ -55,18 +55,10 @@ main(int argc, char *argv[]) exit(0); } -/* String containing the version number of magic. Don't change the string - * here, nor its format. It is updated by the Makefile in this directory. - * - * The version string originates at the top of scripts/config. - * - * Under MAGIC_WRAPPER (Tcl-embedded builds), tclmagic.c owns these globals; - * defining them here as well would produce duplicate-symbol errors when both - * objects end up in the same binary (as in the WASM build). +/* MagicVersion / MagicRevision / MagicCompileTime are defined once in + * utils/buildinfo.c -- the single unit compiled with the version defines -- + * and declared in utils/magic_buildinfo.h. They used to be defined here (and, + * for the Tcl build, in tclmagic.c under a MAGIC_WRAPPER guard); consolidating + * them removes the duplicate-symbol hazard and keeps the volatile build-date + * define off every compile command line. */ - -#ifndef MAGIC_WRAPPER -char *MagicVersion = MAGIC_VERSION; -char *MagicRevision = MAGIC_REVISION; -char *MagicCompileTime = MAGIC_BUILDDATE; -#endif diff --git a/plot/tclplot.c b/plot/tclplot.c index a590f4b5..a261c70a 100644 --- a/plot/tclplot.c +++ b/plot/tclplot.c @@ -44,7 +44,7 @@ Tclplot_Init(interp) /* Sanity checks! */ if (interp == NULL) return TCL_ERROR; - if (Tcl_PkgRequire(interp, "Tclmagic", MAGIC_VERSION, 0) == NULL) + if (Tcl_PkgRequire(interp, "Tclmagic", MagicVersion, 0) == NULL) return TCL_ERROR; if (Tcl_InitStubs(interp, Tclmagic_InitStubsVersion, 0) == NULL) return TCL_ERROR; @@ -67,7 +67,7 @@ Tclplot_Init(interp) invplot = TechSectionGetMask("plot", NULL); if (!TechLoad(NULL, invplot)) return TCL_ERROR; - Tcl_PkgProvide(interp, "Plot", MAGIC_VERSION); + Tcl_PkgProvide(interp, "Plot", MagicVersion); return TCL_OK; } diff --git a/router/tclroute.c b/router/tclroute.c index 3426267b..ad5709b3 100644 --- a/router/tclroute.c +++ b/router/tclroute.c @@ -51,7 +51,7 @@ Tclroute_Init(interp) /* Sanity checks! */ if (interp == NULL) return TCL_ERROR; - if (Tcl_PkgRequire(interp, "Tclmagic", MAGIC_VERSION, 0) == NULL) + if (Tcl_PkgRequire(interp, "Tclmagic", MagicVersion, 0) == NULL) return TCL_ERROR; if (Tcl_InitStubs(interp, Tclmagic_InitStubsVersion, 0) == NULL) return TCL_ERROR; @@ -88,7 +88,7 @@ Tclroute_Init(interp) invsec &= TechSectionGetMask("router", NULL); if (!TechLoad(NULL, invsec)) return TCL_ERROR; - Tcl_PkgProvide(interp, "Route", MAGIC_VERSION); + Tcl_PkgProvide(interp, "Route", MagicVersion); return TCL_OK; } diff --git a/scripts/defs.mak.in b/scripts/defs.mak.in index f8a3adcc..c186c80c 100755 --- a/scripts/defs.mak.in +++ b/scripts/defs.mak.in @@ -163,9 +163,9 @@ ifeq (@MAKE_READLINE@,1) CPPFLAGS += -I${MAGICSRC}/readline endif DFLAGS_MAGICVERSION = -DMAGIC_VERSION=\"${MAGIC_VERSION}\" -DMAGIC_REVISION=\"${MAGIC_REVISION}\" -DMAGIC_COMMIT=\"${MAGIC_COMMIT}\" "-DMAGIC_BUILDDATE=\"${MAGIC_BUILDDATE}\"" -DFLAGS = @extra_defs@ @stub_defs@ @DEFS@ ${DFLAGS_MAGICVERSION} -DGCORE=\"@GCORE@\" +DFLAGS = @extra_defs@ @stub_defs@ @DEFS@ -DGCORE=\"@GCORE@\" DFLAGS += -DSHDLIB_EXT=\"@SHDLIB_EXT@\" @NDEBUG_defs@ @DEBUG_defs@ ${FEATURE_FLAGS} -DFLAGS_NOSTUB = @extra_defs@ @DEFS@ ${DFLAGS_MAGICVERSION} -DGCORE=\"@GCORE@\" +DFLAGS_NOSTUB = @extra_defs@ @DEFS@ -DGCORE=\"@GCORE@\" DFLAGS_NOSTUB += -DSHDLIB_EXT=\"@SHDLIB_EXT@\" @NDEBUG_defs@ @DEBUG_defs@ ${FEATURE_FLAGS} CFLAGS = @CFLAGS@ @SHLIB_CFLAGS@ @INC_SPECS@ diff --git a/tcltk/tclmagic.c b/tcltk/tclmagic.c index 8e8db1c2..1c007ce1 100644 --- a/tcltk/tclmagic.c +++ b/tcltk/tclmagic.c @@ -48,14 +48,11 @@ #include "dbwind/dbwind.h" /* - * String containing the version number of magic. Don't change the string - * here, nor its format. It is updated by the Makefile in this directory. + * MagicVersion / MagicRevision / MagicCompileTime are defined once in + * utils/buildinfo.c and declared in utils/magic_buildinfo.h (included via + * utils/magic.h). They no longer need a MAGIC_WRAPPER-guarded definition here. */ -char *MagicVersion = MAGIC_VERSION; -char *MagicRevision = MAGIC_REVISION; -char *MagicCompileTime = MAGIC_BUILDDATE; - #if TCL_MAJOR_VERSION < 9 const char *Tclmagic_InitStubsVersion = "8.5"; #else @@ -1546,7 +1543,7 @@ Tclmagic_Init(interp) Tcl_SetVar(interp, "CAD_ROOT", cadroot, TCL_GLOBAL_ONLY); } - Tcl_PkgProvide(interp, "Tclmagic", MAGIC_VERSION); + Tcl_PkgProvide(interp, "Tclmagic", MagicVersion); return TCL_OK; } diff --git a/utils/Makefile.in b/utils/Makefile.in index 18a6c232..d767d9e3 100644 --- a/utils/Makefile.in +++ b/utils/Makefile.in @@ -9,7 +9,7 @@ srcdir = @srcdir@ VPATH = @srcdir@ MAGICDIR = @top_builddir@ LIB_SRCS = LIBdbio.c LIBmain.c LIBtextio.c -SRCS = args.c child.c dqueue.c finddisp.c flock.c flsbuf.c fraction.c \ +SRCS = args.c buildinfo.c child.c dqueue.c finddisp.c flock.c flsbuf.c fraction.c \ geometry.c getrect.c hash.c heap.c ihash.c list.c lookup.c \ lookupany.c lookupfull.c macros.c main.c malloc.c match.c \ maxrect.c netlist.c niceabort.c parser.c path.c pathvisit.c \ @@ -23,3 +23,9 @@ DFLAGS += ${GR_DFLAGS} CFLAGS += ${GR_CFLAGS} include ${MAGICSRC}/rules.mak + +# buildinfo.c is the ONLY unit that consumes the version defines; they are no +# longer on the global DFLAGS (that kept the ever-changing MAGIC_BUILDDATE off +# every other compile command line, so ccache/sccache keys stay stable). Apply +# them just here, target-specifically. +buildinfo.o: DFLAGS += ${DFLAGS_MAGICVERSION} diff --git a/utils/buildinfo.c b/utils/buildinfo.c new file mode 100644 index 00000000..d6bf4f79 --- /dev/null +++ b/utils/buildinfo.c @@ -0,0 +1,31 @@ +/* + * buildinfo.c -- + * + * The single translation unit that captures the build-information defines. + * + * This is the ONLY file compiled with -DMAGIC_VERSION / -DMAGIC_REVISION / + * -DMAGIC_COMMIT / -DMAGIC_BUILDDATE (see the buildinfo.o recipe in + * utils/Makefile.in). Isolating them here keeps those defines -- especially the + * ever-changing MAGIC_BUILDDATE -- off every other compile command line, so + * ccache/sccache keys stay stable and the version/date/commit baked into the + * binary are consistent rather than smeared across whenever each object last + * recompiled. + * + * Consumers include "utils/magic_buildinfo.h" and read these externs. + */ + +#include "utils/magic_buildinfo.h" + +char *MagicVersion = MAGIC_VERSION; +char *MagicRevision = MAGIC_REVISION; +char *MagicCommit = MAGIC_COMMIT; + +/* + * MAGIC_BUILDDATE may be disabled at configure time (--disable-magic-builddate) + * for reproducible builds; when it is, MagicCompileTime reports the empty string. + */ +#ifdef MAGIC_NO_BUILDDATE +char *MagicCompileTime = ""; +#else +char *MagicCompileTime = MAGIC_BUILDDATE; +#endif diff --git a/utils/magic.h b/utils/magic.h index cd9a9b18..3edb7b12 100644 --- a/utils/magic.h +++ b/utils/magic.h @@ -145,9 +145,10 @@ extern char *SysLibPath; /* Path for finding system /* ------------ Globally-used strings. -------------------------------- */ -extern char *MagicVersion; -extern char *MagicRevision; -extern char *MagicCompileTime; +/* Build-info globals (MagicVersion / MagicRevision / MagicCommit / + * MagicCompileTime) are defined in utils/buildinfo.c -- the one unit compiled + * with the version defines -- and declared here: */ +#include "utils/magic_buildinfo.h" extern char AbortMessage[]; /* ------------ zlib (compression) support -------------------------------- */ diff --git a/utils/magic_buildinfo.h b/utils/magic_buildinfo.h new file mode 100644 index 00000000..9aeb75fb --- /dev/null +++ b/utils/magic_buildinfo.h @@ -0,0 +1,24 @@ +/* + * magic_buildinfo.h -- + * + * Public declarations for the build-information globals: magic's version, + * revision, git commit, and compile date/time. + * + * These are the ONLY symbols initialized from the -DMAGIC_VERSION / + * -DMAGIC_REVISION / -DMAGIC_COMMIT / -DMAGIC_BUILDDATE command-line defines. + * They are defined in exactly one translation unit, utils/buildinfo.c, which is + * the only unit compiled with those defines. Every other unit reads the values + * through these externs, so the volatile build-date/commit values stay off every + * other compile command line (which keeps ccache/sccache keys stable and the + * baked-in date/commit consistent across the whole binary). + */ + +#ifndef _MAGIC_BUILDINFO_H +#define _MAGIC_BUILDINFO_H + +extern char *MagicVersion; /* e.g. "8.3" (major.minor) */ +extern char *MagicRevision; /* e.g. "670" (patch/revision) */ +extern char *MagicCommit; /* git commit SHA, or "" if unavailable */ +extern char *MagicCompileTime; /* build date/time, or "" if disabled */ + +#endif /* _MAGIC_BUILDINFO_H */