diff --git a/cmake/modules/copy_python_files_in_subdir.cmake b/cmake/modules/copy_python_files_in_subdir.cmake index be477f6066c..0cd33a7e655 100644 --- a/cmake/modules/copy_python_files_in_subdir.cmake +++ b/cmake/modules/copy_python_files_in_subdir.cmake @@ -9,9 +9,12 @@ SPDX-License-Identifier: GPL-2.0-or-later #]] function(copy_python_files_in_subdir dir_name dst_prefix) - cmake_parse_arguments(G "PRE_BUILD;PRE_LINK;POST_BUILD" "TARGET" "" ${ARGN}) + cmake_parse_arguments(G "PRE_BUILD;PRE_LINK;POST_BUILD" "TARGET" "EXCLUDE" ${ARGN}) file(GLOB PY_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${dir_name}/*.py") + foreach(exclude_file ${G_EXCLUDE}) + list(FILTER PY_FILES EXCLUDE REGEX ".*/${exclude_file}") + endforeach() if(DEFINED G_TARGET) if(${G_PRE_BUILD}) diff --git a/include/Make/Install.make b/include/Make/Install.make index 799a695e80d..44fcd0dcf34 100644 --- a/include/Make/Install.make +++ b/include/Make/Install.make @@ -95,11 +95,14 @@ FONTCAP = etc/fontcap TMPGISRC = demolocation/.grassrc$(GRASS_VERSION_MAJOR)$(GRASS_VERSION_MINOR) PLATMAKE = include/Make/Platform.make GRASSMAKE = include/Make/Grass.make +RESOURCE_PATHS = etc/python/grass/app/resource_paths.py real-install: | $(DESTDIR) $(DESTDIR)$(INST_DIR) $(DESTDIR)$(UNIX_BIN) -tar cBCf $(GISBASE) - . | tar xBCf $(DESTDIR)$(INST_DIR) - 2>/dev/null -rm $(DESTDIR)$(INST_DIR)/$(GRASS_NAME).tmp + -rm $(DESTDIR)$(INST_DIR)/$(RESOURCE_PATHS) $(MAKE) $(STARTUP) + $(MAKE) $(DESTDIR)$(INST_DIR)/$(RESOURCE_PATHS) -rm $(DESTDIR)$(INST_DIR)/$(FONTCAP) $(MAKE) $(DESTDIR)$(INST_DIR)/$(FONTCAP) @@ -122,12 +125,17 @@ $(DESTDIR)$(INST_DIR) $(DESTDIR)$(UNIX_BIN): $(MAKE_DIR_CMD) $@ $(STARTUP): $(ARCH_DISTDIR)/$(GRASS_NAME).tmp - sed -e 's#'@GISBASE_INSTALL_PATH@'#'$(INST_DIR)'#g' \ + sed -e 's#'@GRASS_PREFIX@'#'$(INST_DIR)'#g' \ -e 's#'@LD_LIBRARY_PATH_VAR@'#'$(LD_LIBRARY_PATH_VAR)'#g' \ -e 's#'@CONFIG_PROJSHARE@'#'$(PROJSHARE)'#g' \ $< > $@ -$(CHMOD) a+x $@ +$(DESTDIR)$(INST_DIR)/$(RESOURCE_PATHS): $(ARCH_DISTDIR)/resource_paths.py + sed -e 's#'@GRASS_PREFIX@'#'$(INST_DIR)'#g' \ + -e 's#'@GISBASE_INSTALL_PATH@'##g' \ + $< > $@ + define fix_gisbase sed -e 's#$(GISBASE)#$(INST_DIR)#g' $< > $@ endef diff --git a/lib/init/CMakeLists.txt b/lib/init/CMakeLists.txt index 89509db5c69..9f52c2ecf47 100644 --- a/lib/init/CMakeLists.txt +++ b/lib/init/CMakeLists.txt @@ -59,12 +59,14 @@ elseif(WIN32) endif() # configure and install grass.py -set(GISBASE_INSTALL_PATH ${RUNTIME_GISBASE}) +set(GRASS_PREFIX ${OUTDIR}) +set(GRASS_PYDIR ${GRASS_INSTALL_PYDIR}) configure_file(grass.py ${OUTDIR}/${CMAKE_INSTALL_BINDIR}/${START_UP} @ONLY) -set(GISBASE_INSTALL_PATH ${GISBASE}) +set(GRASS_PREFIX ${CMAKE_INSTALL_PREFIX}) configure_file(grass.py ${CMAKE_CURRENT_BINARY_DIR}/${START_UP} @ONLY) -unset(GISBASE_INSTALL_PATH) +unset(GRASS_PYDIR) +unset(GRASS_PREFIX) install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${START_UP} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/lib/init/Makefile b/lib/init/Makefile index 99366b060a9..8a7127189d9 100644 --- a/lib/init/Makefile +++ b/lib/init/Makefile @@ -77,6 +77,8 @@ endif -e 's#@LD_LIBRARY_PATH_VAR@#$(LD_LIBRARY_PATH_VAR)#' \ -e 's#@START_UP@#$(START_UP)#' \ -e 's#@CONFIG_PROJSHARE@#$(PROJSHARE)#' \ + -e 's#@GRASS_PREFIX@#$(RUN_GISBASE)#' \ + -e 's#@GRASS_PYDIR@#etc/python#' \ $< > $@ chmod +x $@ @@ -91,6 +93,7 @@ $(ARCH_DISTDIR)/$(START_UP).tmp: grass.py -e 's#@GRASS_CONFIG_DIR@#$(GRASS_CONFIG_DIR)#' \ -e 's#@LD_LIBRARY_PATH_VAR@#$(LD_LIBRARY_PATH_VAR)#' \ -e 's#@CONFIG_PROJSHARE@#$(PROJSHARE)#' \ + -e 's#@GRASS_PYDIR@#etc/python#' \ $< > $@ $(ETC)/echo$(EXE) $(ETC)/run$(EXE): $(ETC)/%$(EXE): $(OBJDIR)/%.o diff --git a/lib/init/grass.py b/lib/init/grass.py index add393d29e1..11caba10c9d 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -71,6 +71,8 @@ # for wxpath _WXPYTHON_BASE = None +GISBASE = None + try: # Python >= 3.11 ENCODING = locale.getencoding() @@ -81,25 +83,7 @@ print("Default locale not found, using UTF-8") # intentionally not translatable # The "@...@" variables are being substituted during build process -# -# TODO: should GISBASE be renamed to something like GRASS_PATH? -# GISBASE marks complete runtime, so no need to get it here when -# setting it up, possible scenario: existing runtime and starting -# GRASS in that, we want to overwrite the settings, not to take it -# possibly same for GRASS_PROJSHARE and others but maybe not -# -# We need to simultaneously make sure that: -# - we get GISBASE from os.environ if it is defined (doesn't this mean that we are -# already inside a GRASS session? If we are, why do we need to run this script -# again???). -# - GISBASE exists as an ENV variable -# -# pmav99: Ugly as hell, but that's what the code before the refactoring was doing. -if "GISBASE" in os.environ and len(os.getenv("GISBASE")) > 0: - GISBASE = os.path.normpath(os.environ["GISBASE"]) -else: - GISBASE = os.path.normpath("@GISBASE_INSTALL_PATH@") - os.environ["GISBASE"] = GISBASE + CMD_NAME = "@START_UP@" GRASS_VERSION = "@GRASS_VERSION_NUMBER@" GRASS_VERSION_MAJOR = "@GRASS_VERSION_MAJOR@" @@ -2103,9 +2087,16 @@ def validate_cmdline(params: Parameters) -> None: def find_grass_python_package() -> None: """Find path to grass package and add it to path""" - if os.path.exists(gpath("etc", "python")): - path_to_package = gpath("etc", "python") - sys.path.append(path_to_package) + GRASS_PREFIX = "@GRASS_PREFIX@" + + if "GRASS_PYDIR" in os.environ and len(os.getenv("GRASS_PYDIR")) > 0: + GRASS_PYDIR = os.path.normpath(os.environ["GRASS_PYDIR"]) + else: + GRASS_PYDIR = os.path.normpath(os.path.join(GRASS_PREFIX, "@GRASS_PYDIR@")) + os.environ["GRASS_PYDIR"] = GRASS_PYDIR + + if os.path.exists(GRASS_PYDIR): + sys.path.append(GRASS_PYDIR) # now we can import stuff from grass package else: # Not translatable because we don't have translations loaded. @@ -2115,6 +2106,12 @@ def find_grass_python_package() -> None: ) raise RuntimeError(msg) + from grass.app import resource_paths + + resource_paths.set_resource_paths() + global GISBASE + GISBASE = resource_paths.GISBASE + def main() -> None: """The main function which does the whole setup and run procedure diff --git a/python/grass/CMakeLists.txt b/python/grass/CMakeLists.txt index eeb0be1deb2..6ed3c8a21a8 100644 --- a/python/grass/CMakeLists.txt +++ b/python/grass/CMakeLists.txt @@ -1,5 +1,4 @@ set(PYDIRS - app benchmark exceptions grassdb @@ -28,12 +27,23 @@ set(PYDIR_GRASS ${GRASS_INSTALL_PYDIR}/grass) foreach(pydir ${PYDIRS}) copy_python_files_in_subdir(${pydir} ${PYDIR_GRASS}) endforeach() +copy_python_files_in_subdir(app ${PYDIR_GRASS} EXCLUDE resource_paths.py) configure_file(__init__.py ${OUTDIR}/${PYDIR_GRASS}/ COPYONLY) configure_file(script/setup.py ${OUTDIR}/${PYDIR_GRASS}/script/setup.py COPYONLY) -set(pydir_targets ${PYDIRS}) +# configure and install resource_paths.py +set(GRASS_PREFIX ${OUTDIR}) +set(GISBASE_INSTALL_PATH ${GISBASE_DIR}) +configure_file(app/resource_paths.py ${OUTDIR}/${PYDIR_GRASS}/app/resource_paths.py @ONLY) + +set(GRASS_PREFIX ${CMAKE_INSTALL_PREFIX}) +configure_file(app/resource_paths.py ${CMAKE_CURRENT_BINARY_DIR}/resource_paths.py @ONLY) +unset(GISBASE_INSTALL_PATH) +unset(GRASS_PREFIX) + +set(pydir_targets ${PYDIRS} app) list(TRANSFORM pydir_targets REPLACE "/" "_") list(TRANSFORM pydir_targets PREPEND "python_") @@ -44,4 +54,7 @@ add_custom_target( set_target_properties(LIB_PYTHON PROPERTIES FOLDER lib) -install(DIRECTORY ${OUTDIR}/${PYDIR_GRASS} DESTINATION ${GRASS_INSTALL_PYDIR}) +install(DIRECTORY ${OUTDIR}/${PYDIR_GRASS} DESTINATION ${GRASS_INSTALL_PYDIR} + PATTERN "*/resource_paths.py" EXCLUDE) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/resource_paths.py + DESTINATION ${GRASS_INSTALL_PYDIR}/grass/app) diff --git a/python/grass/app/Makefile b/python/grass/app/Makefile index eebbcef8f12..9fe5ba95259 100644 --- a/python/grass/app/Makefile +++ b/python/grass/app/Makefile @@ -14,7 +14,20 @@ MODULES = \ PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__) PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__) -default: $(PYFILES) $(PYCFILES) +PYFILES := $(filter-out resource_paths.py,$(PYFILES)) +PYCFILES := $(filter-out resource_paths.pyc,$(PYCFILES)) + +default: $(PYFILES) $(PYCFILES) $(DSTDIR)/resource_paths.py $(ARCH_DISTDIR)/resource_paths.py + +$(DSTDIR)/resource_paths.py: resource_paths.py + rm -f $@ + sed \ + -e 's#@GRASS_PREFIX@#$(RUN_GISBASE)#' \ + -e 's#@GISBASE_INSTALL_PATH@##' \ + $< > $@ + +$(ARCH_DISTDIR)/resource_paths.py: + cp resource_paths.py $@ $(DSTDIR): $(MKDIR) $@ diff --git a/python/grass/app/resource_paths.py b/python/grass/app/resource_paths.py new file mode 100644 index 00000000000..18bc7754949 --- /dev/null +++ b/python/grass/app/resource_paths.py @@ -0,0 +1,31 @@ +""" + +(C) 2025 by Nicklas Larsson and the GRASS Development Team + +This program is free software under the GNU General Public +License (>=v2). Read the file COPYING that comes with GRASS +for details. + + +This is not a stable part of the API. Use at your own risk. + +The "@...@" variables are being substituted during build process + +""" + +import os + +GRASS_PREFIX = None +GISBASE = None + + +def set_resource_paths(): + global GRASS_PREFIX, GISBASE + + GRASS_PREFIX = "@GRASS_PREFIX@" + + if "GISBASE" in os.environ and len(os.getenv("GISBASE")) > 0: + GISBASE = os.path.normpath(os.environ["GISBASE"]) + else: + GISBASE = os.path.normpath(os.path.join(GRASS_PREFIX, "@GISBASE_INSTALL_PATH@")) + os.environ["GISBASE"] = GISBASE