From 21795cf87a4ee05694657edb28448d5522aff747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Sun, 17 May 2026 18:43:43 +0200 Subject: [PATCH 1/9] feat: Missing toolboxes finder script --- utils/find_missing_toolboxes.sh | 107 ++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100755 utils/find_missing_toolboxes.sh diff --git a/utils/find_missing_toolboxes.sh b/utils/find_missing_toolboxes.sh new file mode 100755 index 00000000000..06cd68a21a3 --- /dev/null +++ b/utils/find_missing_toolboxes.sh @@ -0,0 +1,107 @@ +#!/bin/sh + +TOOLBOXES="gui/wxpython/xml/toolboxes.xml" + +if [ ! -f "$TOOLBOXES" ]; then + echo "ERROR: $TOOLBOXES not found. Run from the repository root." >&2 + exit 1 +fi + +WXGUI_ITEMS="gui/wxpython/xml/wxgui_items.xml" + +# Tools accessible in the GUI: direct module-item entries in toolboxes.xml, +# tools wrapped by a wxgui-item dialog, and tools invoked directly via a tag. +registered=$( + { + sed -n 's/.*\([^<]*\)<\/related-module>.*/\1/p' "$WXGUI_ITEMS" + sed -n 's/.*\([a-z][a-z0-9]*\.[a-z][a-z0-9._]*\).*/\1/p' "$WXGUI_ITEMS" + } | sort -u +) + +# Find tool directories: one level deep anywhere in the tree, name matches the GRASS tool naming convention. +source_tools=$(find . -mindepth 2 -maxdepth 2 -type d -regex '.*/[a-z][a-z0-9]*\.[a-z][a-z0-9._]*' \ + | sed 's|.*/||' | sort -u) + +# Tools intentionally absent from the GUI menu. One name per line so that grep -Fxf can match exactly. +# d.* Display tools are called from the map display, not the module menu. +# g.* Internal tools that are not meant for the user to use. +excluded_list=$(cat <<'EOF' +d.background +d.barscale +d.colorlist +d.colortable +d.correlate +d.erase +d.extract +d.font +d.fontlist +d.frame +d.geodesic +d.graph +d.grid +d.his +d.histogram +d.info +d.labels +d.legend +d.legend.vect +d.linegraph +d.mon +d.northarrow +d.out.file +d.path +d.polar +d.profile +d.rast +d.rast.arrow +d.rast.leg +d.rast.num +d.redraw +d.rgb +d.rhumbline +d.shade +d.text +d.title +d.to.rast +d.vect +d.vect.chart +d.vect.thematic +d.what.rast +d.what.vect +d.where +g.cairocomp +g.dirseps +g.filename +g.findetc +g.findfile +g.gui +g.html2man +g.message +g.mkfontcap +g.parser +g.pnmcomp +g.ppmtopng +g.search.modules +g.setproj +g.tempfile +g.version +EOF +) + +# Remove excluded tools, then remove already-registered tools. +missing=$( + echo "$source_tools" \ + | grep -Fxvf <(echo "$excluded_list") \ + | grep -Fxvf <(echo "$registered") +) + +if [ -z "$missing" ]; then + echo "All source tools are registered in toolboxes.xml." + exit 0 +fi + +count=$(echo "$missing" | wc -l | tr -d ' ') +echo "Tools in source tree but missing from toolboxes.xml ($count):" +echo "" +echo "$missing" | sed 's/^/ /' \ No newline at end of file From 15be2cc1d2e1936d469bc0b92c496cc0d5fd0fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Sun, 17 May 2026 19:26:43 +0200 Subject: [PATCH 2/9] fix: Look for directories with code --- utils/find_missing_toolboxes.sh | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/utils/find_missing_toolboxes.sh b/utils/find_missing_toolboxes.sh index 06cd68a21a3..823db3909ad 100755 --- a/utils/find_missing_toolboxes.sh +++ b/utils/find_missing_toolboxes.sh @@ -19,9 +19,18 @@ registered=$( } | sort -u ) -# Find tool directories: one level deep anywhere in the tree, name matches the GRASS tool naming convention. -source_tools=$(find . -mindepth 2 -maxdepth 2 -type d -regex '.*/[a-z][a-z0-9]*\.[a-z][a-z0-9._]*' \ - | sed 's|.*/||' | sort -u) +# Find tool directories: up to three levels deep anywhere in the tree, +# name matches the GRASS tool naming convention, and contains a Python script or C main.c. +source_tools=$( + find . -mindepth 2 -maxdepth 3 -type d -regex '.*/[a-z][a-z0-9]*\.[a-z][a-z0-9._]*' \ + | while IFS= read -r dir; do + name="${dir##*/}" + if [ -f "${dir}/${name}.py" ] || [ -f "${dir}/main.c" ]; then + echo "$name" + fi + done \ + | sort -u +) # Tools intentionally absent from the GUI menu. One name per line so that grep -Fxf can match exactly. # d.* Display tools are called from the map display, not the module menu. From 08610efe1aab945fd594152a498e8fce9baf4b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Sun, 17 May 2026 19:40:20 +0200 Subject: [PATCH 3/9] feat: Update toolboxes.xml --- gui/wxpython/xml/toolboxes.xml | 156 +++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/gui/wxpython/xml/toolboxes.xml b/gui/wxpython/xml/toolboxes.xml index 34bcbb0de95..d6c26c00175 100644 --- a/gui/wxpython/xml/toolboxes.xml +++ b/gui/wxpython/xml/toolboxes.xml @@ -30,12 +30,24 @@ + + + + + + + + + + + + @@ -166,6 +178,25 @@ + + + + + + + + + + + + + + + + + + + @@ -191,6 +222,18 @@ + + + + + + + + + + + + @@ -377,6 +420,12 @@ + + + + + + @@ -468,6 +517,9 @@ + + + @@ -485,6 +537,9 @@ + + + @@ -569,6 +624,9 @@ + + + @@ -617,6 +675,13 @@ + + + + + + + @@ -637,6 +702,10 @@ + + + + @@ -694,6 +763,13 @@ + + + + + + + @@ -719,6 +795,9 @@ + + + @@ -730,6 +809,9 @@ + + + @@ -1143,6 +1225,15 @@ + + + + + + + + + @@ -1189,6 +1280,15 @@ + + + + + + + + + @@ -1271,6 +1371,9 @@ + + + @@ -1433,6 +1536,12 @@ + + + + + + @@ -1516,6 +1625,12 @@ + + + + + + @@ -1707,6 +1822,12 @@ + + + + + + @@ -1796,6 +1917,9 @@ + + + @@ -1847,6 +1971,12 @@ + + + + + + @@ -1860,9 +1990,18 @@ + + + + + + + + + @@ -1905,6 +2044,13 @@ + + + + + + + @@ -1988,6 +2134,9 @@ + + + @@ -2006,6 +2155,13 @@ + + + + + + + From a487f6419e03dceb91b9df8d95472523c2a270eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= <119013992+gureckyfrantisek@users.noreply.github.com> Date: Sun, 17 May 2026 22:10:28 +0200 Subject: [PATCH 4/9] fix: pre-commit formatting fix --- utils/find_missing_toolboxes.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/find_missing_toolboxes.sh b/utils/find_missing_toolboxes.sh index 823db3909ad..2fd0c426526 100755 --- a/utils/find_missing_toolboxes.sh +++ b/utils/find_missing_toolboxes.sh @@ -19,7 +19,7 @@ registered=$( } | sort -u ) -# Find tool directories: up to three levels deep anywhere in the tree, +# Find tool directories: up to three levels deep anywhere in the tree, # name matches the GRASS tool naming convention, and contains a Python script or C main.c. source_tools=$( find . -mindepth 2 -maxdepth 3 -type d -regex '.*/[a-z][a-z0-9]*\.[a-z][a-z0-9._]*' \ @@ -113,4 +113,4 @@ fi count=$(echo "$missing" | wc -l | tr -d ' ') echo "Tools in source tree but missing from toolboxes.xml ($count):" echo "" -echo "$missing" | sed 's/^/ /' \ No newline at end of file +echo "$missing" | sed 's/^/ /' From 322e62a55cf85367cfbded8182f353d081a866ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Sat, 23 May 2026 17:21:40 +0200 Subject: [PATCH 5/9] fix: Remake the script in Python --- utils/find_missing_toolboxes.py | 177 ++++++++++++++++++++++++++++++++ utils/find_missing_toolboxes.sh | 116 --------------------- 2 files changed, 177 insertions(+), 116 deletions(-) create mode 100644 utils/find_missing_toolboxes.py delete mode 100755 utils/find_missing_toolboxes.sh diff --git a/utils/find_missing_toolboxes.py b/utils/find_missing_toolboxes.py new file mode 100644 index 00000000000..23f40a1ba19 --- /dev/null +++ b/utils/find_missing_toolboxes.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 + +""" +find_missing_toolboxes.py -- Report tools present in the source tree +but absent from the GUI toolbox configuration. + +A directory is treated as a tool when its name matches the GRASS naming +convention (prefix.name) and it contains either a Python entry point +(.py) or a C entry point (main.c). + +Tools already accessible in the GUI are collected from three sources: + - entries in toolboxes.xml + - entries in wxgui_items.xml + - entries in wxgui_items.xml + +Usage: + +python utils/find_missing_toolboxes.py + +@author +""" + +import re +import sys +from pathlib import Path +from xml.etree import ElementTree as ET + +# --------------------------------------------------------------------------- +# Tools intentionally absent from the GUI menu. +# d.* Display tools are driven from the map display, not the module menu. +# g.* Internal plumbing tools not meant for direct user interaction. +# --------------------------------------------------------------------------- +EXCLUDED = { + "d.background", + "d.barscale", + "d.colorlist", + "d.colortable", + "d.correlate", + "d.erase", + "d.extract", + "d.font", + "d.fontlist", + "d.frame", + "d.geodesic", + "d.graph", + "d.grid", + "d.his", + "d.histogram", + "d.info", + "d.labels", + "d.legend", + "d.legend.vect", + "d.linegraph", + "d.mon", + "d.northarrow", + "d.out.file", + "d.path", + "d.polar", + "d.profile", + "d.rast", + "d.rast.arrow", + "d.rast.leg", + "d.rast.num", + "d.redraw", + "d.rgb", + "d.rhumbline", + "d.shade", + "d.text", + "d.title", + "d.to.rast", + "d.vect", + "d.vect.chart", + "d.vect.thematic", + "d.what.rast", + "d.what.vect", + "d.where", + "g.cairocomp", + "g.dirseps", + "g.filename", + "g.findetc", + "g.findfile", + "g.gui", + "g.html2man", + "g.message", + "g.mkfontcap", + "g.parser", + "g.pnmcomp", + "g.ppmtopng", + "g.search.modules", + "g.setproj", + "g.tempfile", + "g.version", +} + +TOOL_PATTERN = re.compile(r"^[a-z][a-z0-9]*\.[a-z][a-z0-9._]*$") + + +def find_source_tools(root: Path) -> set[str]: + """Return names of all tools found in the source tree. + + A directory qualifies when its name matches the GRASS tool naming + convention and it contains a Python entry point or a C main.c. + Searches up to three directory levels deep to catch tools nested + inside parent directories (e.g. raster/r.sim/r.sim.water). + """ + tools = set() + for depth in range(2, 5): # mindepth 2, maxdepth 4 + pattern = "/".join(["*"] * depth) + for candidate in root.glob(pattern): + if not candidate.is_dir(): + continue + if candidate.parts[len(root.parts)] == "doc": + continue + if not TOOL_PATTERN.match(candidate.name): + continue + if (candidate / f"{candidate.name}.py").exists() or ( + candidate / "main.c" + ).exists(): + tools.add(candidate.name) + return tools + + +def find_registered_tools(toolboxes: Path, wxgui_items: Path) -> set[str]: + """Return tool names already accessible somewhere in the GUI.""" + registered = set() + + tree = ET.parse(toolboxes) + for el in tree.iter("module-item"): + name = el.get("name") + if name: + registered.add(name) + + tree = ET.parse(wxgui_items) + for el in tree.iter("related-module"): + if el.text: + registered.add(el.text.strip()) + for el in tree.iter("command"): + if el.text: + # Extract only the tool name (first word before any flags). + tool = el.text.strip().split()[0] + if TOOL_PATTERN.match(tool): + registered.add(tool) + + return registered + + +def main() -> int: + root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd() + + toolboxes = root / "gui" / "wxpython" / "xml" / "toolboxes.xml" + wxgui_items = root / "gui" / "wxpython" / "xml" / "wxgui_items.xml" + + for path in (toolboxes, wxgui_items): + if not path.exists(): + print( + f"ERROR: {path} not found. Run from the repository root.", + file=sys.stderr, + ) + return 1 + + source_tools = find_source_tools(root) + registered = find_registered_tools(toolboxes, wxgui_items) + + missing = sorted(source_tools - registered - EXCLUDED) + + if not missing: + print("All source tools are registered in toolboxes.xml.") + return 0 + + print(f"Tools in source tree but missing from toolboxes.xml ({len(missing)}):\n") + for name in missing: + print(f" {name}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/utils/find_missing_toolboxes.sh b/utils/find_missing_toolboxes.sh deleted file mode 100755 index 2fd0c426526..00000000000 --- a/utils/find_missing_toolboxes.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/sh - -TOOLBOXES="gui/wxpython/xml/toolboxes.xml" - -if [ ! -f "$TOOLBOXES" ]; then - echo "ERROR: $TOOLBOXES not found. Run from the repository root." >&2 - exit 1 -fi - -WXGUI_ITEMS="gui/wxpython/xml/wxgui_items.xml" - -# Tools accessible in the GUI: direct module-item entries in toolboxes.xml, -# tools wrapped by a wxgui-item dialog, and tools invoked directly via a tag. -registered=$( - { - sed -n 's/.*\([^<]*\)<\/related-module>.*/\1/p' "$WXGUI_ITEMS" - sed -n 's/.*\([a-z][a-z0-9]*\.[a-z][a-z0-9._]*\).*/\1/p' "$WXGUI_ITEMS" - } | sort -u -) - -# Find tool directories: up to three levels deep anywhere in the tree, -# name matches the GRASS tool naming convention, and contains a Python script or C main.c. -source_tools=$( - find . -mindepth 2 -maxdepth 3 -type d -regex '.*/[a-z][a-z0-9]*\.[a-z][a-z0-9._]*' \ - | while IFS= read -r dir; do - name="${dir##*/}" - if [ -f "${dir}/${name}.py" ] || [ -f "${dir}/main.c" ]; then - echo "$name" - fi - done \ - | sort -u -) - -# Tools intentionally absent from the GUI menu. One name per line so that grep -Fxf can match exactly. -# d.* Display tools are called from the map display, not the module menu. -# g.* Internal tools that are not meant for the user to use. -excluded_list=$(cat <<'EOF' -d.background -d.barscale -d.colorlist -d.colortable -d.correlate -d.erase -d.extract -d.font -d.fontlist -d.frame -d.geodesic -d.graph -d.grid -d.his -d.histogram -d.info -d.labels -d.legend -d.legend.vect -d.linegraph -d.mon -d.northarrow -d.out.file -d.path -d.polar -d.profile -d.rast -d.rast.arrow -d.rast.leg -d.rast.num -d.redraw -d.rgb -d.rhumbline -d.shade -d.text -d.title -d.to.rast -d.vect -d.vect.chart -d.vect.thematic -d.what.rast -d.what.vect -d.where -g.cairocomp -g.dirseps -g.filename -g.findetc -g.findfile -g.gui -g.html2man -g.message -g.mkfontcap -g.parser -g.pnmcomp -g.ppmtopng -g.search.modules -g.setproj -g.tempfile -g.version -EOF -) - -# Remove excluded tools, then remove already-registered tools. -missing=$( - echo "$source_tools" \ - | grep -Fxvf <(echo "$excluded_list") \ - | grep -Fxvf <(echo "$registered") -) - -if [ -z "$missing" ]; then - echo "All source tools are registered in toolboxes.xml." - exit 0 -fi - -count=$(echo "$missing" | wc -l | tr -d ' ') -echo "Tools in source tree but missing from toolboxes.xml ($count):" -echo "" -echo "$missing" | sed 's/^/ /' From a94d13ecff4cc62b4d34b5eb2acbf3795ef25478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Sat, 23 May 2026 17:33:29 +0200 Subject: [PATCH 6/9] feat: Add toolboxes to Other category --- gui/wxpython/xml/module_tree.xml | 1 + gui/wxpython/xml/toolboxes.xml | 40 +++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/gui/wxpython/xml/module_tree.xml b/gui/wxpython/xml/module_tree.xml index edc2e2ae6c5..3b3b29a4668 100644 --- a/gui/wxpython/xml/module_tree.xml +++ b/gui/wxpython/xml/module_tree.xml @@ -13,6 +13,7 @@ + diff --git a/gui/wxpython/xml/toolboxes.xml b/gui/wxpython/xml/toolboxes.xml index d6c26c00175..ee7119236fb 100644 --- a/gui/wxpython/xml/toolboxes.xml +++ b/gui/wxpython/xml/toolboxes.xml @@ -82,6 +82,9 @@ + + + @@ -577,6 +580,17 @@ + + + + + + + + + + + @@ -809,9 +823,6 @@ - - - @@ -2211,4 +2222,27 @@ + + + + + + + + + + + + + + + + + + + + + + + From a81d951733b3f2872794a65717b6dc2a835f16ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Wed, 27 May 2026 17:06:14 +0200 Subject: [PATCH 7/9] feat: Ignore all d modules --- utils/find_missing_toolboxes.py | 65 +++++++++++---------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/utils/find_missing_toolboxes.py b/utils/find_missing_toolboxes.py index 23f40a1ba19..d968472d939 100644 --- a/utils/find_missing_toolboxes.py +++ b/utils/find_missing_toolboxes.py @@ -31,49 +31,7 @@ # g.* Internal plumbing tools not meant for direct user interaction. # --------------------------------------------------------------------------- EXCLUDED = { - "d.background", - "d.barscale", - "d.colorlist", - "d.colortable", - "d.correlate", - "d.erase", - "d.extract", - "d.font", - "d.fontlist", - "d.frame", - "d.geodesic", - "d.graph", - "d.grid", - "d.his", - "d.histogram", - "d.info", - "d.labels", - "d.legend", - "d.legend.vect", - "d.linegraph", - "d.mon", - "d.northarrow", - "d.out.file", - "d.path", - "d.polar", - "d.profile", - "d.rast", - "d.rast.arrow", - "d.rast.leg", - "d.rast.num", - "d.redraw", - "d.rgb", - "d.rhumbline", - "d.shade", - "d.text", - "d.title", - "d.to.rast", - "d.vect", - "d.vect.chart", - "d.vect.thematic", - "d.what.rast", - "d.what.vect", - "d.where", + "d.*", "g.cairocomp", "g.dirseps", "g.filename", @@ -144,6 +102,23 @@ def find_registered_tools(toolboxes: Path, wxgui_items: Path) -> set[str]: return registered +def is_excluded(name: str) -> bool: + """Return True if *name* matches any entry in EXCLUDED. + + Entries ending with '.*' are treated as prefix wildcards: + 'g.*' matches any name beginning with 'g.'. + All other entries are matched literally. + """ + for pattern in EXCLUDED: + if pattern.endswith(".*"): + prefix = pattern[:-1] # strip the '*', keep 'g.' + if name.startswith(prefix): + return True + elif name == pattern: + return True + return False + + def main() -> int: root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd() @@ -161,7 +136,9 @@ def main() -> int: source_tools = find_source_tools(root) registered = find_registered_tools(toolboxes, wxgui_items) - missing = sorted(source_tools - registered - EXCLUDED) + missing = sorted( + name for name in source_tools - registered if not is_excluded(name) + ) if not missing: print("All source tools are registered in toolboxes.xml.") From c65a5b80d419c51ded9ca2b02614c289797bbfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Wed, 27 May 2026 17:07:21 +0200 Subject: [PATCH 8/9] feat: Miscellaneous searchable and removed from topbar --- gui/wxpython/xml/module_tree.xml | 2 +- gui/wxpython/xml/toolboxes.xml | 36 +++++++++----------------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/gui/wxpython/xml/module_tree.xml b/gui/wxpython/xml/module_tree.xml index 3b3b29a4668..45b06cf71fb 100644 --- a/gui/wxpython/xml/module_tree.xml +++ b/gui/wxpython/xml/module_tree.xml @@ -13,7 +13,7 @@ - + diff --git a/gui/wxpython/xml/toolboxes.xml b/gui/wxpython/xml/toolboxes.xml index ee7119236fb..9cdd5371923 100644 --- a/gui/wxpython/xml/toolboxes.xml +++ b/gui/wxpython/xml/toolboxes.xml @@ -27,27 +27,8 @@ - - - - - - - - - - - - - - - - - - - @@ -2222,23 +2203,26 @@ - - + + + + + - - - - - + + + + + From c95714b69bd58fc76c21581712fe01c9f71d6967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Gureck=C3=BD?= Date: Thu, 18 Jun 2026 18:08:44 +0200 Subject: [PATCH 9/9] fix: Remove deprecated tools --- gui/wxpython/xml/toolboxes.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gui/wxpython/xml/toolboxes.xml b/gui/wxpython/xml/toolboxes.xml index 9cdd5371923..9eff9131984 100644 --- a/gui/wxpython/xml/toolboxes.xml +++ b/gui/wxpython/xml/toolboxes.xml @@ -565,9 +565,6 @@ - - - @@ -674,9 +671,6 @@ - - -