From 9488b26ca5b7ea763db4e210b311bcf3f04233ca Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 2 Dec 2020 15:41:53 +0100 Subject: [PATCH] Add build time option to make PROJ_LIB env var tested last (fixes #2399) If PROJ is built with the PROJ_LIB_ENV_VAR_TRIED_LAST CMake option / --enable-proj-lib-env-var-tried-last configure switch, then the hard-wired path ($prefix/share/proj) will be tried before looking at the environment PROJ_LIB. --- CMakeLists.txt | 7 +++ configure.ac | 13 ++++++ docs/source/resource_files.rst | 5 +++ src/Makefile.am | 2 +- src/filemanager.cpp | 79 +++++++++++++++++++++++++++------- 5 files changed, 89 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ee1444a4fd..344823ee61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,13 @@ if(ENABLE_CURL) endif() endif() +################################################################################ + +option(PROJ_LIB_ENV_VAR_TRIED_LAST "Whether the PROJ_LIB environment variable should be tried after the hardcoded location" OFF) +if(PROJ_LIB_ENV_VAR_TRIED_LAST) + add_definitions(-DPROJ_LIB_ENV_VAR_TRIED_LAST) +endif() + ################################################################################ # threading configuration ################################################################################ diff --git a/configure.ac b/configure.ac index 33f8cc0ca4..cc138aaaff 100644 --- a/configure.ac +++ b/configure.ac @@ -321,6 +321,19 @@ AC_SUBST(CURL_LIBS,$CURL_LIBS) AC_SUBST(CURL_ENABLED_FLAGS,$CURL_ENABLED_FLAGS) AM_CONDITIONAL(HAVE_CURL, [test "x$FOUND_CURL" = "xyes"]) + +dnl --------------------------------------------------------------------------- +dnl proj-lib-env-var-tried-last +dnl --------------------------------------------------------------------------- + +AC_ARG_ENABLE(proj-lib-env-var-tried-last, + AS_HELP_STRING([--enable-proj-lib-env-var-tried-last], + [Whether the PROJ_LIB environment variable should be tried after the hardcoded location [default=no]])) + +if test "x$enable_proj_lib_env_var_tried_last" = "xyes"; then + AC_SUBST(PROJ_LIB_ENV_VAR_TRIED_LAST_FLAGS,-DPROJ_LIB_ENV_VAR_TRIED_LAST) +fi + dnl --------------------------------------------------------------------------- dnl Check for external Google Test dnl --------------------------------------------------------------------------- diff --git a/docs/source/resource_files.rst b/docs/source/resource_files.rst index ae3a808d07..2ae8824fbe 100644 --- a/docs/source/resource_files.rst +++ b/docs/source/resource_files.rst @@ -60,6 +60,11 @@ The following paths are checked in order: that since this is a hard-wired path setting, it only works if the whole PROJ installation is not moved somewhere else. + .. note:: if PROJ is built with the PROJ_LIB_ENV_VAR_TRIED_LAST CMake option / + --enable-proj-lib-env-var-tried-last configure switch, then this + hard-wired path will be tried before looking at the environment + variable :envvar:`PROJ_LIB`. + - The current directory When networking capabilities are enabled, either by API with the diff --git a/src/Makefile.am b/src/Makefile.am index 83ee4adcf3..1c0f18f35b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,7 +6,7 @@ TESTS = geodtest check_PROGRAMS = geodtest AM_CPPFLAGS = -DPROJ_LIB=\"$(pkgdatadir)\" \ - -DMUTEX_@MUTEX_SETTING@ -I$(top_srcdir)/include @SQLITE3_CFLAGS@ @TIFF_CFLAGS@ @TIFF_ENABLED_FLAGS@ @CURL_CFLAGS@ @CURL_ENABLED_FLAGS@ + -DMUTEX_@MUTEX_SETTING@ -I$(top_srcdir)/include @SQLITE3_CFLAGS@ @TIFF_CFLAGS@ @TIFF_ENABLED_FLAGS@ @CURL_CFLAGS@ @CURL_ENABLED_FLAGS@ @PROJ_LIB_ENV_VAR_TRIED_LAST_FLAGS@ AM_CXXFLAGS = @CXX_WFLAGS@ @FLTO_FLAG@ include_HEADERS = proj.h proj_experimental.h proj_constants.h geodesic.h \ diff --git a/src/filemanager.cpp b/src/filemanager.cpp index b51205eb63..ef4229491a 100644 --- a/src/filemanager.cpp +++ b/src/filemanager.cpp @@ -1362,6 +1362,12 @@ static const char *proj_lib_name = nullptr; #endif +#ifdef PROJ_LIB_ENV_VAR_TRIED_LAST +static bool gbPROJ_LIB_ENV_VAR_TRIED_LAST = true; +#else +static bool gbPROJ_LIB_ENV_VAR_TRIED_LAST = false; +#endif + static bool dontReadUserWritableDirectory() { // Env var mostly for testing purposes and being independent from // an existing installation @@ -1448,8 +1454,10 @@ static void *pj_open_lib_internal( sysname = fname.c_str(); } - /* if is environment PROJ_LIB defined */ - else if (!(projLib = NS_PROJ::FileManager::getProjLibEnvVar(ctx)) + /* if the environment PROJ_LIB defined, and *not* tried as last + possibility */ + else if (!gbPROJ_LIB_ENV_VAR_TRIED_LAST && + !(projLib = NS_PROJ::FileManager::getProjLibEnvVar(ctx)) .empty()) { auto paths = NS_PROJ::internal::split(projLib, dirSeparator); for (const auto &path : paths) { @@ -1461,17 +1469,42 @@ static void *pj_open_lib_internal( if (fid) break; } + } + + else if ((sysname = get_path_from_relative_share_proj( + ctx, name, fname)) != nullptr) { /* check if it lives in a ../share/proj dir of the proj dll */ - } else if ((sysname = get_path_from_relative_share_proj( - ctx, name, fname)) != nullptr) { + } else if (proj_lib_name != nullptr && + (fid = open_file( + ctx, + (std::string(proj_lib_name) + DIR_CHAR + name).c_str(), + mode)) != nullptr) { + /* or hardcoded path */ - } else if ((sysname = proj_lib_name) != nullptr) { - fname = sysname; + fname = proj_lib_name; fname += DIR_CHAR; fname += name; sysname = fname.c_str(); + } + + /* if the environment PROJ_LIB defined, and tried as last possibility */ + else if (gbPROJ_LIB_ENV_VAR_TRIED_LAST && + !(projLib = NS_PROJ::FileManager::getProjLibEnvVar(ctx)) + .empty()) { + auto paths = NS_PROJ::internal::split(projLib, dirSeparator); + for (const auto &path : paths) { + fname = path; + fname += DIR_CHAR; + fname += name; + sysname = fname.c_str(); + fid = open_file(ctx, sysname, mode); + if (fid) + break; + } + } + + else { /* just try it bare bones */ - } else { sysname = name; } @@ -1516,21 +1549,35 @@ std::vector pj_get_default_searchpaths(PJ_CONTEXT *ctx) { ignoreUserWritableDirectory[0] == '\0') { ret.push_back(proj_context_get_user_writable_directory(ctx, false)); } + const std::string envPROJ_LIB = NS_PROJ::FileManager::getProjLibEnvVar(ctx); - if (!envPROJ_LIB.empty()) { - ret.push_back(envPROJ_LIB); - } - if (envPROJ_LIB.empty()) { - const std::string relativeSharedProj = pj_get_relative_share_proj(ctx); + const std::string relativeSharedProj = pj_get_relative_share_proj(ctx); + + if (gbPROJ_LIB_ENV_VAR_TRIED_LAST) { +/* Situation where PROJ_LIB environment variable is tried in last */ +#ifdef PROJ_LIB + ret.push_back(PROJ_LIB); +#endif if (!relativeSharedProj.empty()) { ret.push_back(relativeSharedProj); } - } + if (!envPROJ_LIB.empty()) { + ret.push_back(envPROJ_LIB); + } + } else { + /* Situation where PROJ_LIB environment variable is used if defined */ + if (!envPROJ_LIB.empty()) { + ret.push_back(envPROJ_LIB); + } else { + if (!relativeSharedProj.empty()) { + ret.push_back(relativeSharedProj); + } #ifdef PROJ_LIB - if (envPROJ_LIB.empty()) { - ret.push_back(PROJ_LIB); - } + ret.push_back(PROJ_LIB); #endif + } + } + return ret; }