From b8e081fc0a2d90bf4c65d077a4087e4de6c20c2a Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Wed, 26 Mar 2025 11:43:47 +0100 Subject: [PATCH 1/7] build: new specific env vars for FHS, replacing GISBASE --- CMakeLists.txt | 49 ++++++++++----- cmake/modules/GRASSInstallDirs.cmake | 41 ++++++++++-- display/CMakeLists.txt | 26 +++++--- display/d.linegraph/main.c | 4 +- display/d.mon/start.c | 12 ++-- display/d.vect.thematic/main.c | 4 +- display/d.vect/main.c | 4 +- doc/examples/gui/wxpython/frame.py | 2 +- doc/examples/gui/wxpython/g.gui.example.py | 2 +- general/g.gui/main.c | 8 ++- general/g.list/main.c | 2 +- general/g.mapset/main.c | 4 +- general/g.mapsets/main.c | 10 ++- general/g.mkfontcap/Makefile | 3 +- general/g.mkfontcap/main.c | 3 +- general/g.mkfontcap/stroke_fonts.c | 5 +- general/g.setproj/get_stp.c | 4 +- general/g.setproj/local_proto.h | 4 +- general/g.setproj/proj.c | 6 +- gui/scripts/d.wms.py | 2 +- gui/wxpython/core/globalvar.py | 11 ++-- gui/wxpython/core/settings.py | 2 +- gui/wxpython/core/toolboxes.py | 2 +- gui/wxpython/gui_core/ghelp.py | 20 +++--- gui/wxpython/gui_core/widgets.py | 8 +-- gui/wxpython/image2target/ii2t_gis_set.py | 6 +- gui/wxpython/lmgr/frame.py | 5 +- gui/wxpython/main_window/frame.py | 5 +- gui/wxpython/psmap/dialogs.py | 10 +-- gui/wxpython/web_services/cap_interface.py | 2 +- gui/wxpython/web_services/widgets.py | 2 +- include/Make/Install.make | 17 ++++- include/Make/Rules.make | 11 ++++ include/grass/defs/gis.h | 7 +++ include/grass/gprojects.h | 8 +-- lib/driver/font2.c | 4 +- lib/driver/parse_ftcap.c | 2 +- lib/gis/color_rules.c | 7 +-- lib/gis/datum.c | 4 +- lib/gis/find_etc.c | 2 +- lib/gis/get_ellipse.c | 2 +- lib/gis/is.c | 2 +- lib/gis/locale.c | 9 +-- lib/gis/parser.c | 10 ++- lib/gis/resource_dirs.c | 53 ++++++++++++++++ lib/init/Makefile | 13 +--- lib/init/grass.py | 6 +- lib/manage/read_list.c | 2 +- lib/proj/convert.c | 13 ++-- lib/proj/datum.c | 4 +- lib/proj/ellipse.c | 2 +- lib/psdriver/graph_set.c | 3 +- lib/raster/color_rules.c | 2 +- lib/symbol/read.c | 2 +- locale/Makefile | 2 +- locale/grass_po_stats.py | 2 +- man/Makefile | 21 +++++-- man/build_html.py | 2 +- man/build_md.py | 2 +- man/build_rest.py | 4 +- ps/ps.map/makeprocs.c | 2 +- python/grass/__init__.py | 3 +- python/grass/app/Makefile | 12 ++++ python/grass/app/data.py | 11 ++-- python/grass/app/resource_paths.py | 13 ++++ python/grass/app/runtime.py | 63 +++++++++++++++++++ python/grass/docs/conf.py | 40 ++++-------- python/grass/script/setup.py | 22 ++++--- python/grass/script/task.py | 4 +- python/grass/script/utils.py | 4 +- python/grass/semantic_label/reader.py | 2 +- python/grass/temporal/core.py | 4 +- .../CMakeLists.txt | 1 - raster/r.colors/edit_colors.c | 4 +- raster/r.watershed/front/main.c | 2 +- raster/r.watershed/shed/com_line.c | 10 +-- scripts/d.polar/d.polar.py | 2 +- scripts/db.test/db.test.py | 2 +- scripts/g.extension/g.extension.py | 2 +- scripts/g.search.modules/g.search.modules.py | 6 +- scripts/wxpyimgview/wxpyimgview.py | 3 +- utils/thumbnails.py | 2 +- vector/v.label.sa/font.c | 2 +- 83 files changed, 454 insertions(+), 248 deletions(-) mode change 100644 => 100755 doc/examples/gui/wxpython/g.gui.example.py mode change 100644 => 100755 gui/scripts/d.wms.py create mode 100644 lib/gis/resource_dirs.c mode change 100644 => 100755 locale/grass_po_stats.py mode change 100644 => 100755 man/build_rest.py mode change 100644 => 100755 scripts/g.extension/g.extension.py mode change 100644 => 100755 scripts/wxpyimgview/wxpyimgview.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 1327577f0f6..d4d454a6551 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,18 +184,9 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) if(WITH_FHS) file(CREATE_LINK ${OUTDIR}/${GRASS_INSTALL_DEMODIR} ${RUNTIME_GISBASE}/demolocation SYMBOLIC) - file(CREATE_LINK ${OUTDIR}/${GRASS_INSTALL_FONTSDIR} ${RUNTIME_GISBASE}/fonts - SYMBOLIC) - file(CREATE_LINK ${OUTDIR}/${GRASS_INSTALL_ETCDIR}/colors - ${RUNTIME_GISBASE}/etc/colors SYMBOLIC) - file(CREATE_LINK ${OUTDIR}/${GRASS_INSTALL_ETCDIR}/colors.desc - ${RUNTIME_GISBASE}/etc/colors.desc SYMBOLIC) - file(CREATE_LINK ${OUTDIR}/${GRASS_INSTALL_ETCDIR}/element_list - ${RUNTIME_GISBASE}/etc/element_list SYMBOLIC) - file(CREATE_LINK ${OUTDIR}/${GRASS_INSTALL_ETCDIR}/renamed_options - ${RUNTIME_GISBASE}/etc/renamed_options SYMBOLIC) - file(CREATE_LINK ${OUTDIR}/${GRASS_INSTALL_ETCDIR}/VERSIONNUMBER - ${RUNTIME_GISBASE}/etc/VERSIONNUMBER SYMBOLIC) + file(MAKE_DIRECTORY "${RUNTIME_GISBASE}/gui/wxpython") + file(CREATE_LINK ${OUTDIR}/${GRASS_INSTALL_GUIDIR}/wxpython/xml + ${RUNTIME_GISBASE}/gui/wxpython/xml SYMBOLIC) endif() include(set_compiler_flags) @@ -233,6 +224,16 @@ if(WIN32) ${CMAKE_COMMAND} -E env "PATH=${BIN_DIR}${sep}${SCRIPTS_DIR}${sep}${env_path}${sep}${LIB_DIR}" "PYTHONPATH=${ETC_PYTHON_DIR}${sep}${GUI_WXPYTHON_DIR}${sep}$ENV{PYTHONPATH}" "GRASS_PYTHON=${PYTHON_EXECUTABLE}" "GISBASE=${RUN_GISBASE_NATIVE}" "GISRC=${GISRC}" "LC_ALL=C" "LANG=C" + "GRASS_COLORSDIR=${RUNTIME_GRASS_COLORSDIR}" + "GRASS_DOCDIR=${RUNTIME_GRASS_DOCDIR}" + "GRASS_ETCDIR=${RUNTIME_GRASS_ETCDIR}" + "GRASS_FONTSDIR=${RUNTIME_GRASS_FONTSDIR}" + "GRASS_GRAPHICSDIR=${RUNTIME_GRASS_GRAPHICSDIR}" + "GRASS_GUIRESDIR=${RUNTIME_GRASS_GUIRESDIR}" + "GRASS_GUISCRIPTDIR=${RUNTIME_GRASS_GUISCRIPTDIR}" + "GRASS_GUIWXDIR=${GUI_WXPYTHON_DIR}" + "GRASS_LOCALEDIR=${RUNTIME_GRASS_LOCALEDIR}" + "GRASS_MKDOCSDIR=${RUNTIME_GRASS_MKDOCSDIR}" "ARCH=${BUILD_ARCH}" "ARCH_DISTDIR=${RUN_GISBASE_NATIVE}" "LANGUAGE=C" "MODULE_TOPDIR=${MODULE_TOPDIR}" "HTMLDIR=${DOC_DIR}" "LC_ALL=C" "LANG=C" @@ -249,6 +250,16 @@ else() "PYTHONPATH=${ETC_PYTHON_DIR}${sep}${GUI_WXPYTHON_DIR}${sep}$ENV{PYTHONPATH}" "LD_LIBRARY_PATH=${LIB_DIR}${sep}$ENV{LD_LIBRARY_PATH}" "GISBASE=${RUN_GISBASE_NATIVE}" "GISRC=${GISRC}" "LC_ALL=C" "LANG=C" + "GRASS_COLORSDIR=${RUNTIME_GRASS_COLORSDIR}" + "GRASS_DOCDIR=${RUNTIME_GRASS_DOCDIR}" + "GRASS_ETCDIR=${RUNTIME_GRASS_ETCDIR}" + "GRASS_FONTSDIR=${RUNTIME_GRASS_FONTSDIR}" + "GRASS_GRAPHICSDIR=${RUNTIME_GRASS_GRAPHICSDIR}" + "GRASS_GUIRESDIR=${RUNTIME_GRASS_GUIRESDIR}" + "GRASS_GUISCRIPTDIR=${RUNTIME_GRASS_GUISCRIPTDIR}" + "GRASS_GUIWXDIR=${GUI_WXPYTHON_DIR}" + "GRASS_LOCALEDIR=${RUNTIME_GRASS_LOCALEDIR}" + "GRASS_MKDOCSDIR=${RUNTIME_GRASS_MKDOCSDIR}" "ARCH=${BUILD_ARCH}" "ARCH_DISTDIR=${RUN_GISBASE_NATIVE}" "LANGUAGE=C" "MODULE_TOPDIR=${MODULE_TOPDIR}" "HTMLDIR=${DOC_DIR}" "VERSION_NUMBER=\"${GRASS_VERSION_NUMBER}\"" @@ -304,14 +315,18 @@ endif() add_custom_target( r_colors_thumbnails ALL COMMAND ${CMAKE_COMMAND} -E make_directory - ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/colortables + ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/colortables COMMAND ${grass_env_command} ${PYTHON_EXECUTABLE} ${THUMBNAILS_PY} - ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/colortables - BYPRODUCTS ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/colortables + ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/colortables + BYPRODUCTS ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/colortables COMMENT "Creating thumbnails" DEPENDS ALL_MODULES) -install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/colortables - DESTINATION ${GRASS_INSTALL_DOCDIR}) +install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/colortables + DESTINATION ${GRASS_INSTALL_GRAPHICSDIR}) +if(WITH_FHS AND WITH_DOCS) + install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/colortables + DESTINATION ${GRASS_INSTALL_DOCDIR}) +endif() set(misc_files AUTHORS diff --git a/cmake/modules/GRASSInstallDirs.cmake b/cmake/modules/GRASSInstallDirs.cmake index ce40fc03c35..fafbca66626 100644 --- a/cmake/modules/GRASSInstallDirs.cmake +++ b/cmake/modules/GRASSInstallDirs.cmake @@ -22,12 +22,14 @@ if(WITH_FHS) set(GRASS_INSTALL_DEVDOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME_LOWER}-dev-doc") set(GRASS_INSTALL_MANDIR "${CMAKE_INSTALL_MANDIR}") - set(GRASS_INSTALL_MKDOCSDIR - "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME_LOWER}-mkdocs") - set(GRASS_INSTALL_DEMODIR "${GRASS_INSTALL_SHAREDIR}/demolocation") + set(GRASS_INSTALL_MKDOCSDIR "${GRASS_INSTALL_SHAREDIR}/mkdocs") set(GRASS_INSTALL_MISCDIR "${GRASS_INSTALL_SHAREDIR}") + set(GRASS_INSTALL_DEMODIR "${GRASS_INSTALL_MISCDIR}/demolocation") set(GRASS_INSTALL_MAKEFILEDIR "${GISBASE_DIR}/Make") set(GRASS_INSTALL_LOCALEDIR "${CMAKE_INSTALL_LOCALEDIR}") + set(GRASS_INSTALL_COLORSDIR "${GRASS_INSTALL_ETCDIR}/colors") + set(GRASS_INSTALL_GRAPHICSDIR "${GRASS_INSTALL_ETCDIR}/graphics") + set(GRASS_INSTALL_GRASS_GUIWXDIR "${GRASS_INSTALL_GUIDIR}/wxpython") else() message("Legacy file structure") set(GISBASE_DIR "${CMAKE_INSTALL_LIBDIR}/grass${GRASS_VERSION_MAJOR}${GRASS_VERSION_MINOR}") @@ -48,10 +50,13 @@ else() set(GRASS_INSTALL_DEVDOCDIR "${GISBASE_DIR}/html") set(GRASS_INSTALL_MANDIR "${GISBASE_DIR}/docs/man") set(GRASS_INSTALL_MKDOCSDIR "${GISBASE_DIR}/docs/mkdocs") - set(GRASS_INSTALL_DEMODIR "${GISBASE_DIR}/demolocation") set(GRASS_INSTALL_MISCDIR "${GISBASE_DIR}") + set(GRASS_INSTALL_DEMODIR "${GRASS_INSTALL_MISCDIR}/demolocation") set(GRASS_INSTALL_MAKEFILEDIR "${GISBASE_DIR}/Make") set(GRASS_INSTALL_LOCALEDIR "${GISBASE_DIR}/locale") + set(GRASS_INSTALL_COLORSDIR "${GRASS_INSTALL_ETCDIR}/colors") + set(GRASS_INSTALL_GRAPHICSDIR "${GRASS_INSTALL_DOCDIR}") + set(GRASS_INSTALL_GRASS_GUIWXDIR "${GRASS_INSTALL_GUIDIR}/wxpython") endif() message(STATUS "GISBASE_DIR ${GISBASE_DIR}") @@ -76,10 +81,38 @@ message(STATUS "GRASS_INSTALL_DEMODIR ${GRASS_INSTALL_DEMODIR}") message(STATUS "GRASS_INSTALL_MISCDIR ${GRASS_INSTALL_MISCDIR}") message(STATUS "GRASS_INSTALL_MAKEFILEDIR ${GRASS_INSTALL_MAKEFILEDIR}") message(STATUS "GRASS_INSTALL_LOCALEDIR ${GRASS_INSTALL_LOCALEDIR}") +message(STATUS "GRASS_INSTALL_COLORSDIR ${GRASS_INSTALL_COLORSDIR}") +message(STATUS "GRASS_INSTALL_GRAPHICSDIR ${GRASS_INSTALL_GRAPHICSDIR}") +message(STATUS "GRASS_INSTALL_GRASS_GUIWXDIR ${GRASS_INSTALL_GRASS_GUIWXDIR}") set(OUTDIR "${CMAKE_BINARY_DIR}/output") set(GISBASE ${CMAKE_INSTALL_PREFIX}/${GISBASE_DIR}) + +set(GRASS_LOCALEDIR "${GRASS_INSTALL_LOCALEDIR}") +set(GRASS_GUIRESDIR "${GRASS_INSTALL_GUIDIR}") +set(GRASS_GUISCRIPTDIR "${GRASS_INSTALL_GUISCRIPTDIR}") +set(GRASS_DOCDIR "${GRASS_INSTALL_DOCDIR}") +set(GRASS_MKDOCSDIR "${GRASS_INSTALL_MKDOCSDIR}") +set(GRASS_FONTSDIR "${GRASS_INSTALL_FONTSDIR}") +set(GRASS_COLORSDIR "${GRASS_INSTALL_COLORSDIR}") +set(GRASS_GRAPHICSDIR "${GRASS_INSTALL_GRAPHICSDIR}") +set(GRASS_ETCDIR "${GRASS_INSTALL_ETCDIR}") +set(GRASS_ETCBINDIR "${GRASS_INSTALL_ETCBINDIR}") +set(GRASS_PYDIR "${GRASS_INSTALL_PYDIR}") +set(GRASS_GUIWXDIR "${GRASS_INSTALL_GRASS_GUIWXDIR}") +set(GRASS_MISCDIR "${GRASS_INSTALL_MISCDIR}") + set(RUNTIME_GISBASE "${OUTDIR}/${GISBASE_DIR}") +set(RUNTIME_GRASS_LOCALEDIR "${OUTDIR}/${GRASS_INSTALL_LOCALEDIR}") +set(RUNTIME_GRASS_GUIRESDIR "${OUTDIR}/${GRASS_INSTALL_GUIDIR}") +set(RUNTIME_GRASS_GUISCRIPTDIR "${OUTDIR}/${GRASS_INSTALL_GUISCRIPTDIR}") +set(RUNTIME_GRASS_DOCDIR "${OUTDIR}/${GRASS_INSTALL_DOCDIR}") +set(RUNTIME_GRASS_MKDOCSDIR "${OUTDIR}/${GRASS_INSTALL_MKDOCSDIR}") +set(RUNTIME_GRASS_FONTSDIR "${OUTDIR}/${GRASS_INSTALL_FONTSDIR}") +set(RUNTIME_GRASS_COLORSDIR "${OUTDIR}/${GRASS_INSTALL_COLORSDIR}") +set(RUNTIME_GRASS_GRAPHICSDIR "${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}") +set(RUNTIME_GRASS_ETCDIR "${OUTDIR}/${GRASS_INSTALL_ETCDIR}") + set(GISRC_NAME ".grassrc${GRASS_VERSION_MAJOR}${GRASS_VERSION_MINOR}") if(WITH_FHS) diff --git a/display/CMakeLists.txt b/display/CMakeLists.txt index 9ddb3faa214..5be9432a33e 100644 --- a/display/CMakeLists.txt +++ b/display/CMakeLists.txt @@ -52,12 +52,16 @@ add_custom_command( TARGET d.barscale POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory - ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/barscales + ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/barscales COMMAND ${CMAKE_COMMAND} -E copy ${d_barscale_png} - ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/barscales - BYPRODUCTS ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/barscales) -install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/barscales - DESTINATION ${GRASS_INSTALL_DOCDIR}) + ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/barscales + BYPRODUCTS ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/barscales) +install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/barscales + DESTINATION ${GRASS_INSTALL_GRAPHICSDIR}) +if(WITH_FHS AND WITH_DOCS) + install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/barscales + DESTINATION ${GRASS_INSTALL_DOCDIR}) +endif() build_program_in_subdir(d.colorlist DEPENDS grass_gis grass_display grass_raster) @@ -108,13 +112,17 @@ build_program_in_subdir(d.linegraph DEPENDS grass_gis grass_display grass_symb build_program_in_subdir(d.northarrow DEPENDS grass_gis grass_display grass_symb ${LIBM}) if(WITH_DOCS) - file(MAKE_DIRECTORY ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/northarrows) + file(MAKE_DIRECTORY ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/northarrows) file(GLOB d_northarrow_png ${CMAKE_CURRENT_SOURCE_DIR}/d.northarrow/thumbnails/*.png) file(COPY ${d_northarrow_png} - DESTINATION ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/northarrows) - install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_DOCDIR}/northarrows - DESTINATION ${GRASS_INSTALL_DOCDIR}) + DESTINATION ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/northarrows) + install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/northarrows + DESTINATION ${GRASS_INSTALL_GRAPHICSDIR}) + if(WITH_FHS) + install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_GRAPHICSDIR}/northarrows + DESTINATION ${GRASS_INSTALL_DOCDIR}) + endif() endif() build_program_in_subdir(d.path DEPENDS grass_gis grass_display grass_vector) build_program_in_subdir(d.profile DEPENDS grass_gis grass_display grass_raster diff --git a/display/d.linegraph/main.c b/display/d.linegraph/main.c index 3aa493f78ac..1fb7886733f 100644 --- a/display/d.linegraph/main.c +++ b/display/d.linegraph/main.c @@ -89,7 +89,7 @@ static char *icon_files(void) list = NULL; len = 0; - snprintf(path, sizeof(path), "%s/etc/symbol", G_gisbase()); + snprintf(path, sizeof(path), "%s/symbol", G_etc_dir()); dir = opendir(path); if (!dir) @@ -102,7 +102,7 @@ static char *icon_files(void) if (d->d_name[0] == '.') continue; - snprintf(path_i, sizeof(path_i), "%s/etc/symbol/%s", G_gisbase(), + snprintf(path_i, sizeof(path_i), "%s/symbol/%s", G_etc_dir(), d->d_name); dir_i = opendir(path_i); diff --git a/display/d.mon/start.c b/display/d.mon/start.c index 2d741db0bf7..0af93eaa308 100644 --- a/display/d.mon/start.c +++ b/display/d.mon/start.c @@ -115,8 +115,12 @@ char *start_wx(const char *name, const char *element, int width, int height, mapfile = (char *)G_malloc(GPATH_MAX); mapfile[0] = '\0'; - snprintf(progname, sizeof(progname), "%s/gui/wxpython/mapdisp/main.py", - G_gisbase()); + const char *wxdir = getenv("GRASS_GUIWXDIR"); + if (!wxdir) + G_fatal_error(_("Incomplete GRASS session: Variable '%s' not set"), + "GRASS_GUIWXDIR"); + + snprintf(progname, sizeof(progname), "%s/mapdisp/main.py", wxdir); snprintf(str_width, sizeof(str_width), "%d", width); snprintf(str_height, sizeof(str_height), "%d", height); @@ -168,8 +172,8 @@ int start_mon(const char *name, const char *output, int select, int width, leg_file = G_store(file_path); /* create py file (renderer) */ - snprintf(render_cmd_path, sizeof(render_cmd_path), - "%s/etc/d.mon/render_cmd.py", getenv("GISBASE")); + snprintf(render_cmd_path, sizeof(render_cmd_path), "%s/d.mon/render_cmd.py", + G_etcbin_dir()); G_file_name(file_path, mon_path, "render.py", G_mapset()); G_debug(1, "Monitor name=%s, pyfile = %s", name, file_path); if (1 != G_copy_file(render_cmd_path, file_path)) diff --git a/display/d.vect.thematic/main.c b/display/d.vect.thematic/main.c index 7fd51defe21..6be1267183d 100644 --- a/display/d.vect.thematic/main.c +++ b/display/d.vect.thematic/main.c @@ -645,7 +645,7 @@ static char *icon_files(void) list = NULL; len = 0; - snprintf(path, sizeof(path), "%s/etc/symbol", G_gisbase()); + snprintf(path, sizeof(path), "%s/symbol", G_etc_dir()); dir = opendir(path); if (!dir) @@ -658,7 +658,7 @@ static char *icon_files(void) if (d->d_name[0] == '.') continue; - snprintf(path_i, sizeof(path_i), "%s/etc/symbol/%s", G_gisbase(), + snprintf(path_i, sizeof(path_i), "%s/symbol/%s", G_etc_dir(), d->d_name); dir_i = opendir(path_i); diff --git a/display/d.vect/main.c b/display/d.vect/main.c index 265357a1ee7..0c653c67bab 100644 --- a/display/d.vect/main.c +++ b/display/d.vect/main.c @@ -531,7 +531,7 @@ char *icon_files(void) list = NULL; len = 0; - snprintf(path, sizeof(path), "%s/etc/symbol", G_gisbase()); + snprintf(path, sizeof(path), "%s/symbol", G_etc_dir()); dir = opendir(path); if (!dir) @@ -544,7 +544,7 @@ char *icon_files(void) if (d->d_name[0] == '.') continue; - snprintf(path_i, sizeof(path_i), "%s/etc/symbol/%s", G_gisbase(), + snprintf(path_i, sizeof(path_i), "%s/symbol/%s", G_etc_dir(), d->d_name); dir_i = opendir(path_i); diff --git a/doc/examples/gui/wxpython/frame.py b/doc/examples/gui/wxpython/frame.py index 9bb95b17094..e9d6c01e431 100644 --- a/doc/examples/gui/wxpython/frame.py +++ b/doc/examples/gui/wxpython/frame.py @@ -22,7 +22,7 @@ # this enables to run application standalone (> python example/frame.py ) if __name__ == "__main__": - sys.path.append(os.path.join(os.environ["GISBASE"], "etc", "gui", "wxpython")) + sys.path.append(os.environ["GRASS_GUIWXDIR"]) # i18n is taken care of in the grass library code. # So we need to import it before any of the GUI code. diff --git a/doc/examples/gui/wxpython/g.gui.example.py b/doc/examples/gui/wxpython/g.gui.example.py old mode 100644 new mode 100755 index 9c04d659458..fb2e94f4c36 --- a/doc/examples/gui/wxpython/g.gui.example.py +++ b/doc/examples/gui/wxpython/g.gui.example.py @@ -38,7 +38,7 @@ import grass.script.core as gcore if __name__ == "__main__": - wxbase = os.path.join(os.getenv("GISBASE"), "etc", "gui", "wxpython") + wxbase = os.environ["GRASS_GUIWXDIR"] if wxbase not in sys.path: sys.path.append(wxbase) diff --git a/general/g.gui/main.c b/general/g.gui/main.c index 91e7de7e128..a34782d3faf 100644 --- a/general/g.gui/main.c +++ b/general/g.gui/main.c @@ -104,8 +104,12 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); } - snprintf(progname, sizeof(progname), "%s/gui/wxpython/wxgui.py", - G_gisbase()); + const char *wxdir = getenv("GRASS_GUIWXDIR"); + if (!wxdir) + G_fatal_error(_("Incomplete GRASS session: Variable '%s' not set"), + "GRASS_GUIWXDIR"); + + snprintf(progname, sizeof(progname), "%s/wxgui.py", wxdir); if (access(progname, F_OK) == -1) G_fatal_error(_("Your installation doesn't include GUI, exiting.")); diff --git a/general/g.list/main.c b/general/g.list/main.c index dabfec64bb7..ecfe05f5be3 100644 --- a/general/g.list/main.c +++ b/general/g.list/main.c @@ -366,7 +366,7 @@ int main(int argc, char *argv[]) if (flag.full->answer) { char lister[GPATH_MAX]; - snprintf(lister, sizeof(lister), "%s/etc/lister/%s", G_gisbase(), + snprintf(lister, sizeof(lister), "%s/lister/%s", G_etcbin_dir(), elem->element[0]); G_debug(3, "lister CMD: %s", lister); diff --git a/general/g.mapset/main.c b/general/g.mapset/main.c index 07eddafb061..e86ee40fc37 100644 --- a/general/g.mapset/main.c +++ b/general/g.mapset/main.c @@ -245,7 +245,7 @@ int main(int argc, char *argv[]) if (!gis_lock) G_fatal_error(_("Unable to read GIS_LOCK environment variable")); - G_asprintf(&lock_prog, "%s/etc/lock", G_gisbase()); + G_asprintf(&lock_prog, "%s/lock", G_etc_dir()); snprintf(path, sizeof(path), "%s/.gislock", mapset_new_path); G_debug(2, "%s", path); @@ -268,7 +268,7 @@ int main(int argc, char *argv[]) } /* Clean temporary directory */ - snprintf(path, sizeof(path), "%s/etc/clean_temp", G_gisbase()); + snprintf(path, sizeof(path), "%s/clean_temp", G_etcbin_dir()); G_verbose_message(_("Cleaning up temporary files...")); G_spawn(path, "clean_temp", NULL); diff --git a/general/g.mapsets/main.c b/general/g.mapsets/main.c index 54068dd71b7..d4e4f86c3aa 100644 --- a/general/g.mapsets/main.c +++ b/general/g.mapsets/main.c @@ -217,8 +217,14 @@ int main(int argc, char *argv[]) if (opt.dialog->answer) { if (opt.mapset->answer) G_warning(_("Option <%s> ignored"), opt.mapset->key); - snprintf(path_buf, sizeof(path_buf), - "%s/gui/wxpython/modules/mapsets_picker.py", G_gisbase()); + + const char *wxdir = getenv("GRASS_GUIWXDIR"); + if (!wxdir) + G_fatal_error(_("Incomplete GRASS session: Variable '%s' not set"), + "GRASS_GUIWXDIR"); + + snprintf(path_buf, sizeof(path_buf), "%s/modules/mapsets_picker.py", + wxdir); G_spawn(getenv("GRASS_PYTHON"), "mapsets_picker.py", path_buf, NULL); exit(EXIT_SUCCESS); } diff --git a/general/g.mkfontcap/Makefile b/general/g.mkfontcap/Makefile index acf9cb184a3..1adf01758ae 100644 --- a/general/g.mkfontcap/Makefile +++ b/general/g.mkfontcap/Makefile @@ -17,6 +17,7 @@ default: cmd $(CAPFILE) endif $(CAPFILE): $(BIN)/$(PGM)$(EXE) - GISRC=junk GISBASE=$(RUN_GISBASE) \ + GISRC=junk GISBASE=$(RUN_GISBASE) GRASS_ETCDIR=$(RUN_GISBASE)/etc \ + GRASS_LOCALEDIR=$(RUN_GISBASE)/locale GRASS_FONTSDIR=$(RUN_GISBASE)/fonts \ $(LD_LIBRARY_PATH_VAR)="$(ARCH_LIBDIR):$(BASE_LIBDIR):$($(LD_LIBRARY_PATH_VAR))" \ $< -s > $@ diff --git a/general/g.mkfontcap/main.c b/general/g.mkfontcap/main.c index 0280e966103..4297292050a 100644 --- a/general/g.mkfontcap/main.c +++ b/general/g.mkfontcap/main.c @@ -86,13 +86,12 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); if (!tostdout->answer) { - const char *gisbase = G_gisbase(); const char *alt_file = getenv("GRASS_FONT_CAP"); if (alt_file) fontcapfile = G_store(alt_file); else - G_asprintf(&fontcapfile, "%s/etc/fontcap", gisbase); + G_asprintf(&fontcapfile, "%s/fontcap", G_etc_dir()); if (!access(fontcapfile, F_OK)) { /* File exists? */ if (!G_get_overwrite()) diff --git a/general/g.mkfontcap/stroke_fonts.c b/general/g.mkfontcap/stroke_fonts.c index d94b0f441c0..afbfa87d135 100644 --- a/general/g.mkfontcap/stroke_fonts.c +++ b/general/g.mkfontcap/stroke_fonts.c @@ -48,12 +48,11 @@ static const char *get_desc(const char *); void find_stroke_fonts(void) { - char *dirpath, *fonttable; + char *fonttable; char **dirlisting; int numfiles, i; - G_asprintf(&dirpath, "%s/fonts", G_gisbase()); - + const char *dirpath = G_fonts_dir(); dirlisting = G_ls2(dirpath, &numfiles); G_asprintf(&fonttable, "%s/fonts.table", dirpath); diff --git a/general/g.setproj/get_stp.c b/general/g.setproj/get_stp.c index 3969a735f38..cfe906bddd7 100644 --- a/general/g.setproj/get_stp.c +++ b/general/g.setproj/get_stp.c @@ -51,7 +51,7 @@ int get_stp_code(int code, char *string, char *paramfile) int gotit = 0, stp; FILE *fp; - snprintf(nad27, sizeof(nad27), "%s%s", G_gisbase(), paramfile); + snprintf(nad27, sizeof(nad27), "%s/%s", G_etc_dir(), paramfile); fp = fopen(nad27, "r"); if (fp == NULL) { snprintf(buff, sizeof(buff), "Can not open NAD27 file %s", nad27); @@ -84,7 +84,7 @@ int get_stp_num(void) int record, icode, reccnt = 0, special_case; char STabbr[50], COname[50]; - snprintf(FIPSfile, sizeof(FIPSfile), "%s/etc/proj/FIPS.code", G_gisbase()); + snprintf(FIPSfile, sizeof(FIPSfile), "%s/proj/FIPS.code", G_etc_dir()); for (;;) { diff --git a/general/g.setproj/local_proto.h b/general/g.setproj/local_proto.h index 03f67725748..31f7d64b32a 100644 --- a/general/g.setproj/local_proto.h +++ b/general/g.setproj/local_proto.h @@ -4,8 +4,8 @@ #define SP_UNKNOWN 3 /* $GISBASE-relative locations of parameter files */ -#define STP1927PARAMS "/etc/proj/state27" -#define STP1983PARAMS "/etc/proj/state83" +#define STP1927PARAMS "proj/state27" +#define STP1983PARAMS "proj/state83" #define RADIUS_DEF 6370997. diff --git a/general/g.setproj/proj.c b/general/g.setproj/proj.c index eb6de8a7bd7..553e0049ddd 100644 --- a/general/g.setproj/proj.c +++ b/general/g.setproj/proj.c @@ -13,7 +13,7 @@ struct proj_unit *get_proj_unit(const char *arg) struct proj_unit *unit; FILE *fp; - snprintf(buf, sizeof(buf), "%s/etc/proj/units.table", G_gisbase()); + snprintf(buf, sizeof(buf), "%s/proj/units.table", G_etc_dir()); fp = fopen(buf, "r"); if (!fp) @@ -51,7 +51,7 @@ struct proj_desc *get_proj_desc(const char *arg) struct proj_desc *res; FILE *fp; - snprintf(buf, sizeof(buf), "%s/etc/proj/desc.table", G_gisbase()); + snprintf(buf, sizeof(buf), "%s/proj/desc.table", G_etc_dir()); fp = fopen(buf, "r"); if (!fp) @@ -92,7 +92,7 @@ struct proj_parm *get_proj_parms(const char *arg) int done; FILE *fp; - snprintf(buf, sizeof(buf), "%s/etc/proj/parms.table", G_gisbase()); + snprintf(buf, sizeof(buf), "%s/proj/parms.table", G_etc_dir()); fp = fopen(buf, "r"); if (!fp) diff --git a/gui/scripts/d.wms.py b/gui/scripts/d.wms.py old mode 100644 new mode 100755 index e7da3717f3a..becd35fdd96 --- a/gui/scripts/d.wms.py +++ b/gui/scripts/d.wms.py @@ -161,7 +161,7 @@ from grass.script import core as grass -sys.path.append(os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms")) +sys.path.append(os.path.join(os.getenv("GRASS_ETCBINDIR"), "r.in.wms")) def GetRegion(): diff --git a/gui/wxpython/core/globalvar.py b/gui/wxpython/core/globalvar.py index a3c8b50de79..ade5238844b 100644 --- a/gui/wxpython/core/globalvar.py +++ b/gui/wxpython/core/globalvar.py @@ -26,11 +26,10 @@ from pathlib import Path # path to python scripts -ETCDIR = os.path.join(os.getenv("GISBASE"), "etc") -GUIDIR = os.path.join(os.getenv("GISBASE"), "gui") -WXGUIDIR = os.path.join(GUIDIR, "wxpython") -ICONDIR = os.path.join(GUIDIR, "icons") -IMGDIR = os.path.join(GUIDIR, "images") +ETCDIR = os.getenv("GRASS_ETCDIR") +WXGUIDIR = os.getenv("GRASS_GUIWXDIR") +ICONDIR = os.path.join(os.getenv("GRASS_GUIRESDIR"), "icons") +IMGDIR = os.path.join(os.getenv("GRASS_GUIRESDIR"), "images") SYMBDIR = os.path.join(IMGDIR, "symbols") WXPY3_MIN_VERSION = [4, 0, 0, 0] @@ -249,7 +248,7 @@ def UpdateGRASSAddOnCommands(eList=None): gtk3 = "gtk3" in wx.PlatformInfo # Add GUIDIR/scripts into path -os.environ["PATH"] = os.path.join(GUIDIR, "scripts") + os.pathsep + os.environ["PATH"] +os.environ["PATH"] = os.environ["GRASS_GUISCRIPTDIR"] + os.pathsep + os.environ["PATH"] ignoredCmdPattern = ( r"^d\..*|^r[3]?\.mapcalc$|^i.group$|^r.import$|" diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index e0758a482d6..10a126d10ba 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -105,7 +105,7 @@ def __init__(self): def _generateLocale(self): """Generate locales""" try: - locale_path = Path(os.environ["GISBASE"]) / "locale" + locale_path = Path(os.environ["GRASS_LOCALEDIR"]) self.locs = [p.name for p in locale_path.iterdir() if p.is_dir()] self.locs.append("en") # GRASS doesn't ship EN po files self.locs.sort() diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index beff6291f3e..495e06d9d4e 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -31,7 +31,7 @@ # if this will become part of grass Python library or module, this should be # parametrized, so we will get rid of the definition here # (GUI will use its definition and build also its own) -WXGUIDIR = os.path.join(os.getenv("GISBASE"), "gui", "wxpython") +WXGUIDIR = os.environ["GRASS_GUIWXDIR"] # this could be placed to functions diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py index bc4924ecb80..3d866d4de4d 100644 --- a/gui/wxpython/gui_core/ghelp.py +++ b/gui/wxpython/gui_core/ghelp.py @@ -275,7 +275,7 @@ def _pageInfo(self): def _pageCopyright(self): """Copyright information""" - copyfile = os.path.join(os.getenv("GISBASE"), "COPYING") + copyfile = os.path.join(os.getenv("GRASS_MISCDIR"), "COPYING") if os.path.exists(copyfile): copytext = Path(copyfile).read_text() else: @@ -302,7 +302,7 @@ def _pageCopyright(self): def _pageLicense(self): """Licence about""" - licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT") + licfile = os.path.join(os.getenv("GRASS_MISCDIR"), "GPL.TXT") if os.path.exists(licfile): with open(licfile) as licenceFile: license = "".join(licenceFile.readlines()) @@ -355,7 +355,7 @@ def _pageCitation(self): def _pageCredit(self): """Credit about""" # credits - authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS") + authfile = os.path.join(os.getenv("GRASS_MISCDIR"), "AUTHORS") if os.path.exists(authfile): with codecs.open(authfile, encoding="utf-8", mode="r") as authorsFile: authors = "".join(authorsFile.readlines()) @@ -380,9 +380,11 @@ def _pageCredit(self): def _pageContributors(self, extra=False): """Contributors info""" if extra: - contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv") + contribfile = os.path.join( + os.getenv("GRASS_MISCDIR"), "contributors_extra.csv" + ) else: - contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv") + contribfile = os.path.join(os.getenv("GRASS_MISCDIR"), "contributors.csv") if os.path.exists(contribfile): contribs = [] errLines = [] @@ -465,7 +467,7 @@ def _pageContributors(self, extra=False): def _pageTranslators(self): """Translators info""" - translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv") + translatorsfile = os.path.join(os.getenv("GRASS_MISCDIR"), "translators.csv") if os.path.exists(translatorsfile): translators = {} errLines = [] @@ -645,7 +647,7 @@ def OnPaneChanged(self, evt): def _pageStats(self): """Translation statistics info""" fname = "translation_status.json" - statsfile = os.path.join(os.getenv("GISBASE"), fname) + statsfile = os.path.join(os.getenv("GRASS_MISCDIR"), fname) if os.path.exists(statsfile): import json @@ -761,10 +763,10 @@ def __init__(self, parent, command, text, **kwargs): self.historyIdx = 0 self.markdown = True # check if mkdocs is used (add slash at the end) - self.fspath = os.path.join(os.getenv("GISBASE"), "docs", "mkdocs", "site", "") + self.fspath = os.path.join(os.getenv("GRASS_MKDOCSDIR"), "site", "") if not os.path.exists(self.fspath): self.markdown = False - self.fspath = os.path.join(os.getenv("GISBASE"), "docs", "html", "") + self.fspath = os.path.join(os.getenv("GRASS_DOCDIR"), "") self._setFont() self.SetBorders(10) diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index ffbbed02e02..3a44b8b0dc9 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -1727,7 +1727,7 @@ class ColorTablesComboBox(PictureComboBox): def _getPath(self, name): return os.path.join( - os.getenv("GISBASE"), "docs", "html", "colortables", "%s.png" % name + os.getenv("GRASS_GRAPHICSDIR"), "colortables", "%s.png" % name ) @@ -1735,9 +1735,7 @@ class BarscalesComboBox(PictureComboBox): """ComboBox with barscales for d.barscale.""" def _getPath(self, name): - return os.path.join( - os.getenv("GISBASE"), "docs", "html", "barscales", name + ".png" - ) + return os.path.join(os.getenv("GRASS_GRAPHICSDIR"), "barscales", name + ".png") class NArrowsComboBox(PictureComboBox): @@ -1745,7 +1743,7 @@ class NArrowsComboBox(PictureComboBox): def _getPath(self, name): return os.path.join( - os.getenv("GISBASE"), "docs", "html", "northarrows", "%s.png" % name + os.getenv("GRASS_GRAPHICSDIR"), "northarrows", "%s.png" % name ) diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index e2b3da9e60d..c9952ed6160 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -88,11 +88,9 @@ def __init__(self, parent=None, id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE): # image try: if os.getenv("ISISROOT"): - name = os.path.join( - globalvar.GUIDIR, "images", "startup_banner_isis.png" - ) + name = os.path.join(globalvar.IMGDIR, "startup_banner_isis.png") else: - name = os.path.join(globalvar.GUIDIR, "images", "startup_banner.png") + name = os.path.join(globalvar.IMGDIR, "startup_banner.png") self.hbitmap = wx.StaticBitmap( self.panel, wx.ID_ANY, wx.Bitmap(name=name, type=wx.BITMAP_TYPE_PNG) ) diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 9524c3a8b5d..b3ab6730993 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -35,9 +35,6 @@ except ImportError: import wx.lib.flatnotebook as FN -if os.path.join(globalvar.ETCDIR, "python") not in sys.path: - sys.path.append(os.path.join(globalvar.ETCDIR, "python")) - from grass.script import core as grass from grass.script.utils import decode @@ -1463,7 +1460,7 @@ def OnSystemInfo(self, event): # check also OSGeo4W on MS Windows if sys.platform == "win32" and not os.path.exists( - os.path.join(os.getenv("GISBASE"), "WinGRASS-README.url") + os.path.join(os.getenv("GRASS_MISCDIR"), "WinGRASS-README.url") ): osgeo4w = " (OSGeo4W)" else: diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index f6ab655d6c0..1239b10a400 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -41,9 +41,6 @@ except ImportError: import wx.lib.flatnotebook as FN -if os.path.join(globalvar.ETCDIR, "python") not in sys.path: - sys.path.append(os.path.join(globalvar.ETCDIR, "python")) - from grass.script import core as grass from grass.script.utils import decode @@ -1619,7 +1616,7 @@ def OnSystemInfo(self, event): # check also OSGeo4W on MS Windows if sys.platform == "win32" and not os.path.exists( - os.path.join(os.getenv("GISBASE"), "WinGRASS-README.url") + os.path.join(os.getenv("GRASS_MISCDIR"), "WinGRASS-README.url") ): osgeo4w = " (OSGeo4W)" else: diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 2561e3c388e..fa76cc15e1f 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -2203,13 +2203,13 @@ def __init__(self, parent, id, vectors, tmpSettings): self.currLayer = self.vPropertiesDict["layer"] # path to symbols, patterns - gisbase = os.getenv("GISBASE") - self.symbolPath = os.path.join(gisbase, "etc", "symbol") + g_etc_dir = os.getenv("GRASS_ETCDIR") + self.symbolPath = os.path.join(g_etc_dir, "symbol") self.symbols = [] for dir in Path(self.symbolPath).iterdir(): for symbol in Path(self.symbolPath).joinpath(dir.name).iterdir(): self.symbols.append(os.path.join(dir.name, symbol.name)) - self.patternPath = os.path.join(gisbase, "etc", "paint", "patterns") + self.patternPath = os.path.join(g_etc_dir, "paint", "patterns") # notebook notebook = Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT) @@ -6193,8 +6193,8 @@ def _newObject(self): return NorthArrow(self.id, self.instruction, env=self.env) def _getImageDirectory(self): - gisbase = os.getenv("GISBASE") - return os.path.join(gisbase, "etc", "paint", "decorations") + g_etc_dir = os.getenv("GRASS_ETCDIR") + return os.path.join(g_etc_dir, "paint", "decorations") def _addConvergence(self, panel, gridBagSizer): convergence = Button(parent=panel, id=wx.ID_ANY, label=_("Compute convergence")) diff --git a/gui/wxpython/web_services/cap_interface.py b/gui/wxpython/web_services/cap_interface.py index 91b2b396dfd..eacea483b29 100644 --- a/gui/wxpython/web_services/cap_interface.py +++ b/gui/wxpython/web_services/cap_interface.py @@ -25,7 +25,7 @@ import os import sys -WMSLibPath = os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms") +WMSLibPath = os.path.join(os.getenv("GRASS_ETCBINDIR"), "r.in.wms") if WMSLibPath not in sys.path: sys.path.append(WMSLibPath) diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 29a1c99014a..ff7746f358f 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -62,7 +62,7 @@ import grass.script as gs from grass.pydispatch.signal import Signal -rinwms_path = os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms") +rinwms_path = os.path.join(os.getenv("GRASS_ETCBINDIR"), "r.in.wms") if rinwms_path not in sys.path: sys.path.append(rinwms_path) diff --git a/include/Make/Install.make b/include/Make/Install.make index 635ce194190..8e67ca6e869 100644 --- a/include/Make/Install.make +++ b/include/Make/Install.make @@ -128,8 +128,9 @@ $(DESTDIR)$(INST_DIR) $(DESTDIR)$(UNIX_BIN): $(MAKE_DIR_CMD) $@ $(STARTUP): $(ARCH_DISTDIR)/$(GRASS_NAME).tmp - sed -e 's#'@GRASS_PYDIR@'#'$(INST_DIR)/etc/python'#g' \ - $< > $@ + sed \ + -e 's#'@GRASS_PYDIR@'#'$(INST_DIR)/etc/python'#g' \ + $< > $@ -$(CHMOD) a+x $@ $(DESTDIR)$(INST_DIR)/$(RESOURCE_PATHS): $(ARCH_DISTDIR)/resource_paths.py @@ -143,6 +144,18 @@ $(DESTDIR)$(INST_DIR)/$(RESOURCE_PATHS): $(ARCH_DISTDIR)/resource_paths.py -e 's#'@GRASS_VERSION_NUMBER@'#$(GRASS_VERSION_NUMBER)#' \ -e 's#'@LD_LIBRARY_PATH_VAR@'#$(LD_LIBRARY_PATH_VAR)#' \ -e 's#'@START_UP@'#$(GRASS_NAME)#' \ + -e 's#'@GRASS_COLORSDIR@'#etc/colors#' \ + -e 's#'@GRASS_DOCDIR@'#docs/html#' \ + -e 's#'@GRASS_ETCBINDIR@'#etc#' \ + -e 's#'@GRASS_ETCDIR@'#etc#' \ + -e 's#'@GRASS_FONTSDIR@'#fonts#' \ + -e 's#'@GRASS_GRAPHICSDIR@'#docs/html#' \ + -e 's#'@GRASS_GUIRESDIR@'#gui#' \ + -e 's#'@GRASS_GUISCRIPTDIR@'#gui/scripts#' \ + -e 's#'@GRASS_GUIWXDIR@'#gui/wxpython#' \ + -e 's#'@GRASS_LOCALEDIR@'#locale#' \ + -e 's#'@GRASS_MISCDIR@'##' \ + -e 's#'@GRASS_MKDOCSDIR@'#docs/mkdocs#' \ $< > $@ define fix_gisbase diff --git a/include/Make/Rules.make b/include/Make/Rules.make index 9e765fab7a4..7220ad7f466 100644 --- a/include/Make/Rules.make +++ b/include/Make/Rules.make @@ -45,6 +45,17 @@ GRASS_PYTHONPATH := $(call mkpath,$(GISBASE)/etc/python,$(GRASS_PYTHONPATH)) run_grass = \ GISRC=$(RUN_GISRC) \ GISBASE=$(RUN_GISBASE) \ + GRASS_COLORSDIR=$(RUN_GISBASE)/etc/colors \ + GRASS_ETCDIR=$(RUN_GISBASE)/etc \ + GRASS_FONTSDIR=$(RUN_GISBASE)/fonts \ + GRASS_GRAPHICSDIR=$(RUN_GISBASE)/docs/html \ + GRASS_GUIRESDIR=$(RUN_GISBASE)/gui \ + GRASS_GUISCRIPTDIR=$(RUN_GISBASE)/gui/scripts \ + GRASS_GUIWXDIR=$(RUN_GISBASE)/gui/wxpython \ + GRASS_LOCALEDIR=$(RUN_GISBASE)/locale \ + GRASS_MISCDIR=$(RUN_GISBASE) \ + GRASS_MKDOCSDIR=$(RUN_GISBASE)/docs/mkdocs \ + GRASS_PYDIR=$(RUN_GISBASE)/etc/python \ PATH="$(ARCH_DISTDIR)/bin:$(GISBASE)/bin:$(GISBASE)/scripts:$$PATH" \ PYTHONPATH="$(GRASS_PYTHONPATH)" \ $(LD_LIBRARY_PATH_VAR)="$(BIN):$(GISBASE)/bin:$(GISBASE)/scripts:$(ARCH_LIBDIR):$(BASE_LIBDIR):$($(LD_LIBRARY_PATH_VAR))" \ diff --git a/include/grass/defs/gis.h b/include/grass/defs/gis.h index 8bacb260cc2..10a793c6ed5 100644 --- a/include/grass/defs/gis.h +++ b/include/grass/defs/gis.h @@ -394,6 +394,13 @@ void G__gisinit(const char *, const char *); void G__no_gisinit(const char *); void G_init_all(void); +/* resource_dirs.c */ +const char *G_colors_dir(void); +const char *G_etcbin_dir(void); +const char *G_etc_dir(void); +const char *G_fonts_dir(void); +const char *G_locale_dir(void); + /* handler.c */ void G_add_error_handler(void (*)(void *), void *); void G_remove_error_handler(void (*)(void *), void *); diff --git a/include/grass/gprojects.h b/include/grass/gprojects.h index 35f795d87ad..cf1e1236307 100644 --- a/include/grass/gprojects.h +++ b/include/grass/gprojects.h @@ -61,11 +61,11 @@ #endif /* Data Files */ -#define ELLIPSOIDTABLE "/etc/proj/ellipse.table" -#define DATUMTABLE "/etc/proj/datum.table" -#define DATUMTRANSFORMTABLE "/etc/proj/datumtransform.table" +#define ELLIPSOIDTABLE "/proj/ellipse.table" +#define DATUMTABLE "/proj/datum.table" +#define DATUMTRANSFORMTABLE "/proj/datumtransform.table" /* GRASS relative location of datum conversion lookup tables */ -#define GRIDDIR "/etc/proj/nad" +#define GRIDDIR "/proj/nad" /* TODO: rename pj_ to gpj_ to avoid symbol clash with PROJ lib */ struct pj_info { diff --git a/lib/driver/font2.c b/lib/driver/font2.c index eaa91e069d6..272a66e4e2f 100644 --- a/lib/driver/font2.c +++ b/lib/driver/font2.c @@ -128,7 +128,7 @@ static void load_glyphs(void) for (i = 1; i <= 4; i++) { char buf[GPATH_MAX]; - snprintf(buf, sizeof(buf), "%s/fonts/hersh.oc%d", G_gisbase(), i); + snprintf(buf, sizeof(buf), "%s/hersh.oc%d", G_fonts_dir(), i); read_hersh(buf); } } @@ -141,7 +141,7 @@ static void read_fontmap(const char *name) num_chars = 0; memset(fontmap, 0, sizeof(fontmap)); - snprintf(buf, sizeof(buf), "%s/fonts/%s.hmp", G_gisbase(), name); + snprintf(buf, sizeof(buf), "%s/%s.hmp", G_fonts_dir(), name); fp = fopen(buf, "r"); if (!fp) { diff --git a/lib/driver/parse_ftcap.c b/lib/driver/parse_ftcap.c index e8be0572c25..4c03d83fba2 100644 --- a/lib/driver/parse_ftcap.c +++ b/lib/driver/parse_ftcap.c @@ -86,7 +86,7 @@ struct GFONT_CAP *parse_fontcap(void) capfile); } if (fp == NULL) { - snprintf(file, sizeof(file), "%s/etc/fontcap", G_gisbase()); + snprintf(file, sizeof(file), "%s/fontcap", G_etc_dir()); if ((fp = fopen(file, "r")) == NULL) G_warning(_("%s: No font definition file"), file); } diff --git a/lib/gis/color_rules.c b/lib/gis/color_rules.c index bb3ace277f8..8c998b223df 100644 --- a/lib/gis/color_rules.c +++ b/lib/gis/color_rules.c @@ -268,7 +268,7 @@ struct colorinfo *get_colorinfo(int *nrules) char **cnames; /* load color rules */ - snprintf(path, GPATH_MAX, "%s/etc/colors", G_gisbase()); + snprintf(path, GPATH_MAX, "%s", G_colors_dir()); *nrules = 0; cnames = G_ls2(path, nrules); @@ -284,8 +284,7 @@ struct colorinfo *get_colorinfo(int *nrules) colorinfo[i].desc = NULL; /* open color rule file */ - snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), - colorinfo[i].name); + snprintf(path, GPATH_MAX, "%s/%s", G_colors_dir(), colorinfo[i].name); fp = fopen(path, "r"); if (!fp) G_fatal_error(_("Unable to open color rule")); @@ -361,7 +360,7 @@ struct colorinfo *get_colorinfo(int *nrules) qsort(colorinfo, *nrules, sizeof(struct colorinfo), cmp_clrname); /* load color descriptions */ - snprintf(path, GPATH_MAX, "%s/etc/colors.desc", G_gisbase()); + snprintf(path, GPATH_MAX, "%s/colors.desc", G_etc_dir()); fp = fopen(path, "r"); if (!fp) G_fatal_error(_("Unable to open color descriptions")); diff --git a/lib/gis/datum.c b/lib/gis/datum.c index 8931dd328de..7dbeb25a5ff 100644 --- a/lib/gis/datum.c +++ b/lib/gis/datum.c @@ -14,7 +14,7 @@ * *****************************************************************************/ -#define DATUMTABLE "/etc/proj/datum.table" +#define DATUMTABLE "/proj/datum.table" #include #include @@ -148,7 +148,7 @@ void G_read_datum_table(void) if (G_is_initialized(&table.initialized)) return; - snprintf(file, sizeof(file), "%s%s", G_gisbase(), DATUMTABLE); + snprintf(file, sizeof(file), "%s%s", G_etc_dir(), DATUMTABLE); fd = fopen(file, "r"); if (!fd) { diff --git a/lib/gis/find_etc.c b/lib/gis/find_etc.c index 59e8579689e..6e43ce437ac 100644 --- a/lib/gis/find_etc.c +++ b/lib/gis/find_etc.c @@ -40,7 +40,7 @@ static char *G__find_etc(const char *name) /* * check application etc dir */ - snprintf(path, sizeof(path), "%s/etc/%s", G_gisbase(), name); + snprintf(path, sizeof(path), "%s/%s", G_etc_dir(), name); if (access(path, 0) == 0) return G_store(path); diff --git a/lib/gis/get_ellipse.c b/lib/gis/get_ellipse.c index 2a208c47bed..439b16e8068 100644 --- a/lib/gis/get_ellipse.c +++ b/lib/gis/get_ellipse.c @@ -255,7 +255,7 @@ int G_read_ellipsoid_table(int fatal) if (G_is_initialized(&table.initialized)) return 1; - snprintf(file, sizeof(file), "%s/etc/proj/ellipse.table", G_gisbase()); + snprintf(file, sizeof(file), "%s/proj/ellipse.table", G_etc_dir()); fd = fopen(file, "r"); if (fd == NULL) { diff --git a/lib/gis/is.c b/lib/gis/is.c index 68298671306..1fbcd95a64c 100644 --- a/lib/gis/is.c +++ b/lib/gis/is.c @@ -48,7 +48,7 @@ static int test_path_file(const char *path, const char *file) */ int G_is_gisbase(const char *path) { - return test_path_file(path, "etc/element_list"); + return test_path_file(path, "etc/echo"); } /** diff --git a/lib/gis/locale.c b/lib/gis/locale.c index 18316f7c7f0..9b8fcd77215 100644 --- a/lib/gis/locale.c +++ b/lib/gis/locale.c @@ -39,14 +39,9 @@ void G_init_locale(void) #ifdef LC_MESSAGES setlocale(LC_MESSAGES, ""); #endif - const char *gisbase = getenv("GISBASE"); - - if (gisbase && *gisbase) { - char localedir[GPATH_MAX]; - - strcpy(localedir, gisbase); - strcat(localedir, "/locale"); + const char *localedir = G_locale_dir(); + if (localedir && *localedir) { bindtextdomain("grasslibs", localedir); bindtextdomain("grassmods", localedir); } diff --git a/lib/gis/parser.c b/lib/gis/parser.c index 62d905c4173..d906d898dbc 100644 --- a/lib/gis/parser.c +++ b/lib/gis/parser.c @@ -987,8 +987,12 @@ int module_gui_wx(void) if (!st->pgm_path) G_fatal_error(_("Unable to determine program name")); - snprintf(script, GPATH_MAX, "%s/gui/wxpython/gui_core/forms.py", - getenv("GISBASE")); + const char *wxdir = getenv("GRASS_GUIWXDIR"); + if (!wxdir) + G_fatal_error(_("Incomplete GRASS session: Variable '%s' not set"), + "GRASS_GUIWXDIR"); + + snprintf(script, GPATH_MAX, "%s/gui_core/forms.py", wxdir); if (access(script, F_OK) != -1) G_spawn(getenv("GRASS_PYTHON"), getenv("GRASS_PYTHON"), script, G_recreate_command_original_path(), NULL); @@ -1815,7 +1819,7 @@ const char *get_renamed_option(const char *key) /* read renamed options from file (renamed_options) */ char path[GPATH_MAX]; - snprintf(path, GPATH_MAX, "%s/etc/renamed_options", G_gisbase()); + snprintf(path, GPATH_MAX, "%s/renamed_options", G_etc_dir()); st->renamed_options = G_read_key_value_file(path); } diff --git a/lib/gis/resource_dirs.c b/lib/gis/resource_dirs.c new file mode 100644 index 00000000000..8602ceefe49 --- /dev/null +++ b/lib/gis/resource_dirs.c @@ -0,0 +1,53 @@ +/*! + \file lib/gis/resource_dirs.c + + \brief GIS Library - Get paths to resource directories. + + \author Nicklas Larsson + + (c) 2025 by the GRASS Development Team + + SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include + +static const char *get_g_env(const char *); + +const char *G_colors_dir(void) +{ + return get_g_env("GRASS_COLORSDIR"); +} + +const char *G_etcbin_dir(void) +{ + return get_g_env("GRASS_ETCBINDIR"); +} + +const char *G_etc_dir(void) +{ + return get_g_env("GRASS_ETCDIR"); +} + +const char *G_fonts_dir(void) +{ + return get_g_env("GRASS_FONTSDIR"); +} + +const char *G_locale_dir(void) +{ + return get_g_env("GRASS_LOCALEDIR"); +} + +static const char *get_g_env(const char *env_var) +{ + const char *value = getenv(env_var); + if (value) + return value; + + G_fatal_error(_("Incomplete GRASS session: Variable '%s' not set"), + env_var); +} diff --git a/lib/init/Makefile b/lib/init/Makefile index 8cf12836bad..e7e24ba6ca7 100644 --- a/lib/init/Makefile +++ b/lib/init/Makefile @@ -69,22 +69,13 @@ $(ARCH_BINDIR)/$(START_UP): grass.py endif rm -f $@ sed \ - -e 's#@GRASS_PYDIR@#$(RUN_GISBASE)/etc/python#' \ + -e 's#@GRASS_PYDIR@#$(RUN_GISBASE)/etc/python#g' \ $< > $@ chmod +x $@ $(ARCH_DISTDIR)/$(START_UP).tmp: grass.py rm -f $@ - sed \ - -e 's#@GRASS_VERSION_NUMBER@#$(GRASS_VERSION_NUMBER)#' \ - -e 's#@GRASS_VERSION_MAJOR@#$(GRASS_VERSION_MAJOR)#' \ - -e 's#@GRASS_VERSION_MINOR@#$(GRASS_VERSION_MINOR)#' \ - -e 's#@GRASS_VERSION_GIT@#$(GRASS_VERSION_GIT)#' \ - -e 's#@START_UP@#$(START_UP)#' \ - -e 's#@GRASS_CONFIG_DIR@#$(GRASS_CONFIG_DIR)#' \ - -e 's#@LD_LIBRARY_PATH_VAR@#$(LD_LIBRARY_PATH_VAR)#' \ - -e 's#@CONFIG_PROJSHARE@#$(PROJSHARE)#' \ - $< > $@ + cp $< $@ $(ETC)/echo$(EXE) $(ETC)/run$(EXE): $(ETC)/%$(EXE): $(OBJDIR)/%.o $(call linker_base,$(LINK),$(LDFLAGS),$(MANIFEST_OBJ)) diff --git a/lib/init/grass.py b/lib/init/grass.py index 729720ada3c..fae0b5a2640 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1167,7 +1167,7 @@ def set_language(grass_config_dir: StrPath) -> None: os.environ["LC_MESSAGES"] = "C" os.environ["LC_NUMERIC"] = "C" os.environ["LC_TIME"] = "C" - gettext.install("grasslibs", gpath("locale")) + gettext.install("grasslibs", os.environ["GRASS_LOCALEDIR"]) sys.stderr.write( "All attempts to enable English language have" " failed. GRASS running with C locale.\n" @@ -1248,7 +1248,7 @@ def set_language(grass_config_dir: StrPath) -> None: del os.environ["LC_ALL"] # Remove LC_ALL to not override LC_NUMERIC # From now on enforce the new language - gettext.install("grasslibs", gpath("locale")) + gettext.install("grasslibs", os.environ["GRASS_LOCALEDIR"]) # TODO: the gisrcrc here does not make sense, remove it from load_gisrc @@ -2129,7 +2129,7 @@ def main() -> None: GISBASE, \ CONFIG_PROJSHARE - runtime_paths = RuntimePaths() + runtime_paths = RuntimePaths(init_env_vars=True) CMD_NAME = runtime_paths.grass_exe_name GRASS_VERSION = runtime_paths.version GRASS_VERSION_MAJOR = runtime_paths.version_major diff --git a/lib/manage/read_list.c b/lib/manage/read_list.c index 3c9d5fece4d..793b55a0ab8 100644 --- a/lib/manage/read_list.c +++ b/lib/manage/read_list.c @@ -66,7 +66,7 @@ int M_read_list(int check_if_empty, int *num) if (env) strcpy(element_list, env); else - snprintf(element_list, GPATH_MAX, "%s/etc/element_list", G_gisbase()); + snprintf(element_list, GPATH_MAX, "%s/element_list", G_etc_dir()); fd = fopen(element_list, "r"); if (!fd) diff --git a/lib/proj/convert.c b/lib/proj/convert.c index b293706a745..0b8ea6d2a82 100644 --- a/lib/proj/convert.c +++ b/lib/proj/convert.c @@ -27,7 +27,7 @@ #include "local_proto.h" /* GRASS relative location of OGR co-ordinate system lookup tables */ -#define CSVDIR "/etc/proj/ogr_csv" +#define CSVDIR "/proj/ogr_csv" static void DatumNameMassage(char **); #endif @@ -504,8 +504,8 @@ int GPJ_osr_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo, /* use name of the projection as name for the coordinate * system */ - snprintf(path, sizeof(path), "%s/etc/proj/projections", - G_gisbase()); + snprintf(path, sizeof(path), "%s/proj/projections", + G_etc_dir()); if (G_lookup_key_value_from_file(path, pszProj, name, sizeof(name)) > 0) G_set_key_value("name", name, *projinfo); @@ -640,8 +640,7 @@ int GPJ_osr_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo, /* use name of the projection as name for the coordinate system */ - snprintf(path, sizeof(path), "%s/etc/proj/projections", - G_gisbase()); + snprintf(path, sizeof(path), "%s/proj/projections", G_etc_dir()); if (G_lookup_key_value_from_file(path, pszProj, name, sizeof(name)) > 0) G_set_key_value("name", name, *projinfo); @@ -1015,13 +1014,13 @@ int GPJ_wkt_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo, const char *GPJ_set_csv_loc(const char *name) { - const char *gisbase = G_gisbase(); + const char *g_etc_dir = G_etc_dir(); static char *buf = NULL; if (buf != NULL) G_free(buf); - G_asprintf(&buf, "%s%s/%s", gisbase, CSVDIR, name); + G_asprintf(&buf, "%s%s/%s", g_etc_dir, CSVDIR, name); return buf; } diff --git a/lib/proj/datum.c b/lib/proj/datum.c index 1680b554f22..28bff7f2763 100644 --- a/lib/proj/datum.c +++ b/lib/proj/datum.c @@ -270,7 +270,7 @@ GPJ_get_datum_transform_by_name(const char *inputname) /* Now check for additional parameters in datumtransform.table */ - snprintf(file, sizeof(file), "%s%s", G_gisbase(), DATUMTRANSFORMTABLE); + snprintf(file, sizeof(file), "%s%s", G_etc_dir(), DATUMTRANSFORMTABLE); fd = fopen(file, "r"); if (!fd) { @@ -347,7 +347,7 @@ struct datum_list *read_datum_table(void) int line; struct datum_list *current = NULL, *outputlist = NULL; - snprintf(file, sizeof(file), "%s%s", G_gisbase(), DATUMTABLE); + snprintf(file, sizeof(file), "%s%s", G_etc_dir(), DATUMTABLE); fd = fopen(file, "r"); if (!fd) { diff --git a/lib/proj/ellipse.c b/lib/proj/ellipse.c index 0498cc7c566..19d2e52a2be 100644 --- a/lib/proj/ellipse.c +++ b/lib/proj/ellipse.c @@ -232,7 +232,7 @@ struct ellps_list *read_ellipsoid_table(int fatal) struct ellps_list *current = NULL, *outputlist = NULL; double a, e2, rf; - snprintf(file, sizeof(file), "%s%s", G_gisbase(), ELLIPSOIDTABLE); + snprintf(file, sizeof(file), "%s%s", G_etc_dir(), ELLIPSOIDTABLE); fd = fopen(file, "r"); if (!fd) { diff --git a/lib/psdriver/graph_set.c b/lib/psdriver/graph_set.c index b33db00f307..1e4f87244d9 100644 --- a/lib/psdriver/graph_set.c +++ b/lib/psdriver/graph_set.c @@ -56,8 +56,7 @@ static void write_prolog(void) strftime(date_str, sizeof(date_str), DATE_FORMAT, tm); - snprintf(prolog_file, sizeof(prolog_file), "%s/etc/psdriver.ps", - G_gisbase()); + snprintf(prolog_file, sizeof(prolog_file), "%s/psdriver.ps", G_etc_dir()); prolog_fp = fopen(prolog_file, "r"); if (!prolog_fp) diff --git a/lib/raster/color_rules.c b/lib/raster/color_rules.c index 1d9ddd6f1eb..910bc050103 100644 --- a/lib/raster/color_rules.c +++ b/lib/raster/color_rules.c @@ -318,7 +318,7 @@ static void load_rules_name(struct Colors *colors, const char *name, DCELL min, { char path[GPATH_MAX]; - snprintf(path, sizeof(path), "%s/etc/colors/%s", G_gisbase(), name); + snprintf(path, sizeof(path), "%s/%s", G_colors_dir(), name); if (!load_rules_file(colors, path, min, max)) G_fatal_error(_("Unable to load color rules <%s>"), name); diff --git a/lib/symbol/read.c b/lib/symbol/read.c index 5aacb78b32f..19cb89cbed5 100644 --- a/lib/symbol/read.c +++ b/lib/symbol/read.c @@ -273,7 +273,7 @@ SYMBOL *S_read(const char *sname) fp = G_fopen_old(buf, name, ms); } else { /* Search in GISBASE */ - snprintf(buf, sizeof(buf), "%s/etc/symbol/%s", G_gisbase(), sname); + snprintf(buf, sizeof(buf), "%s/symbol/%s", G_etc_dir(), sname); fp = fopen(buf, "r"); } diff --git a/locale/Makefile b/locale/Makefile index 6c5df6a7567..bca151dec68 100644 --- a/locale/Makefile +++ b/locale/Makefile @@ -42,7 +42,7 @@ MSGMERGE_ARGS = --no-wrap --sort-by-file --previous MSGUNIQ_ARGS = --no-wrap --sort-by-file define po_stats -GISBASE="$(RUN_GISBASE)" $(PYTHON) ./grass_po_stats.py +GISBASE="$(RUN_GISBASE)" GRASS_MISCDIR="$(RUN_GISBASE)" $(PYTHON) ./grass_po_stats.py endef #The xgettext utility is used to automate the creation of diff --git a/locale/grass_po_stats.py b/locale/grass_po_stats.py old mode 100644 new mode 100755 index bfae49629d3..e552032f28e --- a/locale/grass_po_stats.py +++ b/locale/grass_po_stats.py @@ -160,5 +160,5 @@ def main(in_dirpath, out_josonpath): if __name__ == "__main__": directory = "po/" - outfile = os.path.join(os.environ["GISBASE"], "translation_status.json") + outfile = os.path.join(os.environ["GRASS_MISCDIR"], "translation_status.json") sys.exit(main(directory, outfile)) diff --git a/man/Makefile b/man/Makefile index 820b8a9f830..0f1b7321f94 100644 --- a/man/Makefile +++ b/man/Makefile @@ -81,35 +81,40 @@ manpages: define build GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ - MDDIR="${MDDIR}" \ + MDDIR="${MDDIR}" GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" \ + GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ $(PYTHON) ./build_$(1).py $(2) endef define build_topics GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ - MDDIR="${MDDIR}" \ + MDDIR="${MDDIR}" GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" \ + GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ $(PYTHON) ./build_topics.py endef define build_keywords GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ - MDDIR="${MDDIR}" \ + MDDIR="${MDDIR}" GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" \ + GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ $(PYTHON) ./build_keywords.py endef define build_graphical_index GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ - MDDIR="${MDDIR}" \ + MDDIR="${MDDIR}" GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" \ + GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ $(PYTHON) ./build_graphical_index.py endef define build_manual_gallery GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ - MDDIR="${MDDIR}" \ + MDDIR="${MDDIR}" GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" \ + GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ $(PYTHON) ./build_manual_gallery.py endef @@ -117,13 +122,15 @@ endef define build_pso GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ + GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ $(PYTHON) ./parser_standard_options.py -t "$(GRASS_HOME)/lib/gis/parser_standard_options.c" \ -f "grass" -o "$(HTMLDIR)/parser_standard_options.html" -p 'id="opts_table" class="scroolTable"' endef define build_pso_md GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ - MDDIR="${MDDIR}" \ + MDDIR="${MDDIR}" GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" \ + GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ $(PYTHON) ./parser_standard_options.py -t "$(GRASS_HOME)/lib/gis/parser_standard_options.c" \ -f "grass" -o "$(MDDIR)/source/parser_standard_options.md" @@ -136,11 +143,13 @@ $(MDDIR)/source/topics.md: $(ALL_MD) define build_class_graphical GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ + GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ $(PYTHON) ./build_class_graphical.py html $(HTMLDIR) endef define build_class_graphical_md GISBASE="$(RUN_GISBASE)" ARCH="$(ARCH)" ARCH_DISTDIR="$(ARCH_DISTDIR)" \ + GRASS_DOCDIR="$(ARCH_DISTDIR)/docs/html" GRASS_MKDOCSDIR="$(ARCH_DISTDIR)/docs/mkdocs" \ VERSION_NUMBER=$(GRASS_VERSION_NUMBER) VERSION_DATE=$(GRASS_VERSION_DATE) \ $(PYTHON) ./build_class_graphical.py md $(MDDIR)/source endef diff --git a/man/build_html.py b/man/build_html.py index 513ef0e2b6b..f2b44e848d3 100644 --- a/man/build_html.py +++ b/man/build_html.py @@ -432,6 +432,6 @@ def get_desc(cmd): ############################################################################ -man_dir = os.path.join(os.environ["ARCH_DISTDIR"], "docs", "html") +man_dir = os.path.join(os.environ["GRASS_DOCDIR"]) ############################################################################ diff --git a/man/build_md.py b/man/build_md.py index bb990c79016..b8e0d84e55d 100644 --- a/man/build_md.py +++ b/man/build_md.py @@ -94,6 +94,6 @@ def get_desc(cmd): ############################################################################ -man_dir = os.path.join(os.environ["ARCH_DISTDIR"], "docs", "mkdocs", "source") +man_dir = os.path.join(os.environ["GRASS_MKDOCSDIR"], "source") ############################################################################ diff --git a/man/build_rest.py b/man/build_rest.py old mode 100644 new mode 100755 index 6cabe95e4e6..592813a4232 --- a/man/build_rest.py +++ b/man/build_rest.py @@ -360,8 +360,8 @@ def get_desc(cmd): arch_dist_dir = os.environ["ARCH_DISTDIR"] rest_dir = os.path.join(arch_dist_dir, "docs", "rest") -gisbase = os.environ["GISBASE"] -ver = read_file(os.path.join(gisbase, "etc", "VERSIONNUMBER")) +grass_etc_dir = os.environ["GRASS_ETCDIR"] +ver = read_file(os.path.join(grass_etc_dir, "VERSIONNUMBER")) try: grass_version = ver.split()[0].strip() except IndexError: diff --git a/ps/ps.map/makeprocs.c b/ps/ps.map/makeprocs.c index ae0bcf9f196..71a0cf05b32 100644 --- a/ps/ps.map/makeprocs.c +++ b/ps/ps.map/makeprocs.c @@ -22,7 +22,7 @@ int make_procs(void) level = (PS.level != 1) ? 2 : 1; fprintf(PS.fp, "/level %d def\n", level); - snprintf(filename, sizeof(filename), "%s/etc/paint/prolog.ps", G_gisbase()); + snprintf(filename, sizeof(filename), "%s/paint/prolog.ps", G_etc_dir()); fp = fopen(filename, "r"); if (!fp) diff --git a/python/grass/__init__.py b/python/grass/__init__.py index 32717194453..d6701140677 100644 --- a/python/grass/__init__.py +++ b/python/grass/__init__.py @@ -68,8 +68,7 @@ def _translate(text): try: import gettext # pylint: disable=import-outside-toplevel - gisbase = os.environ["GISBASE"] - locale_dir = os.path.join(gisbase, "locale") + locale_dir = os.environ["GRASS_LOCALEDIR"] # With fallback set to True, not finding the translations files for # a language or domain results in a use of null translation, so this # does not raise an exception even if the locale settings is broken diff --git a/python/grass/app/Makefile b/python/grass/app/Makefile index e63287710d2..d38a528ea19 100644 --- a/python/grass/app/Makefile +++ b/python/grass/app/Makefile @@ -30,6 +30,18 @@ $(DSTDIR)/resource_paths.py: resource_paths.py -e 's#@GRASS_VERSION_NUMBER@#$(GRASS_VERSION_NUMBER)#' \ -e 's#@LD_LIBRARY_PATH_VAR@#$(LD_LIBRARY_PATH_VAR)#' \ -e 's#@START_UP@#$(GRASS_NAME)#' \ + -e 's#@GRASS_COLORSDIR@#etc/colors#' \ + -e 's#@GRASS_DOCDIR@#docs/html#' \ + -e 's#@GRASS_ETCBINDIR@#etc#' \ + -e 's#@GRASS_ETCDIR@#etc#' \ + -e 's#@GRASS_FONTSDIR@#fonts#' \ + -e 's#@GRASS_GRAPHICSDIR@#docs/html#' \ + -e 's#@GRASS_GUIRESDIR@#gui#' \ + -e 's#@GRASS_GUISCRIPTDIR@#gui/scripts#' \ + -e 's#@GRASS_GUIWXDIR@#gui/wxpython#' \ + -e 's#@GRASS_LOCALEDIR@#locale#' \ + -e 's#@GRASS_MISCDIR@##' \ + -e 's#@GRASS_MKDOCSDIR@#docs/mkdocs#' \ $< > $@ $(ARCH_DISTDIR)/resource_paths.py: diff --git a/python/grass/app/data.py b/python/grass/app/data.py index 2d8b64d4e5c..3c39669cba3 100644 --- a/python/grass/app/data.py +++ b/python/grass/app/data.py @@ -99,8 +99,7 @@ def _get_startup_location_in_distribution(): Returns startup location if found or None if nothing was found. """ - gisbase = os.getenv("GISBASE") - startup_location = os.path.join(gisbase, "demolocation") + startup_location = os.path.join(os.getenv("GRASS_MISCDIR"), "demolocation") # Find out if startup location exists if os.path.exists(startup_location): @@ -206,16 +205,16 @@ def acquire_mapset_lock( :param message_callback: callback to show messages when locked :param env: system environment variables - The function assumes the `GISBASE` variable is in the environment. The variable is - used to find the lock program. If *env* is not provided, - :external:py:data:`os.environ` is used. + The function assumes the `GRASS_ETCBINDIR` variable is in the environment. + The variable is used to find the lock program. If *env* is not provided, + `os.environ` is used. """ if process_id is None: process_id = os.getpid() if not env: env = os.environ lock_file = os.path.join(mapset_path, ".gislock") - locker_path = os.path.join(env["GISBASE"], "etc", "lock") + locker_path = os.path.join(env["GRASS_ETCBINDIR"], "lock") total_sleep = 0 try_number = 0 initial_sleep = min(initial_sleep, timeout) diff --git a/python/grass/app/resource_paths.py b/python/grass/app/resource_paths.py index df4721d5724..d19cb450981 100644 --- a/python/grass/app/resource_paths.py +++ b/python/grass/app/resource_paths.py @@ -21,3 +21,16 @@ GRASS_PREFIX = "@GRASS_PREFIX@" GISBASE = "@GISBASE_INSTALL_PATH@" + +GRASS_COLORSDIR = "@GRASS_COLORSDIR@" +GRASS_DOCDIR = "@GRASS_DOCDIR@" +GRASS_ETCBINDIR = "@GRASS_ETCBINDIR@" +GRASS_ETCDIR = "@GRASS_ETCDIR@" +GRASS_FONTSDIR = "@GRASS_FONTSDIR@" +GRASS_GRAPHICSDIR = "@GRASS_GRAPHICSDIR@" +GRASS_GUIRESDIR = "@GRASS_GUIRESDIR@" +GRASS_GUISCRIPTDIR = "@GRASS_GUISCRIPTDIR@" +GRASS_GUIWXDIR = "@GRASS_GUIWXDIR@" +GRASS_LOCALEDIR = "@GRASS_LOCALEDIR@" +GRASS_MISCDIR = "@GRASS_MISCDIR@" +GRASS_MKDOCSDIR = "@GRASS_MKDOCSDIR@" diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index c20cb215497..28c9f973512 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -35,6 +35,21 @@ def __init__(self, env=None): if env is None: env = os.environ self.env = env + if init_env_vars: + self._gisbase = self.gisbase + self._prefix = self.prefix + self._colors_dir = self.colors_dir + self._doc_dir = self.doc_dir + self._etcbin_dir = self.etcbin_dir + self._etc_dir = self.etc_dir + self._fonts_dir = self.fonts_dir + self._graphics_dir = self.graphics_dir + self._guires_dir = self.guires_dir + self._guiscript_dir = self.guiscript_dir + self._guiwx_dir = self.guiwx_dir + self._locale_dir = self.locale_dir + self._misc_dir = self.misc_dir + self._mkdocs_dir = self.mkdocs_dir @property def version(self): @@ -68,6 +83,54 @@ def gisbase(self): def prefix(self): return self.__get_dir("GRASS_PREFIX") + @property + def colors_dir(self): + return self.__get_dir("GRASS_COLORSDIR") + + @property + def doc_dir(self): + return self.__get_dir("GRASS_DOCDIR") + + @property + def etc_dir(self): + return self.__get_dir("GRASS_ETCDIR") + + @property + def etcbin_dir(self): + return self.__get_dir("GRASS_ETCBINDIR") + + @property + def fonts_dir(self): + return self.__get_dir("GRASS_FONTSDIR") + + @property + def graphics_dir(self): + return self.__get_dir("GRASS_GRAPHICSDIR") + + @property + def guires_dir(self): + return self.__get_dir("GRASS_GUIRESDIR") + + @property + def guiscript_dir(self): + return self.__get_dir("GRASS_GUISCRIPTDIR") + + @property + def guiwx_dir(self): + return self.__get_dir("GRASS_GUIWXDIR") + + @property + def locale_dir(self): + return self.__get_dir("GRASS_LOCALEDIR") + + @property + def misc_dir(self): + return self.__get_dir("GRASS_MISCDIR") + + @property + def mkdocs_dir(self): + return self.__get_dir("GRASS_MKDOCSDIR") + @property def config_projshare(self): return self.env.get("GRASS_PROJSHARE", res_paths.CONFIG_PROJSHARE) diff --git a/python/grass/docs/conf.py b/python/grass/docs/conf.py index bf26c0e74ef..d7939c2a1a3 100644 --- a/python/grass/docs/conf.py +++ b/python/grass/docs/conf.py @@ -19,58 +19,40 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -if not os.getenv("GISBASE"): - sys.exit("GISBASE not defined") -sys.path.insert( - 0, os.path.abspath(os.path.join(os.environ["GISBASE"], "etc", "python", "grass")) -) +if not os.getenv("GRASS_PYDIR"): + sys.exit("GRASS_PYDIR not defined") +sys.path.insert(0, os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass"))) sys.path.insert( 0, - os.path.abspath( - os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "ctypes") - ), + os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass", "ctypes")), ) sys.path.insert( 0, - os.path.abspath( - os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "exceptions") - ), + os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass", "exceptions")), ) sys.path.insert( 0, - os.path.abspath( - os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "gunittest") - ), + os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass", "gunittest")), ) sys.path.insert( 0, - os.path.abspath( - os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "imaging") - ), + os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass", "imaging")), ) sys.path.insert( 0, - os.path.abspath( - os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "pydispatch") - ), + os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass", "pydispatch")), ) sys.path.insert( 0, - os.path.abspath( - os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "pygrass") - ), + os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass", "pygrass")), ) sys.path.insert( 0, - os.path.abspath( - os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "script") - ), + os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass", "script")), ) sys.path.insert( 0, - os.path.abspath( - os.path.join(os.environ["GISBASE"], "etc", "python", "grass", "temporal") - ), + os.path.abspath(os.path.join(os.environ["GRASS_PYDIR"], "grass", "temporal")), ) from grass.script import core # noqa: E402 diff --git a/python/grass/script/setup.py b/python/grass/script/setup.py index d2498beb4a1..3596ffa47f3 100644 --- a/python/grass/script/setup.py +++ b/python/grass/script/setup.py @@ -92,9 +92,6 @@ WINDOWS = sys.platform.startswith("win") MACOS = sys.platform.startswith("darwin") -VERSION_MAJOR = "@GRASS_VERSION_MAJOR@" -VERSION_MINOR = "@GRASS_VERSION_MINOR@" - def write_gisrc(dbase, location, mapset): """Write the ``gisrc`` file and return its path.""" @@ -108,7 +105,7 @@ def write_gisrc(dbase, location, mapset): def set_gui_path(): """Insert wxPython GRASS path to sys.path.""" - gui_path = os.path.join(os.environ["GISBASE"], "gui", "wxpython") + gui_path = os.environ["GRASS_GUIWXDIR"] if gui_path and gui_path not in sys.path: sys.path.insert(0, gui_path) @@ -222,17 +219,21 @@ def setup_runtime_env(gisbase=None, *, env=None): set_executable_paths, set_path_to_python_executable, set_python_path_variable, + RuntimePaths, ) + runtime_paths = RuntimePaths(env=env, init_env_vars=True) + gisbase = runtime_paths.gisbase # Set GISBASE - env["GISBASE"] = gisbase set_executable_paths( install_path=gisbase, - grass_config_dir=get_grass_config_dir(VERSION_MAJOR, VERSION_MINOR, env=env), + grass_config_dir=get_grass_config_dir( + runtime_paths.version_major, runtime_paths.version_minor, env=env + ), env=env, ) set_dynamic_library_path( - variable_name="@LD_LIBRARY_PATH_VAR@", install_path=gisbase, env=env + variable_name=runtime_paths.ld_library_path_var, install_path=gisbase, env=env ) set_python_path_variable(install_path=gisbase, env=env) set_path_to_python_executable(env=env) @@ -383,7 +384,7 @@ def init( # If environment is not provided, use the global one. if not env: env = os.environ - setup_runtime_env(grass_path, env=env) + setup_runtime_env(None, env=env) process_id = os.getpid() env["GIS_LOCK"] = str(process_id) @@ -574,9 +575,10 @@ def clean_temp(env=None): env = os.environ gs.verbose(_("Cleaning up temporary files..."), env=env) - gisbase = env["GISBASE"] call( - [os.path.join(gisbase, "etc", "clean_temp")], stdout=subprocess.DEVNULL, env=env + [os.path.join(env["GRASS_ETCBINDIR"], "clean_temp")], + stdout=subprocess.DEVNULL, + env=env, ) diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 5483a858fbd..acafe1ade30 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -460,7 +460,7 @@ def get_interface_description(cmd): cmd = os.path.splitext(cmd)[0] if cmd == "d.rast3d": - sys.path.insert(0, os.path.join(os.getenv("GISBASE"), "gui", "scripts")) + sys.path.insert(0, os.getenv("GRASS_GUISCRIPTDIR")) p = Popen( [sys.executable, get_real_command(cmd), "--interface-description"], @@ -492,7 +492,7 @@ def get_interface_description(cmd): desc = convert_xml_to_utf8(cmdout) return desc.replace( b"grass-interface.dtd", - os.path.join(os.getenv("GISBASE"), "gui", "xml", "grass-interface.dtd").encode( + os.path.join(os.getenv("GRASS_GUIRESDIR"), "xml", "grass-interface.dtd").encode( "utf-8" ), ) diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 34b9521bb6a..e776e735490 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -434,8 +434,8 @@ def get_lib_path(modname, libname=None): from os import getenv from os.path import isdir, join, sep - if isdir(join(getenv("GISBASE"), "etc", modname)): - path = join(os.getenv("GISBASE"), "etc", modname) + if isdir(join(getenv("GRASS_ETCDIR"), modname)): + path = join(os.getenv("GRASS_ETCDIR"), modname) elif ( getenv("GRASS_ADDON_BASE") and libname diff --git a/python/grass/semantic_label/reader.py b/python/grass/semantic_label/reader.py index d6538e5c940..282ea3e8edc 100644 --- a/python/grass/semantic_label/reader.py +++ b/python/grass/semantic_label/reader.py @@ -22,7 +22,7 @@ class SemanticLabelReader: def __init__(self): self._json_files = glob.glob( - os.path.join(os.environ["GISBASE"], "etc", "i.band.library", "*.json") + os.path.join(os.environ["GRASS_ETCDIR"], "i.band.library", "*.json") ) if not self._json_files: msg = "No semantic label definitions found" diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index 530239aa547..aedee29c32a 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -448,9 +448,7 @@ def get_tgis_database_string(): def get_sql_template_path(): - base = os.getenv("GISBASE") - base_etc = os.path.join(base, "etc") - return os.path.join(base_etc, "sql") + return os.path.join(os.getenv("GRASS_ETCDIR"), "sql") ############################################################################### diff --git a/python/libgrass_interface_generator/CMakeLists.txt b/python/libgrass_interface_generator/CMakeLists.txt index 2da6bfead17..74316a647ae 100644 --- a/python/libgrass_interface_generator/CMakeLists.txt +++ b/python/libgrass_interface_generator/CMakeLists.txt @@ -61,7 +61,6 @@ set(vedit_INCHDRS ${PostgreSQL_INCLUDE_DIRS} ${GDAL_INCLUDE_DIRS}) set(_c_flags ${CMAKE_C_FLAGS}) if(APPLE AND CMAKE_OSX_SYSROOT) string(APPEND _c_flags " --sysroot ${CMAKE_OSX_SYSROOT}") - message("_c_flags") endif() foreach(module ${MODULES}) diff --git a/raster/r.colors/edit_colors.c b/raster/r.colors/edit_colors.c index 6615854b581..5930da91d75 100644 --- a/raster/r.colors/edit_colors.c +++ b/raster/r.colors/edit_colors.c @@ -471,7 +471,7 @@ int edit_colors(int argc, char **argv, int type, const char *maptype, /* check if this style is a percentage style */ /* don't bother with native dirsep as not needed for backwards * compatibility */ - snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), style); + snprintf(path, GPATH_MAX, "%s/%s", G_colors_dir(), style); rule_is_percent = check_percent_rule(path); do_scale = 1; } @@ -491,7 +491,7 @@ int edit_colors(int argc, char **argv, int type, const char *maptype, /* don't bother with native dirsep as not needed for backwards * compatibility */ - snprintf(path, GPATH_MAX, "%s/etc/colors/%s", G_gisbase(), rules); + snprintf(path, GPATH_MAX, "%s/%s", G_colors_dir(), rules); if (!Rast_load_fp_colors(&colors, path, min, max)) G_fatal_error(_("Unable to load rules file <%s>"), rules); diff --git a/raster/r.watershed/front/main.c b/raster/r.watershed/front/main.c index 2d7cd0283bc..2ea844ab2b5 100644 --- a/raster/r.watershed/front/main.c +++ b/raster/r.watershed/front/main.c @@ -261,7 +261,7 @@ int main(int argc, char *argv[]) } /* Build command line */ - snprintf(command, GPATH_MAX, "%s/etc/r.watershed/%s", G_gisbase(), + snprintf(command, GPATH_MAX, "%s/r.watershed/%s", G_etcbin_dir(), flag_seg->answer ? "seg" : "ram"); new_argv[new_argc++] = command; diff --git a/raster/r.watershed/shed/com_line.c b/raster/r.watershed/shed/com_line.c index 287ed7d800e..de509eb21ea 100644 --- a/raster/r.watershed/shed/com_line.c +++ b/raster/r.watershed/shed/com_line.c @@ -53,8 +53,8 @@ int com_line_Gwater(INPUT *input, OUTPUT *output) input->fast = 1; input->com_line_ram = (char *)G_calloc(400, sizeof(char)); prog_name = G_store(RAM_NAME); - snprintf(input->com_line_ram, (400 * sizeof(char)), - "\"%s/etc/water/%s\"", G_gisbase(), RAM_NAME); + snprintf(input->com_line_ram, (400 * sizeof(char)), "\"%s/water/%s\"", + G_etc_dir(), RAM_NAME); fprintf(stderr, "\nIf there is not enough ram for the fast mode (%s) to run,\n", RAM_NAME); @@ -64,15 +64,15 @@ int com_line_Gwater(INPUT *input, OUTPUT *output) input->slow = 1; input->com_line_seg = (char *)G_calloc(400, sizeof(char)); snprintf(input->com_line_seg, (400, sizeof(char)), - "\"%s/etc/water/%s\"", G_gisbase(), SEG_NAME); + "\"%s/water/%s\"", G_etc_dir(), SEG_NAME); } } else { input->slow = 1; prog_name = G_store(SEG_NAME); input->com_line_seg = (char *)G_calloc(400, sizeof(char)); - snprintf(input->com_line_seg, (400, sizeof(char)), - "\"%s/etc/water/%s\"", G_gisbase(), SEG_NAME); + snprintf(input->com_line_seg, (400, sizeof(char)), "\"%s/water/%s\"", + G_etc_dir(), SEG_NAME); } G_message(_("\nIf you hit by itself for the next question, this")); diff --git a/scripts/d.polar/d.polar.py b/scripts/d.polar/d.polar.py index 2d5f6d50a7b..d133ff89d83 100755 --- a/scripts/d.polar/d.polar.py +++ b/scripts/d.polar/d.polar.py @@ -236,7 +236,7 @@ def plot_eps(psout): ########## outf = open(psout, "w") - prolog = os.path.join(os.environ["GISBASE"], "etc", "d.polar", "ps_defs.eps") + prolog = os.path.join(os.environ["GRASS_ETCDIR"], "d.polar", "ps_defs.eps") inf = open(prolog) shutil.copyfileobj(inf, outf) inf.close() diff --git a/scripts/db.test/db.test.py b/scripts/db.test/db.test.py index c4ed7d99c54..45a449ad29d 100755 --- a/scripts/db.test/db.test.py +++ b/scripts/db.test/db.test.py @@ -43,7 +43,7 @@ def main(): dbconn = grassdb.db_connection() gcore.message(_("Using DB driver: %s") % dbconn["driver"]) - infile = os.path.join(os.environ["GISBASE"], "etc", "db.test", test_file) + infile = os.path.join(os.environ["GRASS_ETCDIR"], "db.test", test_file) inf = open(infile) while True: diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py old mode 100644 new mode 100755 index bfe5d33b861..6d4658ded49 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -2355,7 +2355,7 @@ def check_style_file(name): If the files are missing, a warning is issued. """ - dist_file = os.path.join(os.getenv("GISBASE"), "docs", "html", name) + dist_file = os.path.join(os.getenv("GRASS_DOCDIR"), name) addons_file = os.path.join(options["prefix"], "docs", "html", name) try: diff --git a/scripts/g.search.modules/g.search.modules.py b/scripts/g.search.modules/g.search.modules.py index 76a8c4d1f3e..1fc84246cdd 100755 --- a/scripts/g.search.modules/g.search.modules.py +++ b/scripts/g.search.modules/g.search.modules.py @@ -196,8 +196,8 @@ def _search_module( :return dict: modules """ - WXGUIDIR = os.path.join(os.getenv("GISBASE"), "gui", "wxpython") - filename = os.path.join(WXGUIDIR, "xml", "module_items.xml") + GUIRESDIR = os.getenv("GRASS_GUIRESDIR") + filename = os.path.join(GUIRESDIR, "wxpython", "xml", "module_items.xml") with open(filename) as menudata_file: menudata = ET.parse(menudata_file) @@ -213,7 +213,7 @@ def _search_module( items.extend(addon_items) # add system-wide installed addons to modules list - filename_addons_s = os.path.join(os.getenv("GISBASE"), "modules.xml") + filename_addons_s = os.path.join(os.getenv("GRASS_MISCDIR"), "modules.xml") if os.path.isfile(filename_addons_s): with open(filename_addons_s) as addon_menudata_file_s: addon_menudata_s = ET.parse(addon_menudata_file_s) diff --git a/scripts/wxpyimgview/wxpyimgview.py b/scripts/wxpyimgview/wxpyimgview.py old mode 100644 new mode 100755 index ac848791c80..6702f23d24b --- a/scripts/wxpyimgview/wxpyimgview.py +++ b/scripts/wxpyimgview/wxpyimgview.py @@ -45,6 +45,5 @@ image = options["image"] percent = options["percent"] python = os.getenv("GRASS_PYTHON", "python") - gisbase = os.environ["GISBASE"] - script = os.path.join(gisbase, "etc", "wxpyimgview_gui.py") + script = os.path.join(os.environ["GRASS_ETCBINDIR"], "wxpyimgview_gui.py") os.execlp(python, script, script, image, percent) diff --git a/utils/thumbnails.py b/utils/thumbnails.py index c04c806bb13..8e670172919 100755 --- a/utils/thumbnails.py +++ b/utils/thumbnails.py @@ -158,7 +158,7 @@ def main(): os.environ["GRASS_OVERWRITE"] = "1" - color_dir = os.path.join(os.environ["GISBASE"], "etc", "colors") + color_dir = os.environ["GRASS_COLORSDIR"] output_dir = sys.argv[1] if not os.path.exists(output_dir): diff --git a/vector/v.label.sa/font.c b/vector/v.label.sa/font.c index 73dbaadf5e4..57593535ce9 100644 --- a/vector/v.label.sa/font.c +++ b/vector/v.label.sa/font.c @@ -54,7 +54,7 @@ struct GFONT_CAP *find_font_from_freetypecap(const char *font) capfile); } if (fp == NULL) { - snprintf(file, sizeof(file), "%s/etc/fontcap", G_gisbase()); + snprintf(file, sizeof(file), "%s/fontcap", G_etc_dir()); if ((fp = fopen(file, "r")) == NULL) G_warning(_("%s: No font definition file"), file); } From 27b9528dd523cd68e0557f983e1f2764ab0afbfa Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Mon, 28 Jul 2025 19:16:17 +0200 Subject: [PATCH 2/7] correct bad rebase --- python/grass/app/runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index 28c9f973512..f2eb1945e48 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -31,7 +31,7 @@ class RuntimePaths: The resource paths are also set as environmental variables. """ - def __init__(self, env=None): + def __init__(self, env=None, init_env_vars=False): if env is None: env = os.environ self.env = env From ff20546ce19c5f075a1802f57886e42ac264c090 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Thu, 9 Oct 2025 12:37:12 -0400 Subject: [PATCH 3/7] Avoid list of variables in the constructor and in the methods, replace by a single dict object at the class level. Use dynamic attributes to avoid repeating the access code. Separate env modification from path retrieval as in #6469. --- python/grass/app/runtime.py | 133 +++++++----------- .../grass/app/tests/grass_app_runtime_test.py | 66 +++++++++ 2 files changed, 120 insertions(+), 79 deletions(-) create mode 100644 python/grass/app/tests/grass_app_runtime_test.py diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index f2eb1945e48..a7e8bc8ba95 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -28,28 +28,47 @@ class RuntimePaths: """Get runtime paths to resources and basic GRASS build properties - The resource paths are also set as environmental variables. + The resource paths are accessible as attributes (e.g., `.gisbase`, `.etc_dir`) + and can optionally be exported to environment variables. + + Example: + + >>> paths = RuntimePaths(init_env_vars=True) + >>> paths.etc_dir + '/usr/lib/grass/etc' + >>> os.environ["GRASS_ETCDIR"] + '/usr/lib/grass/etc' """ + # Mapping of attribute names to environment variable names + _env_vars = { + "gisbase": "GISBASE", + "prefix": "GRASS_PREFIX", + "colors_dir": "GRASS_COLORSDIR", + "doc_dir": "GRASS_DOCDIR", + "etc_dir": "GRASS_ETCDIR", + "etcbin_dir": "GRASS_ETCBINDIR", + "fonts_dir": "GRASS_FONTSDIR", + "graphics_dir": "GRASS_GRAPHICSDIR", + "guires_dir": "GRASS_GUIRESDIR", + "guiscript_dir": "GRASS_GUISCRIPTDIR", + "guiwx_dir": "GRASS_GUIWXDIR", + "locale_dir": "GRASS_LOCALEDIR", + "misc_dir": "GRASS_MISCDIR", + "mkdocs_dir": "GRASS_MKDOCSDIR", + } + def __init__(self, env=None, init_env_vars=False): if env is None: env = os.environ self.env = env if init_env_vars: - self._gisbase = self.gisbase - self._prefix = self.prefix - self._colors_dir = self.colors_dir - self._doc_dir = self.doc_dir - self._etcbin_dir = self.etcbin_dir - self._etc_dir = self.etc_dir - self._fonts_dir = self.fonts_dir - self._graphics_dir = self.graphics_dir - self._guires_dir = self.guires_dir - self._guiscript_dir = self.guiscript_dir - self._guiwx_dir = self.guiwx_dir - self._locale_dir = self.locale_dir - self._misc_dir = self.misc_dir - self._mkdocs_dir = self.mkdocs_dir + self.set_env_vars() + + def set_env_vars(self): + """Populate all GRASS-related environment variables.""" + for env_var in self._env_vars.values(): + self.env[env_var] = self.__get_dir(env_var) @property def version(self): @@ -75,78 +94,34 @@ def grass_exe_name(self): def grass_version_git(self): return res_paths.GRASS_VERSION_GIT - @property - def gisbase(self): - return self.__get_dir("GISBASE") - - @property - def prefix(self): - return self.__get_dir("GRASS_PREFIX") - - @property - def colors_dir(self): - return self.__get_dir("GRASS_COLORSDIR") - - @property - def doc_dir(self): - return self.__get_dir("GRASS_DOCDIR") - - @property - def etc_dir(self): - return self.__get_dir("GRASS_ETCDIR") - - @property - def etcbin_dir(self): - return self.__get_dir("GRASS_ETCBINDIR") - - @property - def fonts_dir(self): - return self.__get_dir("GRASS_FONTSDIR") - - @property - def graphics_dir(self): - return self.__get_dir("GRASS_GRAPHICSDIR") - - @property - def guires_dir(self): - return self.__get_dir("GRASS_GUIRESDIR") - - @property - def guiscript_dir(self): - return self.__get_dir("GRASS_GUISCRIPTDIR") - - @property - def guiwx_dir(self): - return self.__get_dir("GRASS_GUIWXDIR") - - @property - def locale_dir(self): - return self.__get_dir("GRASS_LOCALEDIR") - - @property - def misc_dir(self): - return self.__get_dir("GRASS_MISCDIR") - - @property - def mkdocs_dir(self): - return self.__get_dir("GRASS_MKDOCSDIR") - @property def config_projshare(self): return self.env.get("GRASS_PROJSHARE", res_paths.CONFIG_PROJSHARE) + def __getattr__(self, name): + """Access paths by attributes.""" + if name in self._env_vars: + env_var = self._env_vars[name] + return self.__get_dir(env_var) + msg = f"{type(self).__name__!r} has no attribute {name!r}" + raise AttributeError(msg) + + def __dir__(self): + """List both static and dynamic attributes.""" + base_dir = set(super().__dir__()) + dynamic_dir = set(self._env_vars.keys()) + return sorted(base_dir | dynamic_dir) + def __get_dir(self, env_var): """Get the directory stored in the environmental variable 'env_var' - If the environmental variable not yet set, it is retrived and + If the environmental variable not yet set, it is retrieved and set from resource_paths.""" - if env_var in self.env and len(self.env[env_var]) > 0: - res = os.path.normpath(self.env[env_var]) - else: - path = getattr(res_paths, env_var) - res = os.path.normpath(os.path.join(res_paths.GRASS_PREFIX, path)) - self.env[env_var] = res - return res + if env_var in self.env and self.env[env_var]: + return os.path.normpath(self.env[env_var]) + # Default to path from the installation + path = getattr(res_paths, env_var) + return os.path.normpath(os.path.join(res_paths.GRASS_PREFIX, path)) def get_grass_config_dir(major_version, minor_version, env): diff --git a/python/grass/app/tests/grass_app_runtime_test.py b/python/grass/app/tests/grass_app_runtime_test.py new file mode 100644 index 00000000000..7ed8dec2f1f --- /dev/null +++ b/python/grass/app/tests/grass_app_runtime_test.py @@ -0,0 +1,66 @@ +import os + +import pytest + +from grass.app.runtime import RuntimePaths + + +def test_attr_access_does_not_modify_env(): + """Accessing attribute should not change environment.""" + env = {} + rp = RuntimePaths(env=env) + assert "GRASS_COLORSDIR" not in env + value = rp.colors_dir # access the attribute + assert value + assert "GRASS_COLORSDIR" not in env, "env was modified unexpectedly" + + +def test_explicit_env_vars_set(): + """Explicit call should set the env vars.""" + env = {} + paths = RuntimePaths(env=env) + paths.set_env_vars() + assert "GRASS_COLORSDIR" in env + assert env["GRASS_COLORSDIR"] == paths.colors_dir + + +def test_constructor_parameter_env_vars_set(): + """Constructor with parameter should set the env vars.""" + env = {} + paths = RuntimePaths(env=env, init_env_vars=True) + assert "GRASS_COLORSDIR" in env + assert env["GRASS_COLORSDIR"] == paths.colors_dir + + +def test_dir_lists_dynamic_attributes_but_does_not_modify_env(): + """dir() should show dynamic attrs but not set env.""" + env = {} + paths = RuntimePaths(env=env) + listing = dir(paths) + assert "colors_dir" in listing + assert "GRASS_COLORSDIR" not in env, "dir() should not modify env" + + +def test_existing_env_value_is_respected(): + """If env already contains GRASS_COLORSDIR, its value is used.""" + value = "/custom/colors" + env = {"GRASS_COLORSDIR": value} + paths = RuntimePaths(env=env) + assert paths.colors_dir == os.path.normpath(value) + assert env["GRASS_COLORSDIR"] == value + + +def test_invalid_attribute_raises(): + """Unknown attribute access should raise AttributeError.""" + rp = RuntimePaths(env={}) + with pytest.raises(AttributeError): + assert rp.unknown_attr # avoiding unused value + + +def test_returned_attribute_consistent(): + """Repeated accesses should return the same value.""" + paths = RuntimePaths(env={}) + first = paths.colors_dir + second = paths.colors_dir + assert first == second + assert first == RuntimePaths(env={}).colors_dir From 83fac85af3533366497d9b6d3b81731b4997f317 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 10 Oct 2025 14:59:19 -0400 Subject: [PATCH 4/7] Manually sync to version of RuntimePaths from #6482 --- lib/init/grass.py | 11 ++++++-- python/grass/app/runtime.py | 52 +++++++++++++++++++++++------------- python/grass/script/setup.py | 24 ++++++++--------- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/lib/init/grass.py b/lib/init/grass.py index fae0b5a2640..a5d01824450 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -2118,6 +2118,7 @@ def main() -> None: find_grass_python_package() from grass.app.runtime import RuntimePaths + from grass.script.setup import get_install_path global \ CMD_NAME, \ @@ -2129,14 +2130,20 @@ def main() -> None: GISBASE, \ CONFIG_PROJSHARE - runtime_paths = RuntimePaths(init_env_vars=True) + runtime_paths = RuntimePaths(set_env_variables=True) CMD_NAME = runtime_paths.grass_exe_name GRASS_VERSION = runtime_paths.version GRASS_VERSION_MAJOR = runtime_paths.version_major GRASS_VERSION_MINOR = runtime_paths.version_minor LD_LIBRARY_PATH_VAR = runtime_paths.ld_library_path_var GRASS_VERSION_GIT = runtime_paths.grass_version_git - GISBASE = runtime_paths.gisbase + gisbase = runtime_paths.gisbase + if not os.path.isdir(gisbase): + gisbase = get_install_path(gisbase) + # Set the main prefix again. + # See also grass.script.setup.setup_runtime_env. + runtime_paths = RuntimePaths(set_env_variables=True, prefix=gisbase) + GISBASE = gisbase CONFIG_PROJSHARE = runtime_paths.config_projshare grass_config_dir = create_grass_config_dir() diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index a7e8bc8ba95..c523df512e7 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -17,7 +17,7 @@ import subprocess import sys -from . import resource_paths as res_paths +from . import resource_paths # Get the system name WINDOWS = sys.platform.startswith("win") @@ -33,14 +33,14 @@ class RuntimePaths: Example: - >>> paths = RuntimePaths(init_env_vars=True) + >>> paths = RuntimePaths(set_env_variables=True) >>> paths.etc_dir '/usr/lib/grass/etc' >>> os.environ["GRASS_ETCDIR"] '/usr/lib/grass/etc' """ - # Mapping of attribute names to environment variable names + # Mapping of attribute names to environment variable name except the prefix. _env_vars = { "gisbase": "GISBASE", "prefix": "GRASS_PREFIX", @@ -58,45 +58,59 @@ class RuntimePaths: "mkdocs_dir": "GRASS_MKDOCSDIR", } - def __init__(self, env=None, init_env_vars=False): + def __init__(self, *, env=None, set_env_variables=False, prefix=None): if env is None: env = os.environ self.env = env - if init_env_vars: - self.set_env_vars() + if prefix: + self._custom_prefix = os.path.normpath(prefix) + self._custom_prefix = self._custom_prefix.removesuffix( + resource_paths.GISBASE + ) + else: + self._custom_prefix = None + if set_env_variables: + self.set_env_variables() - def set_env_vars(self): + def set_env_variables(self): """Populate all GRASS-related environment variables.""" + self.env["GRASS_PREFIX"] = self.prefix for env_var in self._env_vars.values(): - self.env[env_var] = self.__get_dir(env_var) + self.env[env_var] = self.__get_dir(env_var, use_env_values=False) @property def version(self): - return res_paths.GRASS_VERSION + return resource_paths.GRASS_VERSION @property def version_major(self): - return res_paths.GRASS_VERSION_MAJOR + return resource_paths.GRASS_VERSION_MAJOR @property def version_minor(self): - return res_paths.GRASS_VERSION_MINOR + return resource_paths.GRASS_VERSION_MINOR @property def ld_library_path_var(self): - return res_paths.LD_LIBRARY_PATH_VAR + return resource_paths.LD_LIBRARY_PATH_VAR @property def grass_exe_name(self): - return res_paths.GRASS_EXE_NAME + return resource_paths.GRASS_EXE_NAME @property def grass_version_git(self): - return res_paths.GRASS_VERSION_GIT + return resource_paths.GRASS_VERSION_GIT @property def config_projshare(self): - return self.env.get("GRASS_PROJSHARE", res_paths.CONFIG_PROJSHARE) + return self.env.get("GRASS_PROJSHARE", resource_paths.CONFIG_PROJSHARE) + + @property + def prefix(self): + if self._custom_prefix: + return self._custom_prefix + return os.path.normpath(resource_paths.GRASS_PREFIX) def __getattr__(self, name): """Access paths by attributes.""" @@ -112,16 +126,16 @@ def __dir__(self): dynamic_dir = set(self._env_vars.keys()) return sorted(base_dir | dynamic_dir) - def __get_dir(self, env_var): + def __get_dir(self, env_var, *, use_env_values=True): """Get the directory stored in the environmental variable 'env_var' If the environmental variable not yet set, it is retrieved and set from resource_paths.""" - if env_var in self.env and self.env[env_var]: + if use_env_values and env_var in self.env and self.env[env_var]: return os.path.normpath(self.env[env_var]) # Default to path from the installation - path = getattr(res_paths, env_var) - return os.path.normpath(os.path.join(res_paths.GRASS_PREFIX, path)) + path = getattr(resource_paths, env_var) + return os.path.normpath(os.path.join(self.prefix, path)) def get_grass_config_dir(major_version, minor_version, env): diff --git a/python/grass/script/setup.py b/python/grass/script/setup.py index 3596ffa47f3..fb67d73b7ff 100644 --- a/python/grass/script/setup.py +++ b/python/grass/script/setup.py @@ -203,16 +203,6 @@ def setup_runtime_env(gisbase=None, *, env=None): If _gisbase_ is not provided, a heuristic is used to find the path to GRASS installation (see the :func:`get_install_path` function for details). """ - if not gisbase: - gisbase = get_install_path() - - # Accept Path objects. - gisbase = os.fspath(gisbase) - - # If environment is not provided, use the global one. - if not env: - env = os.environ - from grass.app.runtime import ( get_grass_config_dir, set_dynamic_library_path, @@ -222,9 +212,19 @@ def setup_runtime_env(gisbase=None, *, env=None): RuntimePaths, ) - runtime_paths = RuntimePaths(env=env, init_env_vars=True) + # If environment is not provided, use the global one. + if not env: + env = os.environ + + runtime_paths = RuntimePaths(env=env, prefix=gisbase) gisbase = runtime_paths.gisbase - # Set GISBASE + if not os.path.isdir(gisbase): + gisbase = get_install_path(gisbase) + # Set the main prefix again. + # See also the main grass executable code. + runtime_paths = RuntimePaths(env=env, prefix=gisbase) + runtime_paths.set_env_variables() + set_executable_paths( install_path=gisbase, grass_config_dir=get_grass_config_dir( From 9cc3fb52510b83b813e7b6f16e8bc9cb6e5956b5 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 17 Oct 2025 22:57:47 -0400 Subject: [PATCH 5/7] Fix and add missing lines from merge --- python/grass/app/tests/grass_app_runtime_test.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/python/grass/app/tests/grass_app_runtime_test.py b/python/grass/app/tests/grass_app_runtime_test.py index 8a0166a5465..33608db983c 100644 --- a/python/grass/app/tests/grass_app_runtime_test.py +++ b/python/grass/app/tests/grass_app_runtime_test.py @@ -88,6 +88,7 @@ def test_attr_access_does_not_modify_env_colors(): def test_attr_access_does_not_modify_env_gisbase(): """Accessing attribute should not change environment.""" + env = {} paths = RuntimePaths(env=env) assert "GISBASE" not in env value = paths.gisbase # access the attribute @@ -99,13 +100,15 @@ def test_explicit_env_vars_set_colors(): """Explicit call should set the env vars.""" env = {} paths = RuntimePaths(env=env) - paths.set_env_vars() + paths.set_env_variables() + assert env["GRASS_COLORSDIR"] == paths.colors_dir assert "GRASS_COLORSDIR" in env def test_explicit_env_vars_set_gisbase(): """Explicit call should set the env vars.""" - assert env["GRASS_COLORSDIR"] == paths.colors_dir + env = {} + paths = RuntimePaths(env=env) paths.set_env_variables() assert "GISBASE" in env assert env["GISBASE"] == paths.gisbase @@ -114,13 +117,14 @@ def test_explicit_env_vars_set_gisbase(): def test_constructor_parameter_env_vars_set_colors(): """Constructor with parameter should set the env vars.""" env = {} - paths = RuntimePaths(env=env, init_env_vars=True) + paths = RuntimePaths(env=env, set_env_variables=True) assert "GRASS_COLORSDIR" in env assert env["GRASS_COLORSDIR"] == paths.colors_dir def test_constructor_parameter_env_vars_set_gisenv(): """Constructor with parameter should set the env vars.""" + env = {} paths = RuntimePaths(env=env, set_env_variables=True) assert "GISBASE" in env assert env["GISBASE"] == paths.gisbase @@ -143,7 +147,7 @@ def test_dir_lists_dynamic_attributes_but_does_not_modify_env_gisbase(): assert "gisbase" in listing assert "GISBASE" not in env, "dir() should not modify env" - + def test_existing_env_value_is_respected_colors(): """If env already contains GRASS_COLORSDIR, its value is used.""" value = "/custom/colors" @@ -177,7 +181,7 @@ def test_returned_attribute_consistent_colors(): assert first == second assert first == RuntimePaths(env={}).colors_dir - + def test_returned_attribute_consistent_gisbase(): """Repeated accesses should return the same value.""" paths = RuntimePaths(env={}) From 268cbcd49c999116cb5674993ae501d7f502ba34 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 17 Oct 2025 23:04:46 -0400 Subject: [PATCH 6/7] Pass the initial path to setup runtime as done on main (relevant when it is a user-provided path) --- python/grass/script/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/grass/script/setup.py b/python/grass/script/setup.py index b6e9127d3aa..e6b93b514c5 100644 --- a/python/grass/script/setup.py +++ b/python/grass/script/setup.py @@ -397,7 +397,7 @@ def init( # If environment is not provided, use the global one. if not env: env = os.environ - setup_runtime_env(None, env=env) + setup_runtime_env(grass_path, env=env) process_id = os.getpid() env["GIS_LOCK"] = str(process_id) From cfd4dfe448ad56b4f3aea2b7dcde4e10cf607d0e Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 17 Oct 2025 23:42:03 -0400 Subject: [PATCH 7/7] Always fail when g.proj gives non-zero return code (should be moved to a separate PR) --- python/grass/script/core.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 10ed0a43c1c..0e814082bd6 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -2107,8 +2107,15 @@ def local_env(): try_remove(tmp_gisrc) tmp_gisrc = None - if ps.returncode != 0 and error: - raise ScriptError(repr(error)) + if ps.returncode != 0: + raise ScriptError( + repr(error) + if error + else ( + f"g.proj failed with return code {ps.returncode}, " + "but no error message was produced" + ) + ) # If a session was created for messages, but not used for subprocesses, # we still need to clean it up.