diff --git a/bfd/Makefile.in b/bfd/Makefile.in index 14512cf61fb..e5b1f4804c2 100644 --- a/bfd/Makefile.in +++ b/bfd/Makefile.in @@ -1328,6 +1328,24 @@ REGEN_TEXI = \ ) noinst_TEXINFOS = doc/bfdint.texi + +# The HTML manual is built with the navigation sidebar shared from +# texinfo/toc-sidebar: toc-sidebar.init drives texi2any and writes the +# contents tree (toc-sidebar-data.js) into the output directory; copy +# the static assets in alongside it. On a texi2any too old for the +# sidebar the init file disables itself and the plain manual is built. +TOC_SIDEBAR_DIR = $(srcdir)/../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js + + +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. + +# ROCm branding. +MANUAL_CSS = $(srcdir)/../rocm-theme.css all: $(BUILT_SOURCES) config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive @@ -2531,9 +2549,12 @@ doc/bfdver.texi: $(srcdir)/Makefile.in fi html-local: doc/bfd/index.html -doc/bfd/index.html: doc/bfd.texi $(bfd_TEXINFOS) doc/$(am__dirstamp) +doc/bfd/index.html: doc/bfd.texi $(bfd_TEXINFOS) doc/$(am__dirstamp) $(MANUAL_CSS) $(AM_V_at)$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) \ + --init-file=$(TOC_SIDEBAR_DIR)/toc-sidebar.init \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ --split=node -o doc/bfd $(srcdir)/doc/bfd.texi + $(AM_V_at)cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) doc/bfd # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/bfd/doc/local.mk b/bfd/doc/local.mk index bae2bd50ae3..97dcd28fd13 100644 --- a/bfd/doc/local.mk +++ b/bfd/doc/local.mk @@ -142,9 +142,30 @@ DISTCLEANFILES += %D%/bfd.?? %D%/bfd.??? texput.log MAINTAINERCLEANFILES += $(DOCFILES) +# The HTML manual is built with the navigation sidebar shared from +# texinfo/toc-sidebar: toc-sidebar.init drives texi2any and writes the +# contents tree (toc-sidebar-data.js) into the output directory; copy +# the static assets in alongside it. On a texi2any too old for the +# sidebar the init file disables itself and the plain manual is built. +TOC_SIDEBAR_DIR = $(srcdir)/../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js + +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. +MANUAL_CSS = + +# ROCm branding. +MANUAL_CSS += $(srcdir)/../rocm-theme.css + html-local: %D%/bfd/index.html -%D%/bfd/index.html: %D%/bfd.texi $(bfd_TEXINFOS) %D%/$(am__dirstamp) +%D%/bfd/index.html: %D%/bfd.texi $(bfd_TEXINFOS) %D%/$(am__dirstamp) $(MANUAL_CSS) $(AM_V_at)$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) \ + --init-file=$(TOC_SIDEBAR_DIR)/toc-sidebar.init \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ --split=node -o %D%/bfd $(srcdir)/%D%/bfd.texi + $(AM_V_at)cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) %D%/bfd MAINTAINERCLEANFILES += %D%/bfd.info diff --git a/binutils/Makefile.in b/binutils/Makefile.in index 7f3ca38a9d7..cfd1ac14536 100644 --- a/binutils/Makefile.in +++ b/binutils/Makefile.in @@ -893,6 +893,21 @@ AM_MAKEINFOFLAGS = -I "$(srcdir)/doc" -I "$(top_srcdir)/../libiberty" \ TEXI2DVI = texi2dvi -I "$(srcdir)/doc" -I "$(top_srcdir)/../libiberty" \ -I "$(top_srcdir)/../bfd/doc" -I ../bfd/doc + +# Wire in the TOC navigation sidebar with the HTML manual. +# +TOC_SIDEBAR_DIR = $(srcdir)/../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js + + +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. + +# ROCm branding. +MANUAL_CSS = $(srcdir)/../rocm-theme.css all: $(BUILT_SOURCES) config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive @@ -2171,10 +2186,17 @@ doc/$(DEMANGLER_NAME).1: doc/cxxfilt.man Makefile doc/$(am__dirstamp) sed -e 's/cxxfilt/$(DEMANGLER_NAME)/' < $$man \ > doc/$(DEMANGLER_NAME).1 +# Unlike the ld and gas manuals, this rule passes no -o, so texi2any +# derives the split output directory from binutils.texi's @setfilename +# (binutils.info) and writes the manual to binutils_html; the copied +# assets must land in that same directory. html-local: doc/binutils/index.html -doc/binutils/index.html: doc/binutils.texi $(binutils_TEXINFOS) +doc/binutils/index.html: doc/binutils.texi $(binutils_TEXINFOS) $(MANUAL_CSS) $(AM_V_GEN)$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) \ + --init-file=$(TOC_SIDEBAR_DIR)/toc-sidebar.init \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ --split=node -I$(srcdir) $(srcdir)/doc/binutils.texi + $(AM_V_at)cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) binutils_html # Maintenance diff --git a/binutils/doc/local.mk b/binutils/doc/local.mk index 63124abc3bb..7146387dced 100644 --- a/binutils/doc/local.mk +++ b/binutils/doc/local.mk @@ -180,10 +180,32 @@ MAINTAINERCLEANFILES += $(man_MANS) %D%/binutils.info %D%/cxxfilt.man sed -e 's/cxxfilt/$(DEMANGLER_NAME)/' < $$man \ > %D%/$(DEMANGLER_NAME).1 +# Wire in the TOC navigation sidebar with the HTML manual. +# +TOC_SIDEBAR_DIR = $(srcdir)/../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js + +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. +MANUAL_CSS = + +# ROCm branding. +MANUAL_CSS += $(srcdir)/../rocm-theme.css + +# Unlike the ld and gas manuals, this rule passes no -o, so texi2any +# derives the split output directory from binutils.texi's @setfilename +# (binutils.info) and writes the manual to binutils_html; the copied +# assets must land in that same directory. html-local: %D%/binutils/index.html -%D%/binutils/index.html: %D%/binutils.texi $(binutils_TEXINFOS) +%D%/binutils/index.html: %D%/binutils.texi $(binutils_TEXINFOS) $(MANUAL_CSS) $(AM_V_GEN)$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) \ + --init-file=$(TOC_SIDEBAR_DIR)/toc-sidebar.init \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ --split=node -I$(srcdir) $(srcdir)/%D%/binutils.texi + $(AM_V_at)cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) binutils_html # Maintenance diff --git a/gas/Makefile.in b/gas/Makefile.in index b2bb505cc69..fc8156e4ab6 100644 --- a/gas/Makefile.in +++ b/gas/Makefile.in @@ -1026,6 +1026,21 @@ CPU_DOCS = \ # This one isn't ready for prime time yet. Not even a little bit. noinst_TEXINFOS = doc/internals.texi + +# Wire in the TOC navigation sidebar with the HTML manual. +# +TOC_SIDEBAR_DIR = $(srcdir)/../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js + + +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. + +# ROCm branding. +MANUAL_CSS = $(srcdir)/../rocm-theme.css all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive @@ -2272,9 +2287,12 @@ doc/as.1: $(srcdir)/doc/as.texi doc/asconfig.texi $(CPU_DOCS) doc/$(am__dirstamp $(AM_V_at)rm -f doc/as.pod html-local: doc/as/index.html -doc/as/index.html: doc/as.texi $(doc_as_TEXINFOS) doc/$(am__dirstamp) +doc/as/index.html: doc/as.texi $(doc_as_TEXINFOS) doc/$(am__dirstamp) $(MANUAL_CSS) $(AM_V_GEN)$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) \ + --init-file=$(TOC_SIDEBAR_DIR)/toc-sidebar.init \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ --split=node -I$(srcdir)/doc -o doc/as $(srcdir)/doc/as.texi + $(AM_V_at)cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) doc/as # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/gas/doc/local.mk b/gas/doc/local.mk index 4185cfe9a3c..c16182a07ce 100644 --- a/gas/doc/local.mk +++ b/gas/doc/local.mk @@ -124,9 +124,27 @@ info-local: $(MANS) (rm -f $@.T$$$$ && exit 1) $(AM_V_at)rm -f %D%/as.pod +# Wire in the TOC navigation sidebar with the HTML manual. +# +TOC_SIDEBAR_DIR = $(srcdir)/../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js + +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. +MANUAL_CSS = + +# ROCm branding. +MANUAL_CSS += $(srcdir)/../rocm-theme.css + html-local: %D%/as/index.html -%D%/as/index.html: %D%/as.texi $(%C%_as_TEXINFOS) %D%/$(am__dirstamp) +%D%/as/index.html: %D%/as.texi $(%C%_as_TEXINFOS) %D%/$(am__dirstamp) $(MANUAL_CSS) $(AM_V_GEN)$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) \ + --init-file=$(TOC_SIDEBAR_DIR)/toc-sidebar.init \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ --split=node -I$(srcdir)/%D% -o %D%/as $(srcdir)/%D%/as.texi + $(AM_V_at)cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) %D%/as MAINTAINERCLEANFILES += %D%/as.info diff --git a/gdb/doc/Makefile.in b/gdb/doc/Makefile.in index 73d439fac01..ec4153b9d1d 100644 --- a/gdb/doc/Makefile.in +++ b/gdb/doc/Makefile.in @@ -85,6 +85,12 @@ PACKAGE = @PACKAGE@ PKGVERSION = @PKGVERSION@ BUGURL_TEXI = @REPORT_BUGS_TEXI@ +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. +MANUAL_CSS = + # Where is the source dir for the READLINE library doc? # Traditionally readline is in .. or . READLINE_DIR = ${gdbdir}/../readline/readline/doc @@ -542,11 +548,24 @@ gdb.mm: $(GDB_DOC_FILES) links2roff # GDB MANUAL: HTML file -gdb/index.html: ${GDB_DOC_FILES} +# Wire in the TOC navigation sidebar with the HTML manual. +# +TOC_SIDEBAR_DIR = $(srcdir)/../../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js +TOC_SIDEBAR_INIT = $(TOC_SIDEBAR_DIR)/toc-sidebar.init + +# ROCm branding. +MANUAL_CSS += $(srcdir)/../../rocm-theme.css + +gdb/index.html: ${GDB_DOC_FILES} $(TOC_SIDEBAR_INIT) $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) $(ECHO_GEN) $(MAKEHTML) $(MAKEHTMLFLAGS) \ + --init-file=$(TOC_SIDEBAR_INIT) \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ -o gdb \ $(READLINE_TEXI_INCFLAG) -I ${GDBMI_DIR} -I $(srcdir) \ $(srcdir)/gdb.texinfo + $(SILENCE) cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) gdb/ # ANNOTATE DOCUMENTATION: TeX dvi file annotate.dvi : $(ANNOTATE_DOC_FILES) diff --git a/ld/Makefile.am b/ld/Makefile.am index 3ae3d7f14f6..15b04891870 100644 --- a/ld/Makefile.am +++ b/ld/Makefile.am @@ -816,11 +816,29 @@ diststuff: info $(EXTRA_DIST) # ld.1 to support parallel build. info-recursive: ld.1 +# Wire in the TOC navigation sidebar with the HTML manual. +# +TOC_SIDEBAR_DIR = $(srcdir)/../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js + +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. +MANUAL_CSS = + +# ROCm branding. +MANUAL_CSS += $(srcdir)/../rocm-theme.css + html-local: doc/ld/index.html -doc/ld/index.html: ld.texi $(ld_TEXINFOS) +doc/ld/index.html: ld.texi $(ld_TEXINFOS) $(MANUAL_CSS) $(AM_V_at)$(MKDIR_P) doc $(AM_V_GEN)$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) \ + --init-file=$(TOC_SIDEBAR_DIR)/toc-sidebar.init \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ --split=node -I$(srcdir) $(srcdir)/ld.texi -o doc/ld + $(AM_V_at)cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) doc/ld DISTCLEANFILES = site.exp development.exp enablings.exp site.bak distclean-local: diff --git a/ld/Makefile.in b/ld/Makefile.in index d879f04a17c..5b30105a6ca 100644 --- a/ld/Makefile.in +++ b/ld/Makefile.in @@ -1084,6 +1084,21 @@ CLEANFILES = dep.sed DEP DEPA DEP1 DEP2 spu_ovl.s spu_ovl.@OBJEXT@ spu_icache.s EXTRA_DIST = ldgram.c ldgram.h ldlex.c emultempl/spu_ovl.@OBJEXT@_c \ emultempl/spu_icache.@OBJEXT@_c deffilep.c deffilep.h $(man_MANS) + +# Hook in the TOC navigation sidebar. +# +TOC_SIDEBAR_DIR = $(srcdir)/../texinfo/toc-sidebar +TOC_SIDEBAR_ASSETS = $(TOC_SIDEBAR_DIR)/toc-sidebar.css \ + $(TOC_SIDEBAR_DIR)/toc-sidebar.js + + +# List of extra stylesheets to brand the HTML manual. Each stylesheet +# is referenced with --css-ref and copied into the output directory. +# It is empty by default and can be set on the make command line, so a +# build can theme the manual without editing the sources. + +# ROCm branding. +MANUAL_CSS = $(srcdir)/../rocm-theme.css DISTCLEANFILES = site.exp development.exp enablings.exp site.bak all: $(BUILT_SOURCES) config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive @@ -2475,10 +2490,13 @@ diststuff: info $(EXTRA_DIST) info-recursive: ld.1 html-local: doc/ld/index.html -doc/ld/index.html: ld.texi $(ld_TEXINFOS) +doc/ld/index.html: ld.texi $(ld_TEXINFOS) $(MANUAL_CSS) $(AM_V_at)$(MKDIR_P) doc $(AM_V_GEN)$(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) \ + --init-file=$(TOC_SIDEBAR_DIR)/toc-sidebar.init \ + $(foreach css,$(MANUAL_CSS),--css-ref=$(notdir $(css))) \ --split=node -I$(srcdir) $(srcdir)/ld.texi -o doc/ld + $(AM_V_at)cp $(TOC_SIDEBAR_ASSETS) $(MANUAL_CSS) doc/ld distclean-local: rm -rf ldscripts diff --git a/rocm-theme-sync.py b/rocm-theme-sync.py new file mode 100755 index 00000000000..4a9b809db9d --- /dev/null +++ b/rocm-theme-sync.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved. + +# This file is part of GDB. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""Refresh rocm-theme.css's generated token block from the live ROCm docs. + +The ROCm theme stylesheet (rocm-theme.css) defines a block of +--toc-sidebar-* custom properties so that the manual matches the ROCm +documentation site. Those values are the light-mode design tokens of +the pydata-sphinx-theme the ROCm site is built with -- the color +palette, the heading font-size ramp and weight, and the base and +monospace font stacks. ROCm leaves the pydata defaults in place +except for a couple of overrides shipped in rocm_custom.css. + +This script fetches the live site's CSS, reads those source variables, +and rewrites the generated :root block of the sibling stylesheet in +place. The file is only touched when a value actually changed, to +avoid bumping the css's modification time unnecessarily. + +Only the tokens pydata exposes as --pst-* custom properties are +synced. The rest of the theme's metrics (the body and heading +line-heights, the heading margins, the pre and inline-code box) live +in pydata's rule bodies rather than in custom properties, i.e., they +are maintained by hand in the stylesheet, which is why they are not +touched here. + +Needs network access to https://rocm.docs.amd.com. + +""" + +import argparse +import os +import re +import sys +import urllib.request + +BASE = "https://rocm.docs.amd.com/en/latest/_static" + +# The pydata theme stylesheet (which carries the --pst-* defaults) and +# the ROCm custom stylesheet (carries the site's own overrides). +PYDATA_CSS = f"{BASE}/styles/pydata-sphinx-theme.css" +ROCM_CSS = f"{BASE}/rocm_custom.css" + +# The committed theme file, a sibling of this script. +THEME_CSS = os.path.join(os.path.dirname(os.path.abspath(__file__)), "rocm-theme.css") + +# The first line inside the generated :root block. It both labels the +# block as machine-generated and is the anchor that update_css() keys +# on to find the block to replace. Emitter and matcher share this one +# constant. +MARKER = "/* generated by rocm-theme-sync.py */" + +# Each mapping row is a tuple of: +# +# - our property (--toc-sidebar-* or --rocm-*) +# - the pydata/ROCm source variable whose light-mode value we copy +# +# pydata has no variable literally named for links: the site paints +# links with --pst-color-primary and link-hover with +# --pst-color-secondary, which is why those feed our two accents (and +# why the hover accent comes out purple). Every value is a stock +# pydata light-mode default except the row-hover background, which +# ROCm overrides in rocm_custom.css. +# +# The --toc-sidebar-* properties come first: those are the contract +# that toc-sidebar.css reads. The --rocm-* properties follow; they are +# used only within rocm-theme.css. Some of both groups happen to share +# a source token (e.g. --toc-sidebar-bg and --rocm-code-bg are both +# --pst-color-surface), which the trailing source comments record. +PALETTE = [ + # contract: read by toc-sidebar.css + ("--toc-sidebar-accent", "--pst-color-primary"), # site link color + ("--toc-sidebar-text", "--pst-color-text-base"), + ("--toc-sidebar-muted", "--pst-color-text-muted"), + ("--toc-sidebar-border", "--pst-color-border"), + ("--toc-sidebar-bg", "--pst-color-surface"), + ("--toc-sidebar-hover-bg", "--pst-color-table-row-hover-bg"), # rocm override + ("--toc-sidebar-search-bg", "--pst-color-background"), + ("--toc-sidebar-on-accent", "--pst-color-primary-text"), + # internal: used only within rocm-theme.css + ("--rocm-accent-dark", "--pst-color-secondary"), # site link-hover color + ("--rocm-code-bg", "--pst-color-surface"), + ("--rocm-inline-code", "--pst-color-inline-code"), +] + +# The heading font-size ramp and the heading weight. pydata's +# headings are set in the site's light weight, not bold. +TYPE_SCALE = [ + ("--rocm-font-size-h1", "--pst-font-size-h1"), + ("--rocm-font-size-h2", "--pst-font-size-h2"), + ("--rocm-font-size-h3", "--pst-font-size-h3"), + ("--rocm-font-size-h4", "--pst-font-size-h4"), + ("--rocm-font-size-h5", "--pst-font-size-h5"), + ("--rocm-font-size-h6", "--pst-font-size-h6"), + ("--rocm-heading-weight", "--pst-font-weight-heading"), +] + +# The UI and monospace font stacks. We copy the *-system variables, +# which hold the literal stacks; the plain --pst-font-family-* names +# are just var() aliases of these. +FONT_STACKS = [ + ("--rocm-font-base", "--pst-font-family-base-system"), + ("--rocm-font-mono", "--pst-font-family-monospace-system"), +] + +# The groups emitted into the block, in order. The third element says +# whether to pad the rows so the trailing /* source */ comments line +# up. The font stacks are long enough that aligning their comments +# would push them far off to the right, so that group is left +# unaligned. +GROUPS = [ + ("palette", PALETTE, True), + ("type scale", TYPE_SCALE, True), + ("font stacks", FONT_STACKS, False), +] + + +def fetch(url): + """Return the text of URL, decoded as UTF-8.""" + with urllib.request.urlopen(url, timeout=30) as r: + return r.read().decode("utf-8", "replace") + + +def light_values(css): + """Map every --pst-* name to its light-mode value. + + pydata defines the light palette under the html:not([data-theme]) + rule and the dark palette under html[data-theme=dark]. ROCm's + overrides sit under html[data-theme='light']. We want the + light-mode value, so we skip any definition that falls inside a + dark-theme block and keep the rest; a later light definition (an + override) wins over an earlier default, which is the cascade order + on the page too. + """ + values = {} + # Walk the stylesheet in source order, tracking nothing more than + # whether the current selector is a dark-theme block. + for sel, body in re.findall(r"([^{}]*)\{([^{}]*)\}", css): + flat = sel.replace(" ", "").replace('"', "").replace("'", "") + if "data-theme=dark" in flat: + continue + for name, val in re.findall(r"(--pst-[a-z0-9-]+)\s*:\s*([^;]+)", body): + values[name] = val.strip() + return values + + +def format_value(val, *, is_font_stack): + """Normalize a source value for emission into the stylesheet.""" + # Lowercase hex colors; the source mixes cases and the committed + # file keeps them lowercase. + val = re.sub(r"#[0-9a-fA-F]+", lambda m: m.group().lower(), val) + # The site ships the font stacks comma-packed. Space them out so + # that the committed value reads like regular CSS. + if is_font_stack: + val = re.sub(r",\s*", ", ", val) + return val + + +def build_root(): + """Return the full generated :root block as a string of CSS. + + Fetch the live stylesheets, look up each mapped source variable's + light-mode value, and lay the --toc-sidebar-* properties out group + by group, each group introduced by a comment and (when aligned) + padded so its trailing /* source */ comments line up. Exit if a + mapped source variable is missing, which means the upstream theme + changed too much to handle here. + """ + pyd = light_values(fetch(PYDATA_CSS)) + rocm = light_values(fetch(ROCM_CSS)) + # ROCm overrides take precedence over the pydata defaults. + src = {**pyd, **rocm} + + wanted = [theirs for _, group, _ in GROUPS for _, theirs in group] + missing = [v for v in wanted if v not in src] + if missing: + sys.exit( + f"source variables not found (theme changed?): " + f"{', '.join(sorted(set(missing)))}" + ) + + lines = [f" {MARKER}"] + for label, group, align in GROUPS: + # Build each " --toc-sidebar-x: value;" prefix, then (for aligned + # groups) pad them to a common width so the trailing comments + # line up. + rows = [] + for ours, theirs in group: + val = format_value(src[theirs], is_font_stack=(label == "font stacks")) + rows.append((f" {ours}: {val};", theirs.replace("--", ""))) + width = max(len(prefix) for prefix, _ in rows) if align else 0 + lines.append("") + lines.append(f" /* {label} */") + for prefix, comment in rows: + lines.append(f"{prefix.ljust(width)} /* {comment} */") + return ":root {\n" + "\n".join(lines) + "\n}" + + +def update_css(root): + """Replace the generated :root block of the theme file with ROOT. + + Rewrite the file only when the block changes, so that an unchanged + refresh leaves the mtime untouched. Return True if it was + rewritten. + """ + with open(THEME_CSS) as f: + old = f.read() + # Anchor on a column-0 ":root {" whose first inner line is MARKER, + # then run to the next line that is just "}". + pattern = r"^:root \{\n " + re.escape(MARKER) + r"\n.*?\n\}" + new, n = re.subn( + pattern, lambda m: root, old, count=1, flags=re.DOTALL | re.MULTILINE + ) + if n == 0: + sys.exit(f"no generated :root block found in {THEME_CSS}") + if new == old: + return False + with open(THEME_CSS, "w") as f: + f.write(new) + return True + + +def main(): + """Rewrite the generated block in place and report what happened.""" + argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ).parse_args() + + name = os.path.basename(THEME_CSS) + if update_css(build_root()): + print(f"updated {name}; review the change with git diff") + else: + print(f"{name} already matches the live ROCm docs site") + + +if __name__ == "__main__": + main() diff --git a/rocm-theme.css b/rocm-theme.css new file mode 100644 index 00000000000..74063fbd465 --- /dev/null +++ b/rocm-theme.css @@ -0,0 +1,144 @@ +/* Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* ROCm theme for the HTML manual, styled after the AMD ROCm + documentation site. This sets the palette (the --toc-sidebar-* + custom properties the feature reads) and the manual's general look, + the body type and content typography. + + The font stacks are copied from the ROCm documentation site so the + manual matches the surrounding pages. + + The block below is generated from the live ROCm documentation site, + which is built with pydata-sphinx-theme; these are that theme's + light-mode design tokens (ROCm overrides only the row-hover + background). The palette, the heading size ramp and weight, and + the font stacks are all sourced this way, with the source variable + named in each comment. Do not edit the block by hand, as + rocm-theme-sync.py rewrites it in place from the live site. */ +:root { + /* generated by rocm-theme-sync.py */ + + /* palette */ + --toc-sidebar-accent: #0a7d91; /* pst-color-primary */ + --toc-sidebar-text: #222832; /* pst-color-text-base */ + --toc-sidebar-muted: #48566b; /* pst-color-text-muted */ + --toc-sidebar-border: #d1d5da; /* pst-color-border */ + --toc-sidebar-bg: #f3f4f5; /* pst-color-surface */ + --toc-sidebar-hover-bg: #e2e8f0; /* pst-color-table-row-hover-bg */ + --toc-sidebar-search-bg: #fff; /* pst-color-background */ + --toc-sidebar-on-accent: #fff; /* pst-color-primary-text */ + --rocm-accent-dark: #8045e5; /* pst-color-secondary */ + --rocm-code-bg: #f3f4f5; /* pst-color-surface */ + --rocm-inline-code: #912583; /* pst-color-inline-code */ + + /* type scale */ + --rocm-font-size-h1: 2.5rem; /* pst-font-size-h1 */ + --rocm-font-size-h2: 2rem; /* pst-font-size-h2 */ + --rocm-font-size-h3: 1.75rem; /* pst-font-size-h3 */ + --rocm-font-size-h4: 1.5rem; /* pst-font-size-h4 */ + --rocm-font-size-h5: 1.25rem; /* pst-font-size-h5 */ + --rocm-font-size-h6: 1.1rem; /* pst-font-size-h6 */ + --rocm-heading-weight: 400; /* pst-font-weight-heading */ + + /* font stacks */ + --rocm-font-base: -apple-system, "BlinkMacSystemFont", "Segoe UI", "Helvetica Neue", "Arial", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; /* pst-font-family-base-system */ + --rocm-font-mono: "SFMono-Regular", "Menlo", "Consolas", "Monaco", "Liberation Mono", "Lucida Console", monospace; /* pst-font-family-monospace-system */ +} + +html { scroll-behavior: smooth; } + +body { + color: var(--toc-sidebar-text); + font-family: var(--rocm-font-base); + font-size: 16px; /* pydata light-mode value, hand-transcribed */ + line-height: 1.65; /* pydata light-mode value, hand-transcribed */ +} + +/* Heading line-height and margins are pydata-sphinx-theme's + light-mode values, transcribed by hand. Unlike the size ramp and + weight above, pydata keeps these in its heading rule body rather + than in custom properties, so rocm-theme-sync.py cannot pull + them. */ +h1, h2, h3, h4, h5, h6 { + line-height: 1.15; + margin: 2.75rem 0 1.05rem; + font-weight: var(--rocm-heading-weight); +} +h1 { font-size: var(--rocm-font-size-h1); } +h2 { font-size: var(--rocm-font-size-h2); } +h3 { font-size: var(--rocm-font-size-h3); } +h4 { font-size: var(--rocm-font-size-h4); } +h5 { font-size: var(--rocm-font-size-h5); } +h6 { font-size: var(--rocm-font-size-h6); } + +/* The ROCm site underlines links rather than relying on color alone; + the hover color comes from the palette. */ +a { color: var(--toc-sidebar-accent); text-decoration: underline; } +a:hover { color: var(--rocm-accent-dark); } + +pre, code, samp, kbd { + font-family: var(--rocm-font-mono); +} + +/* The code box's border-radius and padding (here and on the inline + code/samp rule below) are pydata-sphinx-theme's light-mode values, + transcribed by hand: like the heading metrics above, pydata keeps + them in its rule bodies rather than in custom properties, so + rocm-theme-sync.py cannot pull them. */ +pre { + background: var(--rocm-code-bg); + border: 1px solid var(--toc-sidebar-border); + border-radius: 0.25rem; + padding: 1rem; + line-height: 1.2; /* tighter than body text; code reads better dense */ + margin: 1.5em 0; /* gap above and below, off the surrounding text */ + overflow-x: auto; +} + +code, samp { + color: var(--rocm-inline-code); + background: var(--rocm-code-bg); + border: 1px solid var(--toc-sidebar-border); + /* See note in the pre rule above. */ + border-radius: 0.25rem; + padding: 0.1rem 0.25rem; +} +pre code, pre samp { + color: inherit; background: none; border: none; padding: 0; +} + +/* The texinfo per-node prev/next/up panel, toned down with a smaller + type. nav-panel is a class texinfo emits in its own HTML output, + and texinfo does not promise it as a stable name. If a texinfo + version ever renames it, this rule stops matching and the panel + falls back to the browser's default styling. */ +.nav-panel { + font-size: 0.85rem; + color: var(--toc-sidebar-muted); + background: var(--toc-sidebar-bg); + border: 1px solid var(--toc-sidebar-border); + border-radius: 6px; + padding: 0.4rem 0.75rem; + margin-bottom: 1.5rem; +} + +table { border-collapse: collapse; } +table td, table th { + border: 1px solid var(--toc-sidebar-border); + padding: 0.4rem 0.6rem; +} diff --git a/texinfo/toc-sidebar/README b/texinfo/toc-sidebar/README new file mode 100644 index 00000000000..efc19c274aa --- /dev/null +++ b/texinfo/toc-sidebar/README @@ -0,0 +1,96 @@ +toc-sidebar: a navigation sidebar for texinfo HTML manuals +========================================================== + +These files add a persistent navigation sidebar -- a table-of-contents +pane on the left of every page -- to the HTML build of a texinfo +manual. + +texinfo has no built-in support for a sidebar, but it can be driven +through a texi2any init file, which is enough to build a sidebar +ourselves. The init file uses a texi2any init API introduced in +version 7.0 and guards on it, falling back to the default +contents-only manual on older texi2any. + +The files here are manual-agnostic. Each manual's HTML build rule +wires the files here in (see that manual's Makefile). This directory +is just the shared implementation. + +The files +--------- + +toc-sidebar.init + A texi2any init file, loaded with --init-file. On every page it + injects an empty sidebar. Once, at the end of the manual + generation, it writes the manual's whole table of contents and its + title to toc-sidebar-data.js in the output directory. + +toc-sidebar.js + Loaded on every page. Fills in the sidebar shell from the + contents tree of toc-sidebar-data.js, and handles all the sidebar + logic. + +toc-sidebar.css + The sidebar's layout and default theme. + +toc-sidebar-data.js + Not in this directory: generated into the output directory at build + time by toc-sidebar.init. Holds the shared contents tree and title + that every page's toc-sidebar.js reads. + + +How it fits together +-------------------- + +The contents tree exists only in index.html. Rather than inline it +into all the pages (about 90 MB across a manual the size of gdb's) or +fetch it at runtime (blocked under file://), toc-sidebar.init writes +it once to the shared toc-sidebar-data.js. Every page loads that one +file with a plain + +
+END_SIDEBAR + +texinfo_set_from_init_file('AFTER_BODY_OPEN', $sidebar); +texinfo_set_from_init_file('PRE_BODY_CLOSE', "
\n"); + +# Pull in the sidebar's own stylesheet here rather than with +# texi2any's --css-ref. The sidebar exists only when this init file +# runs, i.e. on texi2any 7.0+, and referencing toc-sidebar.css from +# here ties it to that, so a build that falls back to the plain manual +# never points at a stylesheet for a sidebar it did not emit. +# +# Mark the page as script-enabled. This inline script has no "defer", +# so the parser runs it immediately, here in the head before the body +# is laid out. +# +# Add the "js" class to , which the stylesheet keys the sidebar +# layout off. Setting the class before the first paint means the page +# is drawn the right way the first time: with the sidebar when +# JavaScript is on, and as a single column when it is off. If class +# were set later instead, the page would first be drawn as a single +# column and then re-laid out once the sidebar appeared. The contents +# data (toc-sidebar-data.js) and the sidebar script (toc-sidebar.js) +# load from the body, next to the sidebar shell; see the comment on +# $sidebar above for why they go there and not here. +texinfo_set_from_init_file('EXTRA_HEAD', + '' + . ''); + +1; diff --git a/texinfo/toc-sidebar/toc-sidebar.js b/texinfo/toc-sidebar/toc-sidebar.js new file mode 100644 index 00000000000..a64fcb4ae1e --- /dev/null +++ b/texinfo/toc-sidebar/toc-sidebar.js @@ -0,0 +1,589 @@ +/* Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Build the left navigation sidebar of the HTML manual. + + Each page ships with an empty sidebar shell (put there by + toc-sidebar.init) and pulls in toc-sidebar-data.js, which sets + window.TOC_SIDEBAR_CONTENTS to the manual's whole table of contents + as a string of HTML. This script turns that string into the + sidebar tree, marks the current entry for the current page, and + makes interior nodes collapsible. The sections the user expands + stay open when moving from one page to the next. + + With JavaScript off, this script never runs -- the stylesheet hides + the sidebar and the page shows a single content column. +*/ + +(function () +{ + "use strict"; + + /* Remember which sections the user has expanded. Each page is a + separate file load that rebuilds the tree from scratch, so + without this, a manual expansion would be lost on when navigating + between pages. + + The set is a map from a node's href to true, kept in + sessionStorage as JSON. This relies on the hrefs being the same + on every page, which is true because every page builds its tree + from the the same shared toc-sidebar-data.js. That is what makes + a set saved on one page apply to the next. Storage is not always + available (private browsing, or some browsers under file://), so + every access is guarded and falls back to an empty set. */ + var STORAGE_KEY = "toc-sidebar-open"; + + /* The set of open sections, or an empty set when none are stored + yet or storage is unavailable. */ + function loadOpenSet () + { + try + { + var raw = window.sessionStorage.getItem (STORAGE_KEY); + return raw ? JSON.parse (raw) : {}; + } + catch (e) + { + return {}; + } + } + + /* Remember the set of open sections. */ + function saveOpenSet (open) + { + try + { + window.sessionStorage.setItem (STORAGE_KEY, JSON.stringify (open)); + } + catch (e) + { + /* Persistence is a nice-to-have, so a failure here is + fine. */ + } + } + + /* How far down the sidebar is scrolled is remembered the same way + as the open set. Each page is a separate file whose sidebar + starts scrolled to the top, so without this the scroll position + lost on every navigation. The value is the sidebar's scrollTop + in pixels, as a string, in sessionStorage. */ + var SCROLL_KEY = "toc-sidebar-scroll"; + + /* The stored sidebar scroll offset in pixels, or null when there is + none yet or storage is unavailable. */ + function loadScroll () + { + try + { + var raw = window.sessionStorage.getItem (SCROLL_KEY); + return raw === null ? null : parseFloat (raw); + } + catch (e) + { + return null; + } + } + + /* Remember the sidebar scroll offset, given in pixels. */ + function saveScroll (px) + { + try + { + window.sessionStorage.setItem (SCROLL_KEY, px); + } + catch (e) + { + /* Persistence is a nice-to-have, so a failure here is + fine. */ + } + } + + /* Return whether LINK's box lies entirely within SIDEBAR's visible + area, so the user can already see it without the sidebar + moving. */ + function entryVisible (sidebar, link) + { + var sr = sidebar.getBoundingClientRect (); + var lr = link.getBoundingClientRect (); + return lr.top >= sr.top && lr.bottom <= sr.bottom; + } + + /* Save the SIDEBAR's scroll offset whenever the user scrolls it, so + that the next page opens at the same place. */ + function rememberScroll (sidebar) + { + /* Defer the write to the next animation frame so a burst of + scroll events collapses into one write rather than hitting + storage on every pixel. */ + var pending = false; + sidebar.addEventListener ("scroll", function () + { + if (pending) + return; + pending = true; + window.requestAnimationFrame (function () + { + pending = false; + saveScroll (sidebar.scrollTop); + }); + }); + } + + /* Basename of the current document. For a directory URL, returns + index.html. */ + function currentFile () + { + var path = window.location.pathname; + var base = path.substring (path.lastIndexOf ("/") + 1); + return base === "" ? "index.html" : base; + } + + /* Basename of a link target, with any #anchor dropped. */ + function linkFile (href) + { + var hash = href.indexOf ("#"); + if (hash >= 0) + href = href.substring (0, hash); + return href.substring (href.lastIndexOf ("/") + 1); + } + + /* The "#anchor" part of a link target, or empty string when there + is none. */ + function linkAnchor (href) + { + var hash = href.indexOf ("#"); + return hash >= 0 ? href.substring (hash) : ""; + } + + /* Pick the contents entry for the current page. Several entries can + point into the same page when a page holds sections that have no + node of their own, which texinfo emits as anchors within the one + file. Among the entries for this page, prefer the one whose anchor + matches the address bar. With no anchor there, that is the entry + for the top of the page itself. */ + function pickCurrent (candidates) + { + var hash = window.location.hash; + var i; + for (i = 0; i < candidates.length; i++) + if (linkAnchor (candidates[i].getAttribute ("href")) === hash) + return candidates[i]; + for (i = 0; i < candidates.length; i++) + if (linkAnchor (candidates[i].getAttribute ("href")) === "") + return candidates[i]; + return candidates.length ? candidates[0] : null; + } + + /* Highlight the entry for the current page and anchor, and open the + path of sections down to it. CANDIDATES are the entries that + point at the current page; which one is current depends on the + address-bar anchor, so this clears any previous highlight first + and reruns on every anchor change, not only on load -- see the + listener in decorate. Returns the highlighted entry, or null + when none matches. */ + function markCurrent (container, candidates, open) + { + var previous = container.querySelector (".toc-sidebar-current"); + if (previous) + previous.classList.remove ("toc-sidebar-current"); + + var current = pickCurrent (candidates); + if (!current) + return null; + + current.classList.add ("toc-sidebar-current"); + + /* Open the path down to the current entry, and remember it the + same way a manual expansion is remembered. Otherwise a section + opened only because the user walked into it would collapse + again the moment they moved to another part of the manual. */ + var node = current.closest ("li"); + while (node) + { + node.classList.add ("toc-sidebar-open"); + var nodeLink = node.querySelector (":scope > a"); + if (nodeLink) + open[nodeLink.getAttribute ("href")] = true; + node = node.parentElement ? node.parentElement.closest ("li") : null; + } + saveOpenSet (open); + + return current; + } + + /* Bring CURRENT on screen, but only when it is not already visible. + When the user moves between two nearby entries, the sidebar stays + put. Only a jump to a far-off part of the TOC scrolls. */ + function revealCurrent (sidebar, current) + { + if (sidebar && current && !entryVisible (sidebar, current)) + current.scrollIntoView ({ block: "center" }); + } + + /* Make the plain TOC tree interactive. Flag every node that has + children (toc-sidebar-has-children) and give it a [+]/[-] toggle. + Then, find the entry for the current page, highlight it, and open + the path down to it. */ + function decorate (container) + { + var here = currentFile (); + var open = loadOpenSet (); + var candidates = []; + var sidebar = document.getElementById ("toc-sidebar"); + + var items = container.querySelectorAll ("li"); + items.forEach (function (li) + { + var sublist = li.querySelector (":scope > ul"); + var link = li.querySelector (":scope > a"); + var key = link ? link.getAttribute ("href") : null; + + if (sublist) + { + li.classList.add ("toc-sidebar-has-children"); + if (key && open[key]) + li.classList.add ("toc-sidebar-open"); + var toggle = document.createElement ("span"); + toggle.className = "toc-sidebar-toggle"; + toggle.setAttribute ("role", "button"); + toggle.setAttribute ("aria-label", "Toggle section"); + toggle.addEventListener ("click", function (event) + { + event.preventDefault (); + var nowOpen = li.classList.toggle ("toc-sidebar-open"); + if (key) + { + if (nowOpen) + open[key] = true; + else + delete open[key]; + saveOpenSet (open); + } + }); + li.insertBefore (toggle, li.firstChild); + } + + if (link && linkFile (link.getAttribute ("href")) === here) + candidates.push (link); + }); + + var current = markCurrent (container, candidates, open); + if (current) + { + /* Put the sidebar back where the user had it on the previous + page, now that the path down to the current entry is open + and the tree has its final height. Then center the current + entry only if that restored position does not already show + it. Centering updates scrollTop, so the explicit save + below records the centered position for the next page. + Restoring it there then leaves the entry on screen, so the + next page does not scroll either. */ + if (sidebar) + { + var saved = loadScroll (); + if (saved !== null) + sidebar.scrollTop = saved; + revealCurrent (sidebar, current); + } + } + else if (sidebar) + { + /* Nothing matched: we're in the contents page itself, whose + Top node has no entry in its own table of contents, or some + auxiliary page. Leave the tree collapsed -- the top-level + entries are always shown, and any sections the user + expanded elsewhere are open through the saved set -- and + just restore the saved scroll position. */ + var savedTop = loadScroll (); + if (savedTop !== null) + sidebar.scrollTop = savedTop; + } + + if (sidebar) + { + saveScroll (sidebar.scrollTop); + rememberScroll (sidebar); + } + + /* Several subsections can share one page, emitted as anchors + within the one file. Following such a link from elsewhere on + that same page is an in-document jump -- the browser moves to + the anchor without reloading. In that case this script does + not run again and the highlight would otherwise stay on the + entry the user came from. Handle every hash change so the + sidebar follows the user into these intra-page sections. */ + window.addEventListener ("hashchange", function () + { + revealCurrent (sidebar, markCurrent (container, candidates, open)); + }); + } + + /* Replace the placeholder in the shell with the decorated tree. */ + function inject (ul) + { + var toc = document.getElementById ("toc-sidebar-tree"); + if (!toc) + return; + toc.innerHTML = ""; + toc.appendChild (ul); + decorate (toc); + } + + /* Parse window.TOC_SIDEBAR_CONTENTS and return its top-level