Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalgetgdalpath.cpp
Line
Count
Source
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
static int GDALGetGDALPathDLIterateCbk(struct dl_phdr_info *info,
40
                                       size_t /*size*/, void *data)
41
{
42
    if (info->dlpi_name && strstr(info->dlpi_name, "/libgdal.so."))
43
    {
44
        *static_cast<std::string *>(data) = info->dlpi_name;
45
        return 1;
46
    }
47
    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
std::string GDALGetGDALPath()
63
0
{
64
0
    const char *pszGDAL_PATH = CPLGetConfigOption("GDAL_PATH", nullptr);
65
0
    if (pszGDAL_PATH)
66
0
    {
67
0
        VSIStatBufL sStat;
68
0
        for (const char *pszProgramName : {"gdal"
69
#ifdef _WIN32
70
                                           ,
71
                                           "gdal.exe"
72
#endif
73
0
             })
74
0
        {
75
0
            std::string osPath =
76
0
                CPLFormFilenameSafe(pszGDAL_PATH, pszProgramName, nullptr);
77
0
            if (VSIStatL(osPath.c_str(), &sStat) == 0)
78
0
                return osPath;
79
0
        }
80
0
        CPLError(CE_Failure, CPLE_AppDefined,
81
0
                 "No 'gdal' binary can be found in '%s'", pszGDAL_PATH);
82
0
        return std::string();
83
0
    }
84
85
0
    constexpr int MAXPATH_SIZE = 4096;
86
0
    std::string osPath;
87
0
    osPath.resize(MAXPATH_SIZE);
88
0
    if (CPLGetExecPath(osPath.data(), MAXPATH_SIZE))
89
0
    {
90
0
        osPath.resize(strlen(osPath.c_str()));
91
0
        if (!cpl::ends_with(osPath, "/gdal") &&
92
0
            !cpl::ends_with(osPath, "\\gdal") &&
93
0
            !cpl::ends_with(osPath, "\\gdal.exe"))
94
0
        {
95
0
            osPath.clear();
96
#if (HAVE_DL_ITERATE_PHDR || (defined(__MACH__) && defined(__APPLE__))) &&     \
97
    !defined(STATIC_BUILD)
98
            std::string osGDALLib;
99
#if HAVE_DL_ITERATE_PHDR
100
            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
            if (!osGDALLib.empty() && osGDALLib[0] == '/')
114
            {
115
                const std::string osPathOfGDALLib =
116
                    CPLGetDirnameSafe(osGDALLib.c_str());
117
                std::string osBinFilename = CPLFormFilenameSafe(
118
                    CPLGetDirnameSafe(osPathOfGDALLib.c_str()).c_str(),
119
                    "bin/gdal", nullptr);
120
                VSIStatBufL sStat;
121
                if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
122
                {
123
                    // Case if osGDALLib=/usr/lib/libgdal.so.xxx
124
                    osPath = std::move(osBinFilename);
125
                }
126
                else
127
                {
128
                    osBinFilename = CPLFormFilenameSafe(
129
                        CPLGetDirnameSafe(
130
                            CPLGetDirnameSafe(osPathOfGDALLib.c_str()).c_str())
131
                            .c_str(),
132
                        "bin/gdal", nullptr);
133
                    if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
134
                    {
135
                        // Case if pszLibName=/usr/lib/libgdal.so.xxx
136
                        osPath = std::move(osBinFilename);
137
                    }
138
                    else
139
                    {
140
                        osBinFilename = CPLFormFilenameSafe(
141
                            osPathOfGDALLib.c_str(), "apps/gdal", nullptr);
142
                        if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
143
                        {
144
                            // Case if pszLibName=/usr/lib/yyyyy/libgdal.so.xxx
145
                            osPath = std::move(osBinFilename);
146
                        }
147
                        else
148
                        {
149
                            osBinFilename = CPLFormFilenameSafe(
150
                                osPathOfGDALLib.c_str(), "apps/gdal", nullptr);
151
                            if (VSIStatL(osBinFilename.c_str(), &sStat) == 0)
152
                            {
153
                                // Case if pszLibName=/path/to/build_dir/libgdal.so.xxx
154
                                osPath = std::move(osBinFilename);
155
                            }
156
                        }
157
                    }
158
                }
159
            }
160
#endif
161
0
        }
162
0
        if (!osPath.empty())
163
0
        {
164
0
            CPLDebug("GDAL", "gdal binary found at '%s'", osPath.c_str());
165
0
        }
166
0
    }
167
0
    else
168
0
    {
169
0
        osPath.clear();
170
0
    }
171
0
    if (osPath.empty())
172
0
    {
173
        // Try to locate from the path
174
#ifdef _WIN32
175
        osPath = "gdal.exe";
176
#else
177
0
        osPath = "gdal";
178
0
#endif
179
0
    }
180
181
0
    const char *const apszArgv[] = {osPath.c_str(), "--version", nullptr};
182
0
    const std::string osTmpFilenameVersion =
183
0
        VSIMemGenerateHiddenFilename(nullptr);
184
0
    auto fpOut = std::unique_ptr<VSIVirtualHandle>(
185
0
        VSIFOpenL(osTmpFilenameVersion.c_str(), "wb+"));
186
0
    VSIUnlink(osTmpFilenameVersion.c_str());
187
0
    CPLAssert(fpOut);
188
0
    CPLSpawn(apszArgv, nullptr, fpOut.get(), /* bDisplayErr = */ false);
189
0
    const auto nPos = fpOut->Tell();
190
0
    std::string osVersion;
191
0
    osVersion.resize(128);
192
0
    if (nPos > 0 && nPos < osVersion.size())
193
0
    {
194
0
        osVersion.resize(static_cast<size_t>(nPos));
195
0
        fpOut->Seek(0, SEEK_SET);
196
0
        fpOut->Read(osVersion.data(), 1, osVersion.size());
197
0
        for (const char ch : {'\n', '\r'})
198
0
        {
199
0
            if (!osVersion.empty() && osVersion.back() == ch)
200
0
            {
201
0
                osVersion.pop_back();
202
0
            }
203
0
        }
204
0
        if (osVersion == GDALVersionInfo(""))
205
0
        {
206
0
            return osPath;
207
0
        }
208
0
        else
209
0
        {
210
0
            CPLError(CE_Failure, CPLE_AppDefined,
211
0
                     "'%s --version' returned '%s', whereas '%s' "
212
0
                     "expected. Make sure the gdal binary corresponding "
213
0
                     "to the version of the libgdal of the current "
214
0
                     "process is in the PATH environment variable",
215
0
                     osPath.c_str(), osVersion.c_str(), GDALVersionInfo(""));
216
0
        }
217
0
    }
218
0
    else
219
0
    {
220
0
        CPLError(CE_Failure, CPLE_AppDefined,
221
0
                 "Could not find 'gdal' binary. Make sure it is in the "
222
0
                 "PATH environment variable.");
223
0
    }
224
0
    return std::string();
225
0
}