aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/utils/environment.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/utils/environment.cpp')
-rw-r--r--src/libs/utils/environment.cpp455
1 files changed, 254 insertions, 201 deletions
diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp
index c99293329c..b9af2dea92 100644
--- a/src/libs/utils/environment.cpp
+++ b/src/libs/utils/environment.cpp
@@ -4,6 +4,7 @@
#include "environment.h"
#include "algorithm.h"
+#include "filepath.h"
#include "qtcassert.h"
#include <QDir>
@@ -11,6 +12,13 @@
#include <QReadWriteLock>
#include <QSet>
+/*!
+ \class Utils::Environment
+ \inmodule QtCreator
+
+ \brief The Environment class sets \QC's system environment.
+*/
+
namespace Utils {
static QReadWriteLock s_envMutex;
@@ -18,83 +26,149 @@ Q_GLOBAL_STATIC_WITH_ARGS(Environment, staticSystemEnvironment,
(QProcessEnvironment::systemEnvironment().toStringList()))
Q_GLOBAL_STATIC(QVector<EnvironmentProvider>, environmentProviders)
+Environment::Environment()
+ : m_dict(HostOsInfo::hostOs())
+{}
+
+Environment::Environment(OsType osType)
+ : m_dict(osType)
+{}
+
+Environment::Environment(const QStringList &env, OsType osType)
+ : m_dict(osType)
+{
+ m_changeItems.append(NameValueDictionary(env, osType));
+}
+
+Environment::Environment(const NameValuePairs &nameValues)
+{
+ m_changeItems.append(NameValueDictionary(nameValues));
+}
+
+Environment::Environment(const NameValueDictionary &dict)
+{
+ m_changeItems.append(dict);
+}
+
NameValueItems Environment::diff(const Environment &other, bool checkAppendPrepend) const
{
- return m_dict.diff(other.m_dict, checkAppendPrepend);
+ const NameValueDictionary &dict = resolved();
+ const NameValueDictionary &otherDict = other.resolved();
+ return dict.diff(otherDict, checkAppendPrepend);
+}
+
+Environment::FindResult Environment::find(const QString &name) const
+{
+ const NameValueDictionary &dict = resolved();
+ const auto it = dict.constFind(name);
+ if (it == dict.constEnd())
+ return {};
+ return Entry{it.key().name, it.value().first, it.value().second};
+}
+
+void Environment::forEachEntry(const std::function<void(const QString &, const QString &, bool)> &callBack) const
+{
+ const NameValueDictionary &dict = resolved();
+ for (auto it = dict.m_values.constBegin(); it != dict.m_values.constEnd(); ++it)
+ callBack(it.key().name, it.value().first, it.value().second);
+}
+
+bool Environment::operator==(const Environment &other) const
+{
+ const NameValueDictionary &dict = resolved();
+ const NameValueDictionary &otherDict = other.resolved();
+ return dict == otherDict;
+}
+
+bool Environment::operator!=(const Environment &other) const
+{
+ const NameValueDictionary &dict = resolved();
+ const NameValueDictionary &otherDict = other.resolved();
+ return dict != otherDict;
+}
+
+QString Environment::value(const QString &key) const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict.value(key);
+}
+
+QString Environment::value_or(const QString &key, const QString &defaultValue) const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict.hasKey(key) ? dict.value(key) : defaultValue;
+}
+
+bool Environment::hasKey(const QString &key) const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict.hasKey(key);
}
bool Environment::hasChanges() const
{
- return m_dict.size() != 0;
+ const NameValueDictionary &dict = resolved();
+ return dict.size() != 0;
+}
+
+OsType Environment::osType() const
+{
+ return m_dict.m_osType;
+}
+
+QStringList Environment::toStringList() const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict.toStringList();
}
QProcessEnvironment Environment::toProcessEnvironment() const
{
+ const NameValueDictionary &dict = resolved();
QProcessEnvironment result;
- for (auto it = m_dict.m_values.constBegin(); it != m_dict.m_values.constEnd(); ++it) {
+ for (auto it = dict.m_values.constBegin(); it != dict.m_values.constEnd(); ++it) {
if (it.value().second)
- result.insert(it.key().name, expandedValueForKey(key(it)));
+ result.insert(it.key().name, expandedValueForKey(dict.key(it)));
}
return result;
}
void Environment::appendOrSetPath(const FilePath &value)
{
- QTC_CHECK(value.osType() == osType());
+ QTC_CHECK(value.osType() == m_dict.m_osType);
if (value.isEmpty())
return;
- appendOrSet("PATH", value.nativePath(),
- QString(OsSpecificAspects::pathListSeparator(osType())));
+ appendOrSet("PATH", value.nativePath(), OsSpecificAspects::pathListSeparator(osType()));
}
void Environment::prependOrSetPath(const FilePath &value)
{
- QTC_CHECK(value.osType() == osType());
+ QTC_CHECK(value.osType() == m_dict.m_osType);
if (value.isEmpty())
return;
- prependOrSet("PATH", value.nativePath(),
- QString(OsSpecificAspects::pathListSeparator(osType())));
+ prependOrSet("PATH", value.nativePath(), OsSpecificAspects::pathListSeparator(osType()));
}
void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
{
- QTC_ASSERT(!key.contains('='), return );
- const auto it = m_dict.findKey(key);
- if (it == m_dict.m_values.end()) {
- m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
- } else {
- // Append unless it is already there
- const QString toAppend = sep + value;
- if (!it.value().first.endsWith(toAppend))
- it.value().first.append(toAppend);
- }
+ addItem(Item{std::in_place_index_t<AppendOrSet>(), key, value, sep});
}
void Environment::prependOrSet(const QString &key, const QString &value, const QString &sep)
{
- QTC_ASSERT(!key.contains('='), return );
- const auto it = m_dict.findKey(key);
- if (it == m_dict.m_values.end()) {
- m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
- } else {
- // Prepend unless it is already there
- const QString toPrepend = value + sep;
- if (!it.value().first.startsWith(toPrepend))
- it.value().first.prepend(toPrepend);
- }
+ addItem(Item{std::in_place_index_t<PrependOrSet>(), key, value, sep});
}
void Environment::prependOrSetLibrarySearchPath(const FilePath &value)
{
QTC_CHECK(value.osType() == osType());
+ const QChar sep = OsSpecificAspects::pathListSeparator(osType());
switch (osType()) {
case OsTypeWindows: {
- const QChar sep = ';';
- prependOrSet("PATH", value.nativePath(), QString(sep));
+ prependOrSet("PATH", value.nativePath(), sep);
break;
}
case OsTypeMac: {
- const QString sep = ":";
const QString nativeValue = value.nativePath();
prependOrSet("DYLD_LIBRARY_PATH", nativeValue, sep);
prependOrSet("DYLD_FRAMEWORK_PATH", nativeValue, sep);
@@ -102,8 +176,7 @@ void Environment::prependOrSetLibrarySearchPath(const FilePath &value)
}
case OsTypeLinux:
case OsTypeOtherUnix: {
- const QChar sep = ':';
- prependOrSet("LD_LIBRARY_PATH", value.nativePath(), QString(sep));
+ prependOrSet("LD_LIBRARY_PATH", value.nativePath(), sep);
break;
}
default:
@@ -118,6 +191,14 @@ void Environment::prependOrSetLibrarySearchPaths(const FilePaths &values)
});
}
+/*!
+ Returns \QC's system environment.
+
+ This can be different from the system environment that \QC started in if the
+ user changed it in \uicontrol Preferences > \uicontrol Environment >
+ \uicontrol System > \uicontrol Environment.
+*/
+
Environment Environment::systemEnvironment()
{
QReadLocker lock(&s_envMutex);
@@ -126,144 +207,24 @@ Environment Environment::systemEnvironment()
void Environment::setupEnglishOutput()
{
- m_dict.set("LC_MESSAGES", "en_US.utf8");
- m_dict.set("LANGUAGE", "en_US:en");
+ addItem(Item{std::in_place_index_t<SetupEnglishOutput>()});
}
-static FilePath searchInDirectory(const QStringList &execs,
- const FilePath &directory,
- QSet<FilePath> &alreadyChecked)
-{
- const int checkedCount = alreadyChecked.count();
- alreadyChecked.insert(directory);
-
- if (directory.isEmpty() || alreadyChecked.count() == checkedCount)
- return FilePath();
-
- for (const QString &exec : execs) {
- const FilePath filePath = directory.pathAppended(exec);
- if (filePath.isExecutableFile())
- return filePath;
- }
- return FilePath();
-}
-
-static QStringList appendExeExtensions(const Environment &env, const QString &executable)
-{
- QStringList execs(executable);
- if (env.osType() == OsTypeWindows) {
- const QFileInfo fi(executable);
- // Check all the executable extensions on windows:
- // PATHEXT is only used if the executable has no extension
- if (fi.suffix().isEmpty()) {
- const QStringList extensions = env.expandedValueForKey("PATHEXT").split(';');
-
- for (const QString &ext : extensions)
- execs << executable + ext.toLower();
- }
- }
- return execs;
-}
+using SearchResultCallback = std::function<IterationPolicy(const FilePath &)>;
QString Environment::expandedValueForKey(const QString &key) const
{
- return expandVariables(m_dict.value(key));
-}
-
-static FilePath searchInDirectoriesHelper(const Environment &env,
- const QString &executable,
- const FilePaths &dirs,
- const Environment::PathFilter &func,
- bool usePath)
-{
- if (executable.isEmpty())
- return FilePath();
-
- const QString exec = QDir::cleanPath(env.expandVariables(executable));
- const QFileInfo fi(exec);
-
- const QStringList execs = appendExeExtensions(env, exec);
-
- if (fi.isAbsolute()) {
- for (const QString &path : execs) {
- QFileInfo pfi = QFileInfo(path);
- if (pfi.isFile() && pfi.isExecutable())
- return FilePath::fromString(path);
- }
- return FilePath::fromString(exec);
- }
-
- QSet<FilePath> alreadyChecked;
- for (const FilePath &dir : dirs) {
- FilePath tmp = searchInDirectory(execs, dir, alreadyChecked);
- if (!tmp.isEmpty() && (!func || func(tmp)))
- return tmp;
- }
-
- if (usePath) {
- if (executable.contains('/'))
- return FilePath();
-
- for (const FilePath &p : env.path()) {
- FilePath tmp = searchInDirectory(execs, p, alreadyChecked);
- if (!tmp.isEmpty() && (!func || func(tmp)))
- return tmp;
- }
- }
- return FilePath();
-}
-
-FilePath Environment::searchInDirectories(const QString &executable,
- const FilePaths &dirs,
- const PathFilter &func) const
-{
- return searchInDirectoriesHelper(*this, executable, dirs, func, false);
+ const NameValueDictionary &dict = resolved();
+ return expandVariables(dict.value(key));
}
FilePath Environment::searchInPath(const QString &executable,
const FilePaths &additionalDirs,
- const PathFilter &func) const
-{
- return searchInDirectoriesHelper(*this, executable, additionalDirs, func, true);
-}
-
-FilePaths Environment::findAllInPath(const QString &executable,
- const FilePaths &additionalDirs,
- const Environment::PathFilter &func) const
+ const FilePathPredicate &filter) const
{
- if (executable.isEmpty())
- return {};
-
- const QString exec = QDir::cleanPath(expandVariables(executable));
- const QFileInfo fi(exec);
-
- const QStringList execs = appendExeExtensions(*this, exec);
-
- if (fi.isAbsolute()) {
- for (const QString &path : execs) {
- QFileInfo pfi = QFileInfo(path);
- if (pfi.isFile() && pfi.isExecutable())
- return {FilePath::fromString(path)};
- }
- return {FilePath::fromString(exec)};
- }
-
- QSet<FilePath> result;
- QSet<FilePath> alreadyChecked;
- for (const FilePath &dir : additionalDirs) {
- FilePath tmp = searchInDirectory(execs, dir, alreadyChecked);
- if (!tmp.isEmpty() && (!func || func(tmp)))
- result << tmp;
- }
-
- if (!executable.contains('/')) {
- for (const FilePath &p : path()) {
- FilePath tmp = searchInDirectory(execs, p, alreadyChecked);
- if (!tmp.isEmpty() && (!func || func(tmp)))
- result << tmp;
- }
- }
- return result.values();
+ const FilePath exec = FilePath::fromUserInput(expandVariables(executable));
+ const FilePaths dirs = path() + additionalDirs;
+ return exec.searchInDirectories(dirs, filter, FilePath::WithAnySuffix);
}
FilePaths Environment::path() const
@@ -299,14 +260,16 @@ void Environment::setSystemEnvironment(const Environment &environment)
*/
QString Environment::expandVariables(const QString &input) const
{
+ const NameValueDictionary &dict = resolved();
+
QString result = input;
if (osType() == OsTypeWindows) {
for (int vStart = -1, i = 0; i < result.length(); ) {
if (result.at(i++) == '%') {
if (vStart > 0) {
- const auto it = m_dict.findKey(result.mid(vStart, i - vStart - 1));
- if (it != m_dict.m_values.constEnd()) {
+ const auto it = dict.findKey(result.mid(vStart, i - vStart - 1));
+ if (it != dict.m_values.constEnd()) {
result.replace(vStart - 1, i - vStart + 1, it->first);
i = vStart - 1 + it->first.length();
vStart = -1;
@@ -339,28 +302,30 @@ QString Environment::expandVariables(const QString &input) const
}
} else if (state == BRACEDVARIABLE) {
if (c == '}') {
- const_iterator it = constFind(result.mid(vStart, i - 1 - vStart));
- if (it != constEnd()) {
- result.replace(vStart - 2, i - vStart + 2, it->first);
- i = vStart - 2 + it->first.length();
+ const QString key = result.mid(vStart, i - 1 - vStart);
+ const Environment::FindResult res = find(key);
+ if (res) {
+ result.replace(vStart - 2, i - vStart + 2, res->value);
+ i = vStart - 2 + res->value.length();
}
state = BASE;
}
} else if (state == VARIABLE) {
if (!c.isLetterOrNumber() && c != '_') {
- const_iterator it = constFind(result.mid(vStart, i - vStart - 1));
- if (it != constEnd()) {
- result.replace(vStart - 1, i - vStart, it->first);
- i = vStart - 1 + it->first.length();
+ const QString key = result.mid(vStart, i - vStart - 1);
+ const Environment::FindResult res = find(key);
+ if (res) {
+ result.replace(vStart - 1, i - vStart, res->value);
+ i = vStart - 1 + res->value.length();
}
state = BASE;
}
}
}
if (state == VARIABLE) {
- const_iterator it = constFind(result.mid(vStart));
- if (it != constEnd())
- result.replace(vStart - 1, result.length() - vStart + 1, it->first);
+ const Environment::FindResult res = find(result.mid(vStart));
+ if (res)
+ result.replace(vStart - 1, result.length() - vStart + 1, res->value);
}
}
return result;
@@ -376,6 +341,12 @@ QStringList Environment::expandVariables(const QStringList &variables) const
return transform(variables, [this](const QString &i) { return expandVariables(i); });
}
+NameValueDictionary Environment::toDictionary() const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict;
+}
+
void EnvironmentProvider::addProvider(EnvironmentProvider &&provider)
{
environmentProviders->append(std::move(provider));
@@ -394,63 +365,145 @@ std::optional<EnvironmentProvider> EnvironmentProvider::provider(const QByteArra
return std::nullopt;
}
-void EnvironmentChange::addSetValue(const QString &key, const QString &value)
+void Environment::addItem(const Item &item)
+{
+ m_dict.clear();
+ m_changeItems.append(item);
+}
+
+void Environment::set(const QString &key, const QString &value, bool enabled)
{
- m_changeItems.append(Item{std::in_place_index_t<SetValue>(), QPair<QString, QString>{key, value}});
+ addItem(Item{std::in_place_index_t<SetValue>(),
+ std::tuple<QString, QString, bool>{key, value, enabled}});
}
-void EnvironmentChange::addUnsetValue(const QString &key)
+void Environment::setFallback(const QString &key, const QString &value)
{
- m_changeItems.append(Item{std::in_place_index_t<UnsetValue>(), key});
+ addItem(Item{std::in_place_index_t<SetFallbackValue>(),
+ std::tuple<QString, QString>{key, value}});
}
-void EnvironmentChange::addPrependToPath(const FilePaths &values)
+void Environment::unset(const QString &key)
{
+ addItem(Item{std::in_place_index_t<UnsetValue>(), key});
+}
+
+void Environment::modify(const NameValueItems &items)
+{
+ addItem(Item{std::in_place_index_t<Modify>(), items});
+}
+
+void Environment::prependToPath(const FilePaths &values)
+{
+ m_dict.clear();
for (int i = values.size(); --i >= 0; ) {
const FilePath value = values.at(i);
- m_changeItems.append(Item{std::in_place_index_t<PrependToPath>(), value});
+ m_changeItems.append(Item{
+ std::in_place_index_t<PrependOrSet>(),
+ QString("PATH"),
+ value.nativePath(),
+ value.pathListSeparator()
+ });
}
}
-void EnvironmentChange::addAppendToPath(const FilePaths &values)
+void Environment::appendToPath(const FilePaths &values)
{
- for (const FilePath &value : values)
- m_changeItems.append(Item{std::in_place_index_t<AppendToPath>(), value});
+ m_dict.clear();
+ for (const FilePath &value : values) {
+ m_changeItems.append(Item{
+ std::in_place_index_t<AppendOrSet>(),
+ QString("PATH"),
+ value.nativePath(),
+ value.pathListSeparator()
+ });
+ }
}
-EnvironmentChange EnvironmentChange::fromDictionary(const NameValueDictionary &dict)
+const NameValueDictionary &Environment::resolved() const
{
- EnvironmentChange change;
- change.m_changeItems.append(Item{std::in_place_index_t<SetFixedDictionary>(), dict});
- return change;
-}
+ if (m_dict.size() != 0)
+ return m_dict;
-void EnvironmentChange::applyToEnvironment(Environment &env) const
-{
+ m_fullDict = false;
for (const Item &item : m_changeItems) {
switch (item.index()) {
case SetSystemEnvironment:
- env = Environment::systemEnvironment();
+ m_dict = Environment::systemEnvironment().toDictionary();
+ m_fullDict = true;
break;
case SetFixedDictionary:
- env = Environment(std::get<SetFixedDictionary>(item));
+ m_dict = std::get<SetFixedDictionary>(item);
+ m_fullDict = true;
break;
case SetValue: {
- const QPair<QString, QString> data = std::get<SetValue>(item);
- env.set(data.first, data.second);
+ auto [key, value, enabled] = std::get<SetValue>(item);
+ m_dict.set(key, value, enabled);
+ break;
+ }
+ case SetFallbackValue: {
+ auto [key, value] = std::get<SetFallbackValue>(item);
+ if (m_fullDict) {
+ if (m_dict.value(key).isEmpty())
+ m_dict.set(key, value, true);
+ } else {
+ QTC_ASSERT(false, qDebug() << "operating on partial dictionary");
+ m_dict.set(key, value, true);
+ }
break;
}
case UnsetValue:
- env.unset(std::get<UnsetValue>(item));
+ m_dict.unset(std::get<UnsetValue>(item));
break;
- case PrependToPath:
- env.prependOrSetPath(std::get<PrependToPath>(item));
+ case PrependOrSet: {
+ auto [key, value, sep] = std::get<PrependOrSet>(item);
+ QTC_ASSERT(!key.contains('='), return m_dict);
+ const auto it = m_dict.findKey(key);
+ if (it == m_dict.m_values.end()) {
+ m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
+ } else {
+ // Prepend unless it is already there
+ const QString toPrepend = value + sep;
+ if (!it.value().first.startsWith(toPrepend))
+ it.value().first.prepend(toPrepend);
+ }
break;
- case AppendToPath:
- env.appendOrSetPath(std::get<AppendToPath>(item));
+ }
+ case AppendOrSet: {
+ auto [key, value, sep] = std::get<AppendOrSet>(item);
+ QTC_ASSERT(!key.contains('='), return m_dict);
+ const auto it = m_dict.findKey(key);
+ if (it == m_dict.m_values.end()) {
+ m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
+ } else {
+ // Prepend unless it is already there
+ const QString toAppend = sep + value;
+ if (!it.value().first.endsWith(toAppend))
+ it.value().first.append(toAppend);
+ }
+ break;
+ }
+ case Modify: {
+ NameValueItems items = std::get<Modify>(item);
+ m_dict.modify(items);
+ break;
+ }
+ case SetupEnglishOutput:
+ m_dict.set("LC_MESSAGES", "en_US.utf8");
+ m_dict.set("LANGUAGE", "en_US:en");
break;
}
}
+
+ return m_dict;
+}
+
+Environment Environment::appliedToEnvironment(const Environment &base) const
+{
+ Environment res = base;
+ res.m_dict.clear();
+ res.m_changeItems.append(m_changeItems);
+ return res;
}
/*!