LCOV - code coverage report
Current view: top level - apps - gdalgetgdalpath.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 60 72 83.3 %
Date: 2025-08-01 10:10:57 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Return the path of the "gdal" binary
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_config.h"
      14             : 
      15             : #if HAVE_DL_ITERATE_PHDR
      16             : #if !defined(_GNU_SOURCE)
      17             : #define _GNU_SOURCE
      18             : #endif
      19             : #include <link.h>
      20             : 
      21             : #elif defined(__MACH__) && defined(__APPLE__)
      22             : #include <mach-o/dyld.h>
      23             : 
      24             : #endif
      25             : 
      26             : #include "cpl_spawn.h"
      27             : #include "cpl_vsi_virtual.h"
      28             : #include "gdal.h"
      29             : #include "gdalgetgdalpath.h"
      30             : 
      31             : #include <cassert>
      32             : 
      33             : /************************************************************************/
      34             : /*                   GDALGetGDALPathDLIterateCbk()                      */
      35             : /************************************************************************/
      36             : 
      37             : #if HAVE_DL_ITERATE_PHDR && !defined(STATIC_BUILD)
      38             : 
      39          93 : static int GDALGetGDALPathDLIterateCbk(struct dl_phdr_info *info,
      40             :                                        size_t /*size*/, void *data)
      41             : {
      42          93 :     if (info->dlpi_name && strstr(info->dlpi_name, "/libgdal.so."))
      43             :     {
      44           3 :         *static_cast<std::string *>(data) = info->dlpi_name;
      45           3 :         return 1;
      46             :     }
      47          90 :     return 0;  // continue iteration
      48             : }
      49             : 
      50             : #endif
      51             : 
      52             : /************************************************************************/
      53             : /*                         GDALGetGDALPath()                            */
      54             : /************************************************************************/
      55             : 
      56             : /** Return the path of the "gdal" binary, or an empty string if it cannot be
      57             :  * found.
      58             :  *
      59             :  * The GDAL_PATH configuration option may be set to point to the directory where
      60             :  * the GDAL binary is located.
      61             :  */
      62           8 : std::string GDALGetGDALPath()
      63             : {
      64           8 :     const char *pszGDAL_PATH = CPLGetConfigOption("GDAL_PATH", nullptr);
      65           8 :     if (pszGDAL_PATH)
      66             :     {
      67             :         VSIStatBufL sStat;
      68           5 :         for (const char *pszProgramName : {"gdal"
      69             : #ifdef _WIN32
      70             :                                            ,
      71             :                                            "gdal.exe"
      72             : #endif
      73          10 :              })
      74             :         {
      75             :             std::string osPath =
      76           5 :                 CPLFormFilenameSafe(pszGDAL_PATH, pszProgramName, nullptr);
      77           5 :             if (VSIStatL(osPath.c_str(), &sStat) == 0)
      78           0 :                 return osPath;
      79             :         }
      80           5 :         CPLError(CE_Failure, CPLE_AppDefined,
      81             :                  "No 'gdal' binary can be found in '%s'", pszGDAL_PATH);
      82           5 :         return std::string();
      83             :     }
      84             : 
      85           3 :     constexpr int MAXPATH_SIZE = 4096;
      86           6 :     std::string osPath;
      87           3 :     osPath.resize(MAXPATH_SIZE);
      88           3 :     if (CPLGetExecPath(osPath.data(), MAXPATH_SIZE))
      89             :     {
      90           3 :         osPath.resize(strlen(osPath.c_str()));
      91           3 :         if (!cpl::ends_with(osPath, "/gdal") &&
      92           6 :             !cpl::ends_with(osPath, "\\gdal") &&
      93           3 :             !cpl::ends_with(osPath, "\\gdal.exe"))
      94             :         {
      95           3 :             osPath.clear();
      96             : #if (HAVE_DL_ITERATE_PHDR || (defined(__MACH__) && defined(__APPLE__))) &&     \
      97             :     !defined(STATIC_BUILD)
      98           6 :             std::string osGDALLib;
      99             : #if HAVE_DL_ITERATE_PHDR
     100           3 :             dl_iterate_phdr(GDALGetGDALPathDLIterateCbk, &osGDALLib);
     101             : #else
     102             :             const uint32_t imageCount = _dyld_image_count();
     103             :             for (uint32_t i = 0; i < imageCount; ++i)
     104             :             {
     105             :                 const char *imageName = _dyld_get_image_name(i);
     106             :                 if (imageName && strstr(imageName, "/libgdal."))
     107             :                 {
     108             :                     osGDALLib = imageName;
     109             :                     break;
     110             :                 }
     111             :             }
     112             : #endif
     113           3 :             if (!osGDALLib.empty() && osGDALLib[0] == '/')
     114             :             {
     115             :                 const std::string osPathOfGDALLib =
     116           6 :                     CPLGetDirnameSafe(osGDALLib.c_str());
     117             :                 std::string osBinFilename = CPLFormFilenameSafe(
     118           3 :                     CPLGetDirnameSafe(osPathOfGDALLib.c_str()).c_str(),
     119           6 :                     "bin/gdal", nullptr);
     120             :                 VSIStatBufL sStat;
     121           3 :                 if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
     122             :                 {
     123             :                     // Case if osGDALLib=/usr/lib/libgdal.so.xxx
     124           0 :                     osPath = std::move(osBinFilename);
     125             :                 }
     126             :                 else
     127             :                 {
     128           6 :                     osBinFilename = CPLFormFilenameSafe(
     129           6 :                         CPLGetDirnameSafe(
     130           6 :                             CPLGetDirnameSafe(osPathOfGDALLib.c_str()).c_str())
     131             :                             .c_str(),
     132           3 :                         "bin/gdal", nullptr);
     133           3 :                     if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
     134             :                     {
     135             :                         // Case if pszLibName=/usr/lib/libgdal.so.xxx
     136           0 :                         osPath = std::move(osBinFilename);
     137             :                     }
     138             :                     else
     139             :                     {
     140           6 :                         osBinFilename = CPLFormFilenameSafe(
     141           3 :                             osPathOfGDALLib.c_str(), "apps/gdal", nullptr);
     142           3 :                         if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
     143             :                         {
     144             :                             // Case if pszLibName=/usr/lib/yyyyy/libgdal.so.xxx
     145           3 :                             osPath = std::move(osBinFilename);
     146             :                         }
     147             :                         else
     148             :                         {
     149           0 :                             osBinFilename = CPLFormFilenameSafe(
     150           0 :                                 osPathOfGDALLib.c_str(), "apps/gdal", nullptr);
     151           0 :                             if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
     152             :                             {
     153             :                                 // Case if pszLibName=/path/to/build_dir/libgdal.so.xxx
     154           0 :                                 osPath = std::move(osBinFilename);
     155             :                             }
     156             :                         }
     157             :                     }
     158             :                 }
     159             :             }
     160             : #endif
     161             :         }
     162           3 :         if (!osPath.empty())
     163             :         {
     164           3 :             CPLDebug("GDAL", "gdal binary found at '%s'", osPath.c_str());
     165             :         }
     166             :     }
     167             :     else
     168             :     {
     169           0 :         osPath.clear();
     170             :     }
     171           3 :     if (osPath.empty())
     172             :     {
     173             :         // Try to locate from the path
     174             : #ifdef _WIN32
     175             :         osPath = "gdal.exe";
     176             : #else
     177           0 :         osPath = "gdal";
     178             : #endif
     179             :     }
     180             : 
     181           3 :     const char *const apszArgv[] = {osPath.c_str(), "--version", nullptr};
     182             :     const std::string osTmpFilenameVersion =
     183           6 :         VSIMemGenerateHiddenFilename(nullptr);
     184             :     auto fpOut = std::unique_ptr<VSIVirtualHandle>(
     185           6 :         VSIFOpenL(osTmpFilenameVersion.c_str(), "wb+"));
     186           3 :     VSIUnlink(osTmpFilenameVersion.c_str());
     187           3 :     CPLAssert(fpOut);
     188           3 :     CPLSpawn(apszArgv, nullptr, fpOut.get(), /* bDisplayErr = */ false);
     189           3 :     const auto nPos = fpOut->Tell();
     190           6 :     std::string osVersion;
     191           3 :     osVersion.resize(128);
     192           3 :     if (nPos > 0 && nPos < osVersion.size())
     193             :     {
     194           3 :         osVersion.resize(static_cast<size_t>(nPos));
     195           3 :         fpOut->Seek(0, SEEK_SET);
     196           3 :         fpOut->Read(osVersion.data(), 1, osVersion.size());
     197           9 :         for (const char ch : {'\n', '\r'})
     198             :         {
     199           6 :             if (!osVersion.empty() && osVersion.back() == ch)
     200             :             {
     201           3 :                 osVersion.pop_back();
     202             :             }
     203             :         }
     204           3 :         if (osVersion == GDALVersionInfo(""))
     205             :         {
     206           3 :             return osPath;
     207             :         }
     208             :         else
     209             :         {
     210           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     211             :                      "'%s --version' returned '%s', whereas '%s' "
     212             :                      "expected. Make sure the gdal binary corresponding "
     213             :                      "to the version of the libgdal of the current "
     214             :                      "process is in the PATH environment variable",
     215             :                      osPath.c_str(), osVersion.c_str(), GDALVersionInfo(""));
     216             :         }
     217             :     }
     218             :     else
     219             :     {
     220           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     221             :                  "Could not find 'gdal' binary. Make sure it is in the "
     222             :                  "PATH environment variable.");
     223             :     }
     224           0 :     return std::string();
     225             : }

Generated by: LCOV version 1.14