environment.cpp 15.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
31
#include "environment.h"

32
33
#include "hostosinfo.h"

34
35
#include <QDir>
#include <QProcess>
36
#include <QProcessEnvironment>
37
#include <QString>
38
39
40
41
42
43
#include <QCoreApplication>

class SystemEnvironment : public Utils::Environment
{
public:
    SystemEnvironment()
44
        : Environment(QProcessEnvironment::systemEnvironment().toStringList())
45
    {
46
47
48
        if (Utils::HostOsInfo::isLinuxHost()) {
            QString ldLibraryPath = value(QLatin1String("LD_LIBRARY_PATH"));
            QDir lib(QCoreApplication::applicationDirPath());
49
            lib.cd(QLatin1String("../lib"));
50
            QString toReplace = lib.path();
51
            lib.cd(QLatin1String("qtcreator"));
52
            toReplace.append(QLatin1Char(':'));
53
54
55
56
57
            toReplace.append(lib.path());

            if (ldLibraryPath.startsWith(toReplace))
                set(QLatin1String("LD_LIBRARY_PATH"), ldLibraryPath.remove(0, toReplace.length()));
        }
58
59
60
61
    }
};

Q_GLOBAL_STATIC(SystemEnvironment, staticSystemEnvironment)
con's avatar
con committed
62

hjk's avatar
hjk committed
63
namespace Utils {
con's avatar
con committed
64

65
66
67
68
69
70
71
72
73
74
static bool sortEnvironmentItem(const EnvironmentItem &a, const EnvironmentItem &b)
{
    return a.name < b.name;
}

void EnvironmentItem::sort(QList<EnvironmentItem> *list)
{
    qSort(list->begin(), list->end(), &sortEnvironmentItem);
}

hjk's avatar
hjk committed
75
QList<EnvironmentItem> EnvironmentItem::fromStringList(const QStringList &list)
con's avatar
con committed
76
77
78
79
{
    QList<EnvironmentItem> result;
    foreach (const QString &string, list) {
        int pos = string.indexOf(QLatin1Char('='));
hjk's avatar
hjk committed
80
        if (pos == -1) {
81
            EnvironmentItem item(string, QString());
con's avatar
con committed
82
83
84
85
86
87
88
89
90
91
            item.unset = true;
            result.append(item);
        } else {
            EnvironmentItem item(string.left(pos), string.mid(pos+1));
            result.append(item);
        }
    }
    return result;
}

hjk's avatar
hjk committed
92
QStringList EnvironmentItem::toStringList(const QList<EnvironmentItem> &list)
con's avatar
con committed
93
94
95
{
    QStringList result;
    foreach (const EnvironmentItem &item, list) {
hjk's avatar
hjk committed
96
        if (item.unset)
con's avatar
con committed
97
98
            result << QString(item.name);
        else
99
            result << QString(item.name + QLatin1Char('=') + item.value);
con's avatar
con committed
100
101
102
103
    }
    return result;
}

hjk's avatar
hjk committed
104
Environment::Environment(const QStringList &env)
con's avatar
con committed
105
{
hjk's avatar
hjk committed
106
    foreach (const QString &s, env) {
hjk's avatar
hjk committed
107
        int i = s.indexOf(QLatin1Char('='));
hjk's avatar
hjk committed
108
        if (i >= 0) {
109
110
111
112
            if (HostOsInfo::isWindowsHost())
                m_values.insert(s.left(i).toUpper(), s.mid(i+1));
            else
                m_values.insert(s.left(i), s.mid(i+1));
con's avatar
con committed
113
114
115
116
        }
    }
}

117
QStringList Environment::toStringList() const
con's avatar
con committed
118
119
{
    QStringList result;
120
121
122
123
124
125
126
    const QMap<QString, QString>::const_iterator end = m_values.constEnd();
    for (QMap<QString, QString>::const_iterator it = m_values.constBegin(); it != end; ++it) {
        QString entry = it.key();
        entry += QLatin1Char('=');
        entry += it.value();
        result.push_back(entry);
    }
con's avatar
con committed
127
128
129
    return result;
}

130
131
132
133
134
135
136
137
138
QProcessEnvironment Environment::toProcessEnvironment() const
{
    QProcessEnvironment result;
    const QMap<QString, QString>::const_iterator end = m_values.constEnd();
    for (QMap<QString, QString>::const_iterator it = m_values.constBegin(); it != end; ++it)
        result.insert(it.key(), it.value());
    return result;
}

con's avatar
con committed
139
140
void Environment::set(const QString &key, const QString &value)
{
141
    m_values.insert(HostOsInfo::isWindowsHost() ? key.toUpper() : key, value);
con's avatar
con committed
142
143
144
145
}

void Environment::unset(const QString &key)
{
146
    m_values.remove(HostOsInfo::isWindowsHost() ? key.toUpper() : key);
con's avatar
con committed
147
148
149
150
}

void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
{
151
152
    const QString &_key = HostOsInfo::isWindowsHost() ? key.toUpper() : key;
    QMap<QString, QString>::iterator it = m_values.find(_key);
153
    if (it == m_values.end()) {
con's avatar
con committed
154
155
        m_values.insert(_key, value);
    } else {
156
157
158
159
        // Append unless it is already there
        const QString toAppend = sep + value;
        if (!it.value().endsWith(toAppend))
            it.value().append(toAppend);
con's avatar
con committed
160
161
162
163
164
    }
}

void Environment::prependOrSet(const QString&key, const QString &value, const QString &sep)
{
165
166
    const QString &_key = HostOsInfo::isWindowsHost() ? key.toUpper() : key;
    QMap<QString, QString>::iterator it = m_values.find(_key);
167
    if (it == m_values.end()) {
con's avatar
con committed
168
169
        m_values.insert(_key, value);
    } else {
170
171
172
173
        // Prepend unless it is already there
        const QString toPrepend = value + sep;
        if (!it.value().startsWith(toPrepend))
            it.value().prepend(toPrepend);
con's avatar
con committed
174
175
176
177
178
    }
}

void Environment::appendOrSetPath(const QString &value)
{
179
180
    appendOrSet(QLatin1String("PATH"), QDir::toNativeSeparators(value),
            QString(HostOsInfo::pathListSeparator()));
con's avatar
con committed
181
182
183
184
}

void Environment::prependOrSetPath(const QString &value)
{
185
186
    prependOrSet(QLatin1String("PATH"), QDir::toNativeSeparators(value),
            QString(HostOsInfo::pathListSeparator()));
con's avatar
con committed
187
188
}

189
190
void Environment::prependOrSetLibrarySearchPath(const QString &value)
{
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
    switch (HostOsInfo::hostOs()) {
    case HostOsInfo::HostOsWindows: {
        const QChar sep = QLatin1Char(';');
        const QLatin1String path("PATH");
        prependOrSet(path, QDir::toNativeSeparators(value), QString(sep));
        break;
    }
    case HostOsInfo::HostOsLinux:
    case HostOsInfo::HostOsOtherUnix: {
        const QChar sep = QLatin1Char(':');
        const QLatin1String path("LD_LIBRARY_PATH");
        prependOrSet(path, QDir::toNativeSeparators(value), QString(sep));
        break;
    }
    default: // we could set DYLD_LIBRARY_PATH on Mac but it is unnecessary in practice
        break;
    }
208
209
}

con's avatar
con committed
210
211
Environment Environment::systemEnvironment()
{
212
    return *staticSystemEnvironment();
con's avatar
con committed
213
214
215
216
217
218
219
}

void Environment::clear()
{
    m_values.clear();
}

220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
QString Environment::searchInDirectory(const QStringList &execs, QString directory) const
{
    const QChar slash = QLatin1Char('/');
    if (directory.isEmpty())
        return QString();
    // Avoid turing / into // on windows which triggers windows to check
    // for network drives!
    if (!directory.endsWith(slash))
        directory += slash;

    foreach (const QString &exec, execs) {
        QFileInfo fi(directory + exec);
        if (fi.exists() && fi.isFile() && fi.isExecutable())
            return fi.absoluteFilePath();
    }
    return QString();
}

238
QString Environment::searchInPath(const QString &executable,
239
                                  const QStringList &additionalDirs) const
con's avatar
con committed
240
{
241
242
243
244
245
246
247
248
    QString exec = QDir::cleanPath(expandVariables(executable));
    QFileInfo fi(exec);
    if (fi.isAbsolute())
        return exec;

    if (executable.isEmpty())
        return QString();

Daniel Teske's avatar
Daniel Teske committed
249
    QStringList execs(exec);
250
251
    if (HostOsInfo::isWindowsHost()) {
        // Check all the executable extensions on windows:
252
253
254
        // PATHEXT is only used if the executable has no extension
        if (fi.suffix().isEmpty()) {
            QStringList extensions = value(QLatin1String("PATHEXT")).split(QLatin1Char(';'));
255

256
257
258
            foreach (const QString &ext, extensions)
                execs << executable + ext.toLower();
        }
259
    }
260

261
    foreach (const QString &dir, additionalDirs) {
262
263
264
265
        QString tmp = searchInDirectory(execs, dir);
        if (!tmp.isEmpty())
            return tmp;
    }
266

267
268
269
270
271
272
273
    if (executable.indexOf(QLatin1Char('/')) != -1)
        return QString();

    foreach (const QString &p, path()) {
        QString tmp = searchInDirectory(execs, QDir::fromNativeSeparators(p));
        if (!tmp.isEmpty())
            return tmp;
274
    }
275
    return QString();
con's avatar
con committed
276
277
278
279
}

QStringList Environment::path() const
{
280
281
    return m_values.value(QLatin1String("PATH")).split(HostOsInfo::pathListSeparator(),
                                                       QString::SkipEmptyParts);
con's avatar
con committed
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
}

QString Environment::value(const QString &key) const
{
    return m_values.value(key);
}

QString Environment::key(Environment::const_iterator it) const
{
    return it.key();
}

QString Environment::value(Environment::const_iterator it) const
{
    return it.value();
}

Environment::const_iterator Environment::constBegin() const
{
    return m_values.constBegin();
}

Environment::const_iterator Environment::constEnd() const
{
    return m_values.constEnd();
}

309
Environment::const_iterator Environment::constFind(const QString &name) const
con's avatar
con committed
310
311
{
    QMap<QString, QString>::const_iterator it = m_values.constFind(name);
hjk's avatar
hjk committed
312
    if (it == m_values.constEnd())
con's avatar
con committed
313
314
315
316
317
318
319
320
321
322
323
324
325
326
        return constEnd();
    else
        return it;
}

int Environment::size() const
{
    return m_values.size();
}

void Environment::modify(const QList<EnvironmentItem> & list)
{
    Environment resultEnvironment = *this;
    foreach (const EnvironmentItem &item, list) {
hjk's avatar
hjk committed
327
        if (item.unset) {
con's avatar
con committed
328
329
330
331
            resultEnvironment.unset(item.name);
        } else {
            // TODO use variable expansion
            QString value = item.value;
hjk's avatar
hjk committed
332
333
334
            for (int i=0; i < value.size(); ++i) {
                if (value.at(i) == QLatin1Char('$')) {
                    if ((i + 1) < value.size()) {
con's avatar
con committed
335
336
                        const QChar &c = value.at(i+1);
                        int end = -1;
337
338
339
340
                        if (c == QLatin1Char('('))
                            end = value.indexOf(QLatin1Char(')'), i);
                        else if (c == QLatin1Char('{'))
                            end = value.indexOf(QLatin1Char('}'), i);
hjk's avatar
hjk committed
341
                        if (end != -1) {
con's avatar
con committed
342
                            const QString &name = value.mid(i+2, end-i-2);
343
                            Environment::const_iterator it = constFind(name);
hjk's avatar
hjk committed
344
                            if (it != constEnd())
con's avatar
con committed
345
346
347
348
349
350
351
352
353
354
355
                                value.replace(i, end-i+1, it.value());
                        }
                    }
                }
            }
            resultEnvironment.set(item.name, value);
        }
    }
    *this = resultEnvironment;
}

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
QList<EnvironmentItem> Environment::diff(const Environment &other) const
{
    QMap<QString, QString>::const_iterator thisIt = constBegin();
    QMap<QString, QString>::const_iterator otherIt = other.constBegin();

    QList<EnvironmentItem> result;
    while (thisIt != constEnd() || otherIt != other.constEnd()) {
        if (thisIt == constEnd()) {
            result.append(Utils::EnvironmentItem(otherIt.key(), otherIt.value()));
            ++otherIt;
        } else if (otherIt == constEnd()) {
            Utils::EnvironmentItem item(thisIt.key(), QString());
            item.unset = true;
            result.append(item);
            ++thisIt;
        } else if (thisIt.key() < otherIt.key()) {
            Utils::EnvironmentItem item(thisIt.key(), QString());
            item.unset = true;
            result.append(item);
            ++thisIt;
        } else if (thisIt.key() > otherIt.key()) {
            result.append(Utils::EnvironmentItem(otherIt.key(), otherIt.value()));
            ++otherIt;
        } else {
            result.append(Utils::EnvironmentItem(otherIt.key(), otherIt.value()));
            ++otherIt;
            ++thisIt;
        }
    }
    return result;
}

388
389
390
391
392
bool Environment::hasKey(const QString &key)
{
    return m_values.contains(key);
}

393
394
395
396
397
QString Environment::userName() const
{
    return value(QLatin1String(HostOsInfo::isWindowsHost() ? "USERNAME" : "USER"));
}

398
bool Environment::operator!=(const Environment &other) const
399
400
401
402
{
    return !(*this == other);
}

403
bool Environment::operator==(const Environment &other) const
404
405
406
407
{
    return m_values == other.m_values;
}

408
409
410
/** Expand environment variables in a string.
 *
 * Environment variables are accepted in the following forms:
411
412
413
 * $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
 * No escapes and quoting are supported.
 * If a variable is not found, it is not substituted.
414
415
416
 */
QString Environment::expandVariables(const QString &input) const
{
417
418
    QString result = input;

419
420
421
422
423
424
425
426
427
428
429
430
    if (HostOsInfo::isWindowsHost()) {
        for (int vStart = -1, i = 0; i < result.length(); ) {
            if (result.at(i++) == QLatin1Char('%')) {
                if (vStart > 0) {
                    const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1).toUpper());
                    if (it != m_values.constEnd()) {
                        result.replace(vStart - 1, i - vStart + 1, *it);
                        i = vStart - 1 + it->length();
                        vStart = -1;
                    } else {
                        vStart = i;
                    }
431
432
433
434
435
                } else {
                    vStart = i;
                }
            }
        }
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    } else {
        enum { BASE, OPTIONALVARIABLEBRACE, VARIABLE, BRACEDVARIABLE } state = BASE;
        int vStart = -1;

        for (int i = 0; i < result.length();) {
            QChar c = result.at(i++);
            if (state == BASE) {
                if (c == QLatin1Char('$'))
                    state = OPTIONALVARIABLEBRACE;
            } else if (state == OPTIONALVARIABLEBRACE) {
                if (c == QLatin1Char('{')) {
                    state = BRACEDVARIABLE;
                    vStart = i;
                } else if (c.isLetterOrNumber() || c == QLatin1Char('_')) {
                    state = VARIABLE;
                    vStart = i - 1;
                } else {
                    state = BASE;
454
                }
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
            } else if (state == BRACEDVARIABLE) {
                if (c == QLatin1Char('}')) {
                    const_iterator it = m_values.constFind(result.mid(vStart, i - 1 - vStart));
                    if (it != constEnd()) {
                        result.replace(vStart - 2, i - vStart + 2, *it);
                        i = vStart - 2 + it->length();
                    }
                    state = BASE;
                }
            } else if (state == VARIABLE) {
                if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
                    const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1));
                    if (it != constEnd()) {
                        result.replace(vStart - 1, i - vStart, *it);
                        i = vStart - 1 + it->length();
                    }
                    state = BASE;
472
                }
473
474
            }
        }
475
476
477
478
479
        if (state == VARIABLE) {
            const_iterator it = m_values.constFind(result.mid(vStart));
            if (it != constEnd())
                result.replace(vStart - 1, result.length() - vStart + 1, *it);
        }
480
481
482
    }
    return result;
}
483
484
485
486

QStringList Environment::expandVariables(const QStringList &variables) const
{
    QStringList results;
hjk's avatar
hjk committed
487
    foreach (const QString &i, variables)
488
489
490
        results << expandVariables(i);
    return results;
}
hjk's avatar
hjk committed
491
492

} // namespace Utils