Skip to content

win: Fix standalone installer MSVC runtime and build the installer in CI - #664

Open
wenzeslaus wants to merge 28 commits into
echoix:mainfrom
wenzeslaus:fix-msvc-runtime-dll-sizes
Open

wenzeslaus wants to merge 28 commits into
echoix:mainfrom
wenzeslaus:fix-msvc-runtime-dll-sizes

Conversation

@wenzeslaus

Copy link
Copy Markdown

This fixes the standalone Windows installer and adds a CI workflow that builds it, so the fix (and future installer changes) can be tested from CI artifacts.

Installer fix (mswindows/GRASS-Installer.nsi.tmpl)

  • Update the MSVC runtime archive to msvcrt2019-14.2-2.tar.bz2 as published on download.osgeo.org, and correct the download size (920 KB, verified against the server) and unpacked size shown by the installer.
  • Run g.mkfontcap in a new hidden -RunMkFontCap section placed after the Microsoft runtime section, so the runtime DLLs it needs are already installed when it runs. Previously it ran at the end of the GRASS section, before the runtime download, which is part of [Bug] install of WinGRASS-8.5dev-a277d8547c-18-Setup VCRUNTIME140.dll not found error OSGeo/grass#5663.

CI workflow (.github/workflows/windows-installer.yml)

New workflow that builds the NSIS standalone installer and uploads WinGRASS-*-Setup.exe as an artifact (7-day retention). It mirrors the production flow of wingrass-maintenance-scripts:

  1. Same MSYS2/OSGeo4W setup as the OSGeo4W workflow, but calling mswindows/osgeo4w/build_osgeo4w.sh directly to skip the UNITTEST additions that would otherwise end up in the package.
  2. make install into C:\OSGeo4W\apps\grass\grassXY plus the packaging fixups from mswindows/osgeo4w/package.sh (config.h switch, etc/grass.py -> etc/grassXY.py rename that the generated launcher depends on, MinGW runtime DLLs copied into the package bin). The MSVC .lib generation is skipped as it is only needed for the OSGeo4W package.
  3. GRASS-Packager.bat, followed by a content sanity check (xcopy failures do not stop the batch file).
  4. makensis with the AccessControl and untgz plugins (not bundled with NSIS; downloaded with pinned SHA256 checksums, unicode DLLs installed into Plugins\x86-unicode as described in the Trac wiki).

The workflow triggers on changes under mswindows/**, changes to the workflow itself, and manual dispatch. Note that the makensis step uses the solid LZMA compression from the installer script and may take a while on a hosted runner.

The CI workflow was written with the assistance of Claude Code.

🤖 Generated with Claude Code

…stall

The msvcrt2019 archive published on download.osgeo.org changed to
msvcrt2019-14.2-2.tar.bz2; update the archive name and the download and
unpacked sizes reported by the installer to match it.

Run g.mkfontcap in a hidden section placed after the Microsoft runtime
section so that the runtime DLLs it needs are already installed when it
runs (OSGeo#5663).
Build the NSIS-based standalone installer and upload it as an artifact
so that changes to the installer can be tested without a local
MSYS2/OSGeo4W setup. The job mirrors the flow used by
wingrass-maintenance-scripts: build with the OSGeo4W scripts, make
install into the OSGeo4W tree with the packaging fixups from
mswindows/osgeo4w/package.sh, run GRASS-Packager.bat, and compile
GRASS-Installer.nsi with the AccessControl and untgz NSIS plugins.

The workflow runs only for changes under mswindows or to the workflow
itself, and on manual dispatch.

Written with the assistance of Claude Code.
echoix and others added 2 commits July 12, 2026 14:35
The OSGeo4W CI build script removes etc/fontcap from the dist tree so
that the test tree regenerates it on first run, but make install
requires the file and has no rule to rebuild it from the top-level
Makefile. Rebuild it with the g.mkfontcap Makefile before make install.

Also set PYTHONHOME like the build script does so the OSGeo4W Python
used by make install finds its standard library.
@echoix

echoix commented Jul 13, 2026

Copy link
Copy Markdown
Owner

The install went ok. I have some screenshots, but some are missing because I mistakenly made them inside the trimmed down Windows Sandbox, which didn't save them.

Screenshots:
Capture d’écran 2026-07-13 093612

Capture d’écran 2026-07-13 093629 Capture d’écran 2026-07-13 093643 Capture d’écran 2026-07-13 093751 Capture d’écran 2026-07-13 094319 Capture d’écran 2026-07-13 100225

@echoix

echoix commented Jul 13, 2026

Copy link
Copy Markdown
Owner

I checked the box to open GRASS after the installation is done on the installer. It didn't open grass. I double-clicked on the shortcut, and it didn't open grass either. I called the same thing that the shortcut does inside a terminal, and I saw the following:

C:\Users\WDAGUtilityAccount>"C:\Program Files\GRASS 8.6\grass86.bat" --gui
Traceback (most recent call last):
  File "C:\Program Files\GRASS 8.6\etc\grass86.py", line 2537, in <module>
    main()
  File "C:\Program Files\GRASS 8.6\etc\grass86.py", line 2178, in main
    find_grass_python_package()
  File "C:\Program Files\GRASS 8.6\etc\grass86.py", line 2127, in find_grass_python_package
    raise RuntimeError(msg)
RuntimeError: \c\OSGeo4W\apps\grass\grass86\etc\python with the grass Python package does not exist. Is the installation of GRASS complete?

We don't have a "C:\OSGeo4W" folder either, the standalone instal installs in "C:\Program Files\GRASS 8.6". The install seems fine, and contain the grass package:
Capture d’écran 2026-07-13 101036
Capture d’écran 2026-07-13 101112
Capture d’écran 2026-07-13 101130

@ant9000

ant9000 commented Jul 14, 2026

Copy link
Copy Markdown

Modified etc/env.bat, adding:

set GRASS_PYDIR=%GISBASE%\etc\python

and now Grass correctly starts.

…er, making grassXY.py able to start

Co-authored-by: Antonio Galea <antonio.galea@gmail.com>
Comment thread .github/workflows/windows-installer.yml Outdated
Valyrian-Code and others added 18 commits July 14, 2026 17:25
Migrates the r.null testsuite from gunittest to pytest. Everything goes through grass.tools (r.describe results read with .json, no gs.parse_command). The three checks from the old testsuite are preserved: setnull on an integer map with -i, setnull on a floating point map with -f, and filling NULL cells with the null parameter.
CodeQL Code Quality had some AI findings on a recently changed file, a test file for g.md2man. The tests suggested seemed legitimate, and they are passing. I am thus submitting them here, after testing them in my fork.

Original text explaining the findings: 

-------

The test asserts that there are exactly 3 bullet list markers for the input `"- first\n- second\n - nested\n"`, but this input only has 3 items (first, second, nested). The assertion `== 3` may be correct, but it should be verified that the nested item also produces the same `.IP \(bu 4n` marker, since nested items could produce a different marker format. If the nested list intentionally uses the same marker, this is fine, but the test name `test_unordered_list_nested` suggests the nesting behavior is what's being tested, and there is no assertion verifying that the nested item is actually rendered as nested (e.g., inside `.RS`/`.RE` blocks relative to the parent items). Consider adding an assertion that checks the order of `.RS`, `.IP`, and `.RE` to confirm correct nesting structure.


----------

The `test_comment_parsing_unit` test covers multi-line and inline comments, but there is no unit test for `strip_html_comments` behavior inside a fenced code block (i.e., that a comment inside a fence is preserved). The integration test `test_comment_kept_in_fenced_code` covers this via subprocess, but a direct unit test of `strip_html_comments` with a fenced block containing a comment would provide more precise coverage of that branch in `gmd.py`.

* Apply suggested fix to utils/g.md2man/tests/g_md2man_test.py from Copilot Autofix

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Apply suggested fix to utils/g.md2man/tests/g_md2man_test.py from Copilot Autofix

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Apply ruff format

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Fix the today config type, skip PLY lexer docstrings in autodoc, remove
the unsupported logo theme option, correct the toctree entries, and make
the sample data links work again.

The initial changes were AI-assisted (OpenCode with DeepSeek), the review
and follow-up fixes used Claude Code (with Fable).

---------

Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com>
Co-authored-by: Vaclav Petras <wenzeslaus@gmail.com>
…OSGeo#7672)

* set sqlite as default backend on init

* read tgis_db_version from globals if no DB is initialized

* add tests for STDS construction
…OSGeo#7337)

* move to Tools API

* add upper/lower filter range options) (backwards compatible) for both vector and raster based filtering

* propagate options of underlying tools (r.clump)

* add tests

Co-authored-by: Markus Neteler <markus@neteler.org>

* Edit html

* Unchange header line

* typos

---------

Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com>
Co-authored-by: Markus Neteler <markus@neteler.org>
…e function" (OSGeo#7673)

Follow-up from OSGeo#7660

This PR applies the same logic as the other PR, where how sscanf can also return EOF "if input failure occurs before the first receiving argument was assigned". (See https://en.cppreference.com/c/io/fscanf).

Here, the little touch that is different for `raster/r.out.vtk/writeascii.c` and `raster3d/r3.out.vtk/writeVTKData.c`, is that I assume that if there's the `EOF` case, or any invalid case for the `null_value`, we would still want the null value to be 0. I think that's the design intention behind the code.


This was assisted by AI (Copilot-generated fix suggestions for code scanning alerts), but we can reason through it and I applied some myself in order to batch them in one single PR.

This should fix:
- https://github.com/OSGeo/grass/security/code-scanning/525
- https://github.com/OSGeo/grass/security/code-scanning/526
- https://github.com/OSGeo/grass/security/code-scanning/527
- https://github.com/OSGeo/grass/security/code-scanning/528
- https://github.com/OSGeo/grass/security/code-scanning/531
- https://github.com/OSGeo/grass/security/code-scanning/532
- https://github.com/OSGeo/grass/security/code-scanning/533


* Potential fix for code scanning alert no. 525: Incorrect return-value check for a 'scanf'-like function

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Potential fix for code scanning alert no. 528: Incorrect return-value check for a 'scanf'-like function

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Update writeascii.c

* r3.out.vtk: Fix "Incorrect return-value check for a 'scanf'-like function" for null value

* r.watershed: Fix "Incorrect return-value check for a 'scanf'-like function" for disturbed_land default value

Adresses https://github.com/OSGeo/grass/security/code-scanning/532

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
We observed some random, transient timeouts on different tests. For one of them, t.list, there was no reason to fail on macOS, where there are 3 cores instead of 2 in GitHub Actions. There's a possibility that there's a problem with the temporal library locking up somewhere, but there’s also a small chance that the r.mapcalc calls are now using multiple processors. It "costs" not much to add this env var so that the number of threads is limited, even if it doesn't fix all problems; it might just fix some potential ones, since we are already running in parallel in that section.
AI findings on a recently modified file found a typo in a translated string. This adds "be" in "Values should be in between"...

Apply suggested fix to raster3d/r3.out.vtk/writeVTKData.c from Copilot Autofix

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
The ctypes interface assigned the number of columns to the rows key and
the number of rows to the cols key, for raster as well as 3D raster maps.
The swapped values are stored in the temporal database when a map is
registered and are reported by t.info, which therefore disagreed with
r.info and r3.info. The number of cells was not affected, because it is
the product of both.

Maps registered before this fix keep the swapped values in the temporal
database until they are registered again.

Update the doctests which asserted the swapped values and add a test
comparing the metadata read through the interface with the output of
r.info and r3.info.

The test was written with the help of Claude Code.
Python 3.10 receives security fixes only and reaches end of life in
October 2026, before the next release. Per RFC 8, raise the minimum
supported Python version from 3.10 to 3.11.

Update the version in REQUIREMENTS.md, pyproject.toml (requires-python,
Black, Pylint, Pyright, and isort) and CI (pytest matrix and
MIN_PYTHON_VERSION).

Update places in code which add code for the older version:

- utils/mkdocs.py: datetime.fromisoformat parses the Z suffix used by
  recent Git natively since 3.11, so the fallback is no longer needed.
- Use the datetime.UTC alias added in 3.11. Ruff enables this check
  (UP017) once it infers the higher target version from requires-python.
- Drop the toml extra for bandit. It provided a TOML parser for reading
  the bandit configuration from pyproject.toml, which the standard
  library covers since 3.11 with tomllib. Here the deciding version is
  the one the workflow runs, which is well past 3.11 already.
* CMake: Build man pages from markdown
Generate the markdown documentation (--md-description + mkmarkdown.py)
in all three doc paths (modules, GUI, scripts) and convert man pages
from it with g.md2man instead of from HTML with g.html2man, mirroring
the Autotools build. Install g.md2man.py and gmd.py, and install the
generated docs/mkdocs tree, which was previously not installed at all.

Add man/build_manpages.py, a sweep run after the index scripts that
converts any markdown page without an up-to-date man page. It ports
man/Makefile's MANPAGES wildcard to CMake, which has no build-time
glob, and produces the index and dynamically named topic_* man pages
that the CMake build previously did not ship.

Run the doc generators in the tool source directory like Autotools
does. This fixes the SOURCE CODE footer in all CMake-built pages,
which previously pointed to a nonexistent build-directory path in the
repository and fell back to an Accessed date instead of the last
commit, and makes the copy-source-into-build-dir steps unnecessary.
Also fix build_class.py receiving the documentation directory as its
year argument.

Verified against an Autotools build: g.md2man output for the same
generated markdown is byte-identical.

Generated with AI assistance (Claude Fable), including the build
verification against the Autotools doc tree.

* address review
The table connection raised a new ImportError with the hint as its message,
so the message of the original error, which says which module is missing,
was replaced by the hint.

Add the hint as a note to the original error instead. From the Python
documentation of BaseException.add_note(): Add the string note to the
exception's notes which appear in the standard traceback after the exception
string. The traceback then shows both the missing module and the hint.

Exception notes need Python 3.11, which is now the minimum version.
The test loader raised a new ImportError to say which module could not be
imported. The message used e.message, which does not exist in Python 3, so
the handler raised AttributeError instead of the intended error.

Add the module name as a note to the original error instead. From the Python
documentation of BaseException.add_note(): Add the string note to the
exception's notes which appear in the standard traceback after the exception
string. The traceback then shows both the import error and the module name.

Exception notes need Python 3.11, which is now the minimum version.
)

An empty WHERE result left an empty data array, which the classification
algorithms then indexed at data[0] and data[count - 1]. The values printed
as class breaks were whatever the heap happened to hold, which is zeroed
on Linux but not on Windows, making the test flaky there.

Warn the user and keep the zero-initialized breaks when nothing is selected, and
reject an empty array in AS_class_apply_algorithm.
The Scatterplot, Timeline, and Temporal Plot tools raised a new ImportError
with the hint as its message, so the message of the original error, which
says which module is actually missing, was replaced by the hint.

Add the hint as a note to the original error instead. From the Python
documentation of BaseException.add_note(): Add the string note to the
exception's notes which appear in the standard traceback after the exception
string. The traceback then shows both the missing module and the hint.

The consumer code (various places) shows always its own message, so the
not, not the original message is not used. These changes do not change that.  

Exception notes need Python 3.11, which is now the minimum version.
…7697)

locale.getencoding() was added in Python 3.11, so the fallbacks to the
deprecated locale.getdefaultlocale() are never used now that 3.11 is the
minimal supported version. Call locale.getencoding() directly.

The two places calling locale.getlocale() get the same treatment. That
function is available in all supported versions, so those fallbacks could
not be reached even before.
echoix and others added 3 commits July 14, 2026 17:38
…aking grassXY.py able to start

Co-authored-by: Antonio Galea <antonio.galea@gmail.com>
… installer, making grassXY.py able to start"

This reverts commit 0b77028.
@ant9000

ant9000 commented Jul 15, 2026

Copy link
Copy Markdown

Just tested latest installer - confirmed working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants