LCOV - code coverage report
Current view: top level - apps - gdalgetgdalpath.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 62 69 89.9 %
Date: 2025-07-09 17:50:03 Functions: 1 1 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_spawn.h"
      14             : #include "cpl_vsi_virtual.h"
      15             : #include "gdal.h"
      16             : #include "gdalgetgdalpath.h"
      17             : 
      18             : #include <cassert>
      19             : 
      20             : /************************************************************************/
      21             : /*                         GDALGetGDALPath()                            */
      22             : /************************************************************************/
      23             : 
      24             : /** Return the path of the "gdal" binary, or an empty string if it cannot be
      25             :  * found.
      26             :  *
      27             :  * The GDAL_PATH configuration option may be set to point to the directory where
      28             :  * the GDAL binary is located.
      29             :  */
      30           3 : std::string GDALGetGDALPath()
      31             : {
      32           3 :     const char *pszGDAL_PATH = CPLGetConfigOption("GDAL_PATH", nullptr);
      33           3 :     if (pszGDAL_PATH)
      34             :     {
      35             :         VSIStatBufL sStat;
      36           1 :         for (const char *pszProgramName : {"gdal"
      37             : #ifdef _WIN32
      38             :                                            ,
      39             :                                            "gdal.exe"
      40             : #endif
      41           2 :              })
      42             :         {
      43             :             std::string osPath =
      44           1 :                 CPLFormFilenameSafe(pszGDAL_PATH, pszProgramName, nullptr);
      45           1 :             if (VSIStatL(osPath.c_str(), &sStat) == 0)
      46           0 :                 return osPath;
      47             :         }
      48           1 :         CPLError(CE_Failure, CPLE_AppDefined,
      49             :                  "No 'gdal' binary can be found in '%s'", pszGDAL_PATH);
      50           1 :         return std::string();
      51             :     }
      52             : 
      53           2 :     constexpr int MAXPATH_SIZE = 4096;
      54           4 :     std::string osPath;
      55           2 :     osPath.resize(MAXPATH_SIZE);
      56           2 :     if (CPLGetExecPath(osPath.data(), MAXPATH_SIZE))
      57             :     {
      58           2 :         osPath.resize(strlen(osPath.c_str()));
      59           2 :         if (!cpl::ends_with(osPath, "/gdal") &&
      60           4 :             !cpl::ends_with(osPath, "\\gdal") &&
      61           2 :             !cpl::ends_with(osPath, "\\gdal.exe"))
      62             :         {
      63           2 :             osPath.clear();
      64             : #if defined(__linux) && !defined(STATIC_BUILD)
      65           4 :             const CPLStringList aosLines(CSLLoad("/proc/self/maps"));
      66        2741 :             for (const char *pszLine : aosLines)
      67             :             {
      68        2739 :                 const char *pszLibName = strstr(pszLine, "/libgdal.so.");
      69         850 :                 while (pszLibName &&
      70        3159 :                        static_cast<size_t>(pszLibName - pszLine) > 1 &&
      71         430 :                        pszLibName[-1] != ' ')
      72             :                 {
      73         420 :                     --pszLibName;
      74             :                 }
      75        2739 :                 if (pszLibName &&
      76          10 :                     static_cast<size_t>(pszLibName - pszLine) > 1 &&
      77          10 :                     pszLibName[0] == '/')
      78             :                 {
      79             :                     const std::string osPathOfGDALLib =
      80          20 :                         CPLGetDirnameSafe(pszLibName);
      81             :                     std::string osBinFilename = CPLFormFilenameSafe(
      82          10 :                         CPLGetDirnameSafe(osPathOfGDALLib.c_str()).c_str(),
      83          20 :                         "bin/gdal", nullptr);
      84             :                     VSIStatBufL sStat;
      85          10 :                     if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
      86             :                     {
      87             :                         // Case if pszLibName=/usr/lib/libgdal.so.xxx
      88           0 :                         osPath = std::move(osBinFilename);
      89             :                     }
      90             :                     else
      91             :                     {
      92          20 :                         osBinFilename = CPLFormFilenameSafe(
      93          20 :                             CPLGetDirnameSafe(
      94          20 :                                 CPLGetDirnameSafe(osPathOfGDALLib.c_str())
      95             :                                     .c_str())
      96             :                                 .c_str(),
      97          10 :                             "bin/gdal", nullptr);
      98          10 :                         if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
      99             :                         {
     100             :                             // Case if pszLibName=/usr/lib/yyyyy/libgdal.so.xxx
     101           0 :                             osPath = std::move(osBinFilename);
     102             :                         }
     103             :                         else
     104             :                         {
     105          20 :                             osBinFilename = CPLFormFilenameSafe(
     106          10 :                                 osPathOfGDALLib.c_str(), "apps/gdal", nullptr);
     107          10 :                             if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
     108             :                             {
     109             :                                 // Case if pszLibName=/path/to/build_dir/libgdal.so.xxx
     110          10 :                                 osPath = std::move(osBinFilename);
     111             :                             }
     112             :                         }
     113             :                     }
     114             :                 }
     115             :             }
     116             : #endif
     117             :         }
     118           2 :         if (!osPath.empty())
     119             :         {
     120           2 :             CPLDebug("GDAL", "gdal binary found at '%s'", osPath.c_str());
     121             :         }
     122             :     }
     123           2 :     if (osPath.empty())
     124             :     {
     125             :         // Try to locate from the path
     126             : #ifdef _WIN32
     127             :         osPath = "gdal.exe";
     128             : #else
     129           0 :         osPath = "gdal";
     130             : #endif
     131             :     }
     132             : 
     133           2 :     const char *const apszArgv[] = {osPath.c_str(), "--version", nullptr};
     134             :     const std::string osTmpFilenameVersion =
     135           4 :         VSIMemGenerateHiddenFilename(nullptr);
     136             :     auto fpOut = std::unique_ptr<VSIVirtualHandle>(
     137           4 :         VSIFOpenL(osTmpFilenameVersion.c_str(), "wb+"));
     138           2 :     VSIUnlink(osTmpFilenameVersion.c_str());
     139           2 :     CPLAssert(fpOut);
     140           2 :     CPLSpawn(apszArgv, nullptr, fpOut.get(), /* bDisplayErr = */ false);
     141           2 :     const auto nPos = fpOut->Tell();
     142           4 :     std::string osVersion;
     143           2 :     osVersion.resize(128);
     144           2 :     if (nPos > 0 && nPos < osVersion.size())
     145             :     {
     146           2 :         osVersion.resize(static_cast<size_t>(nPos));
     147           2 :         fpOut->Seek(0, SEEK_SET);
     148           2 :         fpOut->Read(osVersion.data(), 1, osVersion.size());
     149           6 :         for (const char ch : {'\n', '\r'})
     150             :         {
     151           4 :             if (!osVersion.empty() && osVersion.back() == ch)
     152             :             {
     153           2 :                 osVersion.pop_back();
     154             :             }
     155             :         }
     156           2 :         if (osVersion == GDALVersionInfo(""))
     157             :         {
     158           2 :             return osPath;
     159             :         }
     160             :         else
     161             :         {
     162           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     163             :                      "'%s --version' returned '%s', whereas '%s' "
     164             :                      "expected. Make sure the gdal binary corresponding "
     165             :                      "to the version of the libgdal of the current "
     166             :                      "process is in the PATH environment variable",
     167             :                      osPath.c_str(), osVersion.c_str(), GDALVersionInfo(""));
     168             :         }
     169             :     }
     170             :     else
     171             :     {
     172           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     173             :                  "Could not find 'gdal' binary. Make sure it is in the "
     174             :                  "PATH environment variable.");
     175             :     }
     176           0 :     return std::string();
     177             : }

Generated by: LCOV version 1.14