forked from qt-creator/qt-creator
Utils: Continue Environment/EnvironmentChange consolidation
Make Environment a stack of changes that gets "expanded" to a full environment before things are actively accessed. Later this expansion should be done lazily if possible. Task-number: QTCREATORBUG-28357 Change-Id: If1c7bfdb9f58b81e71c51ed87ee75d6964a47019 Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -19,84 +19,137 @@ 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 auto it = m_dict.constFind(name);
|
||||
if (it == m_dict.constEnd())
|
||||
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
|
||||
{
|
||||
for (auto it = m_dict.m_values.constBegin(); it != m_dict.m_values.constEnd(); ++it)
|
||||
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(m_dict.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)
|
||||
@@ -105,11 +158,11 @@ void Environment::prependOrSetLibrarySearchPath(const FilePath &value)
|
||||
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 QChar sep = ':';
|
||||
const QString nativeValue = value.nativePath();
|
||||
prependOrSet("DYLD_LIBRARY_PATH", nativeValue, sep);
|
||||
prependOrSet("DYLD_FRAMEWORK_PATH", nativeValue, sep);
|
||||
@@ -118,7 +171,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:
|
||||
@@ -141,8 +194,7 @@ 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>()});
|
||||
}
|
||||
|
||||
using SearchResultCallback = std::function<IterationPolicy(const FilePath &)>;
|
||||
@@ -190,7 +242,8 @@ static FilePaths appendExeExtensions(const Environment &env, const FilePath &exe
|
||||
|
||||
QString Environment::expandedValueForKey(const QString &key) const
|
||||
{
|
||||
return expandVariables(m_dict.value(key));
|
||||
const NameValueDictionary &dict = resolved();
|
||||
return expandVariables(dict.value(key));
|
||||
}
|
||||
|
||||
static void searchInDirectoriesHelper(const SearchResultCallback &resultCallback,
|
||||
@@ -324,14 +377,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;
|
||||
@@ -403,6 +458,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));
|
||||
@@ -421,63 +482,125 @@ 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_changeItems.append(Item{std::in_place_index_t<SetValue>(), QPair<QString, QString>{key, value}});
|
||||
m_dict.clear();
|
||||
m_changeItems.append(item);
|
||||
}
|
||||
|
||||
void EnvironmentChange::addUnsetValue(const QString &key)
|
||||
void Environment::set(const QString &key, const QString &value, bool enabled)
|
||||
{
|
||||
m_changeItems.append(Item{std::in_place_index_t<UnsetValue>(), key});
|
||||
addItem(Item{std::in_place_index_t<SetValue>(),
|
||||
std::tuple<QString, QString, bool>{key, value, enabled}});
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
for (const Item &item : m_changeItems) {
|
||||
switch (item.index()) {
|
||||
case SetSystemEnvironment:
|
||||
env = Environment::systemEnvironment();
|
||||
m_dict = Environment::systemEnvironment().toDictionary();
|
||||
break;
|
||||
case SetFixedDictionary:
|
||||
env = Environment(std::get<SetFixedDictionary>(item));
|
||||
m_dict = std::get<SetFixedDictionary>(item);
|
||||
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 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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
Reference in New Issue
Block a user