Skip to content

Commit

Permalink
Use relative directory to locate PROJ resource files.
Browse files Browse the repository at this point in the history
Fixes #1490

This is an extension of the Window-specific logic added recently to
Unix builds. This reuses parts of proposed past commit
82a07e5
(credits to @abellgithub)
  • Loading branch information
rouault committed Feb 11, 2020
1 parent a10b5ed commit 2958319
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 42 deletions.
8 changes: 5 additions & 3 deletions cmake/ProjConfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
3 changes: 3 additions & 0 deletions cmake/proj_config.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 12 additions & 7 deletions docs/source/resource_files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions scripts/reference_exported_symbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion src/apps/projsync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
115 changes: 85 additions & 30 deletions src/filemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1264,21 +1270,23 @@ 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;

std::wstring wout;
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) {
Expand All @@ -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;
}
Expand All @@ -1322,8 +1383,6 @@ static const char *get_path_from_win32_projlib(PJ_CONTEXT *ctx,
: nullptr;
}

#endif

/************************************************************************/
/* pj_open_lib_internal() */
/************************************************************************/
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/filemanager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions src/lib_proj.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
1 change: 1 addition & 0 deletions src/proj_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 16 additions & 0 deletions travis/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 2958319

Please sign in to comment.