diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-02-11 18:50:53 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-11 18:50:53 +0100 |
| commit | 595aafef26bc7878cb4d708cb682e4944e5f7fbb (patch) | |
| tree | c862cb9d4206f3be8bc11b9f28855abe43cff018 | |
| parent | a10b5edfad805b1e09d88b31b953707412440b09 (diff) | |
| parent | 29583196dfae2565a35c7a8759cc8a5640d6a2b1 (diff) | |
| download | PROJ-595aafef26bc7878cb4d708cb682e4944e5f7fbb.tar.gz PROJ-595aafef26bc7878cb4d708cb682e4944e5f7fbb.zip | |
Merge pull request #1921 from rouault/pj_get_relative_share_proj
Use relative directory to locate PROJ resource files.
| -rw-r--r-- | cmake/ProjConfig.cmake | 8 | ||||
| -rw-r--r-- | cmake/proj_config.cmake.in | 3 | ||||
| -rw-r--r-- | configure.ac | 1 | ||||
| -rw-r--r-- | docs/source/resource_files.rst | 19 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 2 | ||||
| -rw-r--r-- | src/apps/projsync.cpp | 7 | ||||
| -rw-r--r-- | src/filemanager.cpp | 115 | ||||
| -rw-r--r-- | src/filemanager.hpp | 2 | ||||
| -rw-r--r-- | src/lib_proj.cmake | 4 | ||||
| -rw-r--r-- | src/proj_internal.h | 1 | ||||
| -rwxr-xr-x | travis/install.sh | 16 |
11 files changed, 136 insertions, 42 deletions
diff --git a/cmake/ProjConfig.cmake b/cmake/ProjConfig.cmake index 661e6e4f..fa085384 100644 --- a/cmake/ProjConfig.cmake +++ b/cmake/ProjConfig.cmake @@ -25,9 +25,11 @@ check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS) check_function_exists(localeconv HAVE_LOCALECONV) check_function_exists(strerror HAVE_STRERROR) - -# check libm need on unix -check_library_exists(m ceil "" HAVE_LIBM) +if(NOT WIN32) + check_library_exists(dl dladdr "" HAVE_LIBDL) + # check libm need on unix + check_library_exists(m ceil "" HAVE_LIBM) +endif() set(PACKAGE "proj") set(PACKAGE_BUGREPORT "https://github.com/OSGeo/PROJ/issues") diff --git a/cmake/proj_config.cmake.in b/cmake/proj_config.cmake.in index d8385ce9..ddb24347 100644 --- a/cmake/proj_config.cmake.in +++ b/cmake/proj_config.cmake.in @@ -4,6 +4,9 @@ /* Define to 1 if you have the <inttypes.h> header file. */ #cmakedefine HAVE_INTTYPES_H 1 +/* Define to 1 if you have the `dl' library (-ldl). */ +#cmakedefine HAVE_LIBDL 1 + /* Define to 1 if you have the `m' library (-lm). */ #cmakedefine HAVE_LIBM 1 diff --git a/configure.ac b/configure.ac index fba41302..ea8c7eb4 100644 --- a/configure.ac +++ b/configure.ac @@ -199,6 +199,7 @@ AC_SEARCH_LIBS([sqrt], [m]) AC_CHECK_FUNC(localeconv, [AC_DEFINE(HAVE_LOCALECONV,1,[Define to 1 if you have localeconv])]) AC_CHECK_FUNCS([strerror]) +AC_CHECK_LIB(dl,dladdr,,,) dnl --------------------------------------------------------------------------- dnl Provide a mechanism to disable real mutex support (if lacking win32 or diff --git a/docs/source/resource_files.rst b/docs/source/resource_files.rst index 28628d30..48120982 100644 --- a/docs/source/resource_files.rst +++ b/docs/source/resource_files.rst @@ -23,8 +23,8 @@ PROJ will attempt to locate its resource files - database, transformation grids or init-files - from several directories. The following paths are checked in order: -- For transformation grids that have an explict relative or absolute path, - the directory specified in the grid filename. +- For resource files that have an explict relative or absolute path, + the directory specified in the filename. - Path resolved by the callback function set with the :c:func:`proj_context_set_file_finder`. If it is set, the next tests @@ -35,7 +35,9 @@ The following paths are checked in order: .. _user_writable_directory: -- The PROJ user writable directory, which is : +- .. versionadded:: 7.0 + + The PROJ user writable directory, which is : * on Windows, ${LOCALAPPDATA}/proj * on MacOSX, ${HOME}/Library/Application Support/proj @@ -45,13 +47,16 @@ The following paths are checked in order: - Path(s) set with by the environment variable :envvar:`PROJ_LIB`. On Linux/MacOSX/Unix, use ``:`` to separate paths. On Windows, ``;`` -- On Windows, the *..\\share\\proj\\* and its contents are found automatically +- .. versionadded:: 7.0 + + The *../share/proj/* and its contents are found automatically at run-time if the installation respects the build structure. That is, the - binaries and proj.dll are installed under *..\\bin\\*, and resource files - are in *..\\share\\proj\\*. + binaries and proj.dll/libproj.so are installed under *../bin/* or *../lib/*, + and resource files are in *../share/proj/*. - A path built into PROJ as its resource installation directory (whose value is - $(pkgdatadir)), for builds using the Makefile build system. Note, however, + $(pkgdatadir) for builds using the Makefile build system or + ${CMAKE_INSTALL_PREFIX}/${DATADIR} for CMake builds). Note, however, that since this is a hard-wired path setting, it only works if the whole PROJ installation is not moved somewhere else. diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index d624a893..01f8204b 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -282,6 +282,7 @@ osgeo::proj::datum::VerticalReferenceFrame::create(osgeo::proj::util::PropertyMa osgeo::proj::datum::VerticalReferenceFrame::realizationMethod() const osgeo::proj::datum::VerticalReferenceFrame::~VerticalReferenceFrame() osgeo::proj::File::~File() +osgeo::proj::FileManager::exists(projCtx_t*, char const*) osgeo::proj::FileManager::open(projCtx_t*, char const*, osgeo::proj::FileAccess) osgeo::proj::File::read_line(unsigned long, bool&, bool&) osgeo::proj::GenericShiftGrid::~GenericShiftGrid() @@ -780,6 +781,7 @@ pj_get_default_ctx pj_get_default_fileapi pj_get_default_searchpaths(projCtx_t*) pj_get_errno_ref +pj_get_relative_share_proj(projCtx_t*) pj_get_release pj_get_spheroid_defn pj_has_inverse diff --git a/src/apps/projsync.cpp b/src/apps/projsync.cpp index 40848f51..61681260 100644 --- a/src/apps/projsync.cpp +++ b/src/apps/projsync.cpp @@ -137,7 +137,12 @@ int main(int argc, char *argv[]) { } else if (arg == "--user-writable-directory") { // do nothing } else if (arg == "--system-directory") { - targetDir = PROJ_LIB; + targetDir = pj_get_relative_share_proj(ctx); +#ifdef PROJ_LIB + if (targetDir.empty()) { + targetDir = PROJ_LIB; + } +#endif } else if (arg == "--target-dir" && i + 1 < argc) { i++; targetDir = argv[i]; diff --git a/src/filemanager.cpp b/src/filemanager.cpp index aea9b11d..a90af572 100644 --- a/src/filemanager.cpp +++ b/src/filemanager.cpp @@ -46,9 +46,15 @@ #include <sys/stat.h> +#include "proj_config.h" + #ifdef _WIN32 #include <shlobj.h> +#include <windows.h> #else +#ifdef HAVE_LIBDL +#include <dlfcn.h> +#endif #include <sys/types.h> #include <unistd.h> #endif @@ -1264,13 +1270,15 @@ static bool is_rel_or_absolute_filename(const char *name) { // --------------------------------------------------------------------------- +static std::string pj_get_relative_share_proj_internal_no_check() { +#if defined(_WIN32) || defined(HAVE_LIBDL) #ifdef _WIN32 - -static std::string pj_get_win32_projlib() { - /* Check if proj.db lieves in a share/proj dir parallel to bin/proj.dll */ - /* Based in - * https://stackoverflow.com/questions/9112893/how-to-get-path-to-executable-in-c-running-on-windows - */ + HMODULE hm = NULL; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)&pj_get_relative_share_proj, &hm) == 0) { + return std::string(); + } DWORD path_size = 1024; @@ -1278,7 +1286,7 @@ static std::string pj_get_win32_projlib() { for (;;) { wout.clear(); wout.resize(path_size); - DWORD result = GetModuleFileNameW(nullptr, &wout[0], path_size - 1); + DWORD result = GetModuleFileNameW(hm, &wout[0], path_size - 1); DWORD last_error = GetLastError(); if (result == 0) { @@ -1292,26 +1300,79 @@ static std::string pj_get_win32_projlib() { break; } } - // Now remove the program's name. It was (example) - // "C:\programs\gmt6\bin\gdal_translate.exe" wout.resize(wcslen(wout.c_str())); std::string out = NS_PROJ::WStringToUTF8(wout); - size_t k = out.size(); - while (k > 0 && out[--k] != '\\') { + constexpr char dir_sep = '\\'; +#else + Dl_info info; + if (!dladdr((const void *)pj_get_relative_share_proj, &info)) { + return std::string(); } - out.resize(k); - - out += "/../share/proj"; + std::string out(info.dli_fname); + constexpr char dir_sep = '/'; + // "optimization" for cmake builds where RUNPATH is set to ${prefix}/lib + out = replaceAll(out, "/bin/../", "/"); +#ifdef __linux + // If we get a filename without any path, this is most likely a static + // binary. Resolve the executable name + if (out.find(dir_sep) == std::string::npos) { + constexpr size_t BUFFER_SIZE = 1024; + std::vector<char> path(BUFFER_SIZE + 1); + ssize_t nResultLen = readlink("/proc/self/exe", &path[0], BUFFER_SIZE); + if (nResultLen >= 0 && static_cast<size_t>(nResultLen) < BUFFER_SIZE) { + out.assign(path.data(), static_cast<size_t>(nResultLen)); + } + } +#endif + if (starts_with(out, "./")) + out = out.substr(2); +#endif + auto pos = out.find_last_of(dir_sep); + if (pos == std::string::npos) { + // The initial path was something like libproj.so" + out = "../share/proj"; + return out; + } + out.resize(pos); + pos = out.find_last_of(dir_sep); + if (pos == std::string::npos) { + // The initial path was something like bin/libproj.so" + out = "share/proj"; + return out; + } + out.resize(pos); + // The initial path was something like foo/bin/libproj.so" + out += "/share/proj"; return out; +#else + return std::string(); +#endif } -// --------------------------------------------------------------------------- +static std::string +pj_get_relative_share_proj_internal_check_exists(PJ_CONTEXT *ctx) { + if (ctx == nullptr) { + ctx = pj_get_default_ctx(); + } + std::string path(pj_get_relative_share_proj_internal_no_check()); + if (!path.empty() && NS_PROJ::FileManager::exists(ctx, path.c_str())) { + return path; + } + return std::string(); +} -static const char *get_path_from_win32_projlib(PJ_CONTEXT *ctx, - const char *name, - std::string &out) { +std::string pj_get_relative_share_proj(PJ_CONTEXT *ctx) { + static std::string path( + pj_get_relative_share_proj_internal_check_exists(ctx)); + return path; +} - out = pj_get_win32_projlib(); +// --------------------------------------------------------------------------- + +static const char *get_path_from_relative_share_proj(PJ_CONTEXT *ctx, + const char *name, + std::string &out) { + out = pj_get_relative_share_proj(ctx); if (out.empty()) { return nullptr; } @@ -1322,8 +1383,6 @@ static const char *get_path_from_win32_projlib(PJ_CONTEXT *ctx, : nullptr; } -#endif - /************************************************************************/ /* pj_open_lib_internal() */ /************************************************************************/ @@ -1442,11 +1501,9 @@ pj_open_lib_internal(projCtx ctx, const char *name, const char *mode, if (fid) break; } -#ifdef _WIN32 /* check if it lives in a ../share/proj dir of the proj dll */ - } else if ((sysname = get_path_from_win32_projlib(ctx, name, fname)) != - nullptr) { -#endif + } else if ((sysname = get_path_from_relative_share_proj( + ctx, name, fname)) != nullptr) { /* or hardcoded path */ } else if ((sysname = proj_lib_name) != nullptr) { fname = sysname; @@ -1503,14 +1560,12 @@ std::vector<std::string> pj_get_default_searchpaths(PJ_CONTEXT *ctx) { if (!envPROJ_LIB.empty()) { ret.push_back(envPROJ_LIB); } -#ifdef _WIN32 if (envPROJ_LIB.empty()) { - const std::string win32Dir = pj_get_win32_projlib(); - if (!win32Dir.empty()) { - ret.push_back(win32Dir); + const std::string relativeSharedProj = pj_get_relative_share_proj(ctx); + if (!relativeSharedProj.empty()) { + ret.push_back(relativeSharedProj); } } -#endif #ifdef PROJ_LIB if (envPROJ_LIB.empty()) { ret.push_back(PROJ_LIB); diff --git a/src/filemanager.hpp b/src/filemanager.hpp index 9446a0bc..233c657c 100644 --- a/src/filemanager.hpp +++ b/src/filemanager.hpp @@ -55,7 +55,7 @@ class FileManager { // "Low-level" interface. static PROJ_DLL std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename, FileAccess access); - static bool exists(PJ_CONTEXT *ctx, const char *filename); + static PROJ_DLL bool exists(PJ_CONTEXT *ctx, const char *filename); static bool mkdir(PJ_CONTEXT *ctx, const char *filename); static bool unlink(PJ_CONTEXT *ctx, const char *filename); static bool rename(PJ_CONTEXT *ctx, const char *oldPath, diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index 3c42f973..d14a48c6 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -415,6 +415,10 @@ if(UNIX) if(M_LIB) target_link_libraries(${PROJ_CORE_TARGET} -lm) endif() + find_library(DL_LIB dl) + if(M_LIB) + target_link_libraries(${PROJ_CORE_TARGET} -ldl) + endif() endif() if(USE_THREAD AND Threads_FOUND AND CMAKE_USE_PTHREADS_INIT) target_link_libraries(${PROJ_CORE_TARGET} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/src/proj_internal.h b/src/proj_internal.h index 557c61cb..0cffb475 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -874,6 +874,7 @@ std::string PROJ_DLL pj_context_get_grid_cache_filename(PJ_CONTEXT *ctx); // For use by projsync std::string PROJ_DLL pj_context_get_user_writable_directory(PJ_CONTEXT *ctx, bool create); void PROJ_DLL pj_context_set_user_writable_directory(PJ_CONTEXT* ctx, const std::string& path); +std::string PROJ_DLL pj_get_relative_share_proj(PJ_CONTEXT *ctx); /* classic public API */ #include "proj_api.h" diff --git a/travis/install.sh b/travis/install.sh index 953a7f0b..b0c16881 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -82,6 +82,14 @@ if [ "$SKIP_BUILDS_WITHOUT_GRID" != "yes" ]; then cd .. + if [ $TRAVIS_OS_NAME != "osx" ]; then + # Check that we can retrieve the resource directory in a relative way after renaming the installation prefix + mkdir /tmp/proj_autoconf_install_from_dist_all_renamed + mv /tmp/proj_autoconf_install_from_dist_all /tmp/proj_autoconf_install_from_dist_all_renamed/subdir + LD_LIBRARY_PATH=/tmp/proj_autoconf_install_from_dist_all_renamed/subdir/lib /tmp/proj_autoconf_install_from_dist_all_renamed/subdir/bin/projsync --source-id ? --dry-run --system-directory || /bin/true + LD_LIBRARY_PATH=/tmp/proj_autoconf_install_from_dist_all_renamed/subdir/lib /tmp/proj_autoconf_install_from_dist_all_renamed/subdir/bin/projsync --source-id ? --dry-run --system-directory 2>/dev/null | grep "Downloading from https://cdn.proj.org into /tmp/proj_autoconf_install_from_dist_all_renamed/subdir/share/proj" + fi + # cmake build from generated tarball mkdir build_cmake cd build_cmake @@ -92,6 +100,14 @@ if [ "$SKIP_BUILDS_WITHOUT_GRID" != "yes" ]; then find /tmp/proj_cmake_install cd .. + if [ $TRAVIS_OS_NAME != "osx" ]; then + # Check that we can retrieve the resource directory in a relative way after renaming the installation prefix + mkdir /tmp/proj_cmake_install_renamed + mv /tmp/proj_cmake_install /tmp/proj_cmake_install_renamed/subdir + /tmp/proj_cmake_install_renamed/subdir/bin/projsync --source-id ? --dry-run --system-directory || /bin/true + /tmp/proj_cmake_install_renamed/subdir/bin/projsync --source-id ? --dry-run --system-directory 2>/dev/null | grep "Downloading from https://cdn.proj.org into /tmp/proj_cmake_install_renamed/subdir/share/proj" + fi + # return to root cd ../.. fi |
