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:
hjk
2023-01-26 17:48:36 +01:00
parent ae4e178221
commit 2766b4004b
16 changed files with 261 additions and 206 deletions

View File

@@ -235,7 +235,7 @@ def qdump__Utils__Port(d, value):
def qdump__Utils__Environment(d, value): def x_qdump__Utils__Environment(d, value):
qdump__Utils__NameValueDictionary(d, value) qdump__Utils__NameValueDictionary(d, value)
@@ -243,7 +243,7 @@ def qdump__Utils__DictKey(d, value):
d.putStringValue(value["name"]) d.putStringValue(value["name"])
def qdump__Utils__NameValueDictionary(d, value): def x_qdump__Utils__NameValueDictionary(d, value):
dptr = d.extractPointer(value) dptr = d.extractPointer(value)
if d.qtVersion() >= 0x60000: if d.qtVersion() >= 0x60000:
if dptr == 0: if dptr == 0:

View File

@@ -632,7 +632,7 @@ public:
QString m_placeHolderText; QString m_placeHolderText;
QString m_historyCompleterKey; QString m_historyCompleterKey;
PathChooser::Kind m_expectedKind = PathChooser::File; PathChooser::Kind m_expectedKind = PathChooser::File;
EnvironmentChange m_environmentChange; Environment m_environment;
QPointer<ElidingLabel> m_labelDisplay; QPointer<ElidingLabel> m_labelDisplay;
QPointer<FancyLineEdit> m_lineEditDisplay; QPointer<FancyLineEdit> m_lineEditDisplay;
QPointer<PathChooser> m_pathChooserDisplay; QPointer<PathChooser> m_pathChooserDisplay;
@@ -975,16 +975,11 @@ void StringAspect::setExpectedKind(const PathChooser::Kind expectedKind)
d->m_pathChooserDisplay->setExpectedKind(expectedKind); d->m_pathChooserDisplay->setExpectedKind(expectedKind);
} }
void StringAspect::setEnvironmentChange(const EnvironmentChange &change)
{
d->m_environmentChange = change;
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setEnvironmentChange(change);
}
void StringAspect::setEnvironment(const Environment &env) void StringAspect::setEnvironment(const Environment &env)
{ {
setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary())); d->m_environment = env;
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setEnvironment(env);
} }
void StringAspect::setBaseFileName(const FilePath &baseFileName) void StringAspect::setBaseFileName(const FilePath &baseFileName)
@@ -1082,7 +1077,7 @@ void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey); d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey);
if (d->m_validator) if (d->m_validator)
d->m_pathChooserDisplay->setValidationFunction(d->m_validator); d->m_pathChooserDisplay->setValidationFunction(d->m_validator);
d->m_pathChooserDisplay->setEnvironmentChange(d->m_environmentChange); d->m_pathChooserDisplay->setEnvironment(d->m_environment);
d->m_pathChooserDisplay->setBaseDirectory(d->m_baseFileName); d->m_pathChooserDisplay->setBaseDirectory(d->m_baseFileName);
d->m_pathChooserDisplay->setOpenTerminalHandler(d->m_openTerminal); d->m_pathChooserDisplay->setOpenTerminalHandler(d->m_openTerminal);
if (defaultValue() == value()) if (defaultValue() == value())

View File

@@ -383,7 +383,6 @@ public:
void setPlaceHolderText(const QString &placeHolderText); void setPlaceHolderText(const QString &placeHolderText);
void setHistoryCompleter(const QString &historyCompleterKey); void setHistoryCompleter(const QString &historyCompleterKey);
void setExpectedKind(const PathChooser::Kind expectedKind); void setExpectedKind(const PathChooser::Kind expectedKind);
void setEnvironmentChange(const EnvironmentChange &change);
void setEnvironment(const Environment &env); void setEnvironment(const Environment &env);
void setBaseFileName(const FilePath &baseFileName); void setBaseFileName(const FilePath &baseFileName);
void setUndoRedoEnabled(bool readOnly); void setUndoRedoEnabled(bool readOnly);

View File

@@ -19,84 +19,137 @@ Q_GLOBAL_STATIC_WITH_ARGS(Environment, staticSystemEnvironment,
(QProcessEnvironment::systemEnvironment().toStringList())) (QProcessEnvironment::systemEnvironment().toStringList()))
Q_GLOBAL_STATIC(QVector<EnvironmentProvider>, environmentProviders) 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 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 Environment::FindResult Environment::find(const QString &name) const
{ {
const auto it = m_dict.constFind(name); const NameValueDictionary &dict = resolved();
if (it == m_dict.constEnd()) const auto it = dict.constFind(name);
if (it == dict.constEnd())
return {}; return {};
return Entry{it.key().name, it.value().first, it.value().second}; 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 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); 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 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 QProcessEnvironment Environment::toProcessEnvironment() const
{ {
const NameValueDictionary &dict = resolved();
QProcessEnvironment result; 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) 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; return result;
} }
void Environment::appendOrSetPath(const FilePath &value) void Environment::appendOrSetPath(const FilePath &value)
{ {
QTC_CHECK(value.osType() == osType()); QTC_CHECK(value.osType() == m_dict.m_osType);
if (value.isEmpty()) if (value.isEmpty())
return; return;
appendOrSet("PATH", value.nativePath(), appendOrSet("PATH", value.nativePath(), OsSpecificAspects::pathListSeparator(osType()));
QString(OsSpecificAspects::pathListSeparator(osType())));
} }
void Environment::prependOrSetPath(const FilePath &value) void Environment::prependOrSetPath(const FilePath &value)
{ {
QTC_CHECK(value.osType() == osType()); QTC_CHECK(value.osType() == m_dict.m_osType);
if (value.isEmpty()) if (value.isEmpty())
return; return;
prependOrSet("PATH", value.nativePath(), prependOrSet("PATH", value.nativePath(), OsSpecificAspects::pathListSeparator(osType()));
QString(OsSpecificAspects::pathListSeparator(osType())));
} }
void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep) void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
{ {
QTC_ASSERT(!key.contains('='), return ); addItem(Item{std::in_place_index_t<AppendOrSet>(), key, value, sep});
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);
}
} }
void Environment::prependOrSet(const QString &key, const QString &value, const QString &sep) void Environment::prependOrSet(const QString &key, const QString &value, const QString &sep)
{ {
QTC_ASSERT(!key.contains('='), return ); addItem(Item{std::in_place_index_t<PrependOrSet>(), key, value, sep});
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);
}
} }
void Environment::prependOrSetLibrarySearchPath(const FilePath &value) void Environment::prependOrSetLibrarySearchPath(const FilePath &value)
@@ -105,11 +158,11 @@ void Environment::prependOrSetLibrarySearchPath(const FilePath &value)
switch (osType()) { switch (osType()) {
case OsTypeWindows: { case OsTypeWindows: {
const QChar sep = ';'; const QChar sep = ';';
prependOrSet("PATH", value.nativePath(), QString(sep)); prependOrSet("PATH", value.nativePath(), sep);
break; break;
} }
case OsTypeMac: { case OsTypeMac: {
const QString sep = ":"; const QChar sep = ':';
const QString nativeValue = value.nativePath(); const QString nativeValue = value.nativePath();
prependOrSet("DYLD_LIBRARY_PATH", nativeValue, sep); prependOrSet("DYLD_LIBRARY_PATH", nativeValue, sep);
prependOrSet("DYLD_FRAMEWORK_PATH", nativeValue, sep); prependOrSet("DYLD_FRAMEWORK_PATH", nativeValue, sep);
@@ -118,7 +171,7 @@ void Environment::prependOrSetLibrarySearchPath(const FilePath &value)
case OsTypeLinux: case OsTypeLinux:
case OsTypeOtherUnix: { case OsTypeOtherUnix: {
const QChar sep = ':'; const QChar sep = ':';
prependOrSet("LD_LIBRARY_PATH", value.nativePath(), QString(sep)); prependOrSet("LD_LIBRARY_PATH", value.nativePath(), sep);
break; break;
} }
default: default:
@@ -141,8 +194,7 @@ Environment Environment::systemEnvironment()
void Environment::setupEnglishOutput() void Environment::setupEnglishOutput()
{ {
m_dict.set("LC_MESSAGES", "en_US.utf8"); addItem(Item{std::in_place_index_t<SetupEnglishOutput>()});
m_dict.set("LANGUAGE", "en_US:en");
} }
using SearchResultCallback = std::function<IterationPolicy(const FilePath &)>; 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 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, static void searchInDirectoriesHelper(const SearchResultCallback &resultCallback,
@@ -324,14 +377,16 @@ void Environment::setSystemEnvironment(const Environment &environment)
*/ */
QString Environment::expandVariables(const QString &input) const QString Environment::expandVariables(const QString &input) const
{ {
const NameValueDictionary &dict = resolved();
QString result = input; QString result = input;
if (osType() == OsTypeWindows) { if (osType() == OsTypeWindows) {
for (int vStart = -1, i = 0; i < result.length(); ) { for (int vStart = -1, i = 0; i < result.length(); ) {
if (result.at(i++) == '%') { if (result.at(i++) == '%') {
if (vStart > 0) { if (vStart > 0) {
const auto it = m_dict.findKey(result.mid(vStart, i - vStart - 1)); const auto it = dict.findKey(result.mid(vStart, i - vStart - 1));
if (it != m_dict.m_values.constEnd()) { if (it != dict.m_values.constEnd()) {
result.replace(vStart - 1, i - vStart + 1, it->first); result.replace(vStart - 1, i - vStart + 1, it->first);
i = vStart - 1 + it->first.length(); i = vStart - 1 + it->first.length();
vStart = -1; vStart = -1;
@@ -403,6 +458,12 @@ QStringList Environment::expandVariables(const QStringList &variables) const
return transform(variables, [this](const QString &i) { return expandVariables(i); }); 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) void EnvironmentProvider::addProvider(EnvironmentProvider &&provider)
{ {
environmentProviders->append(std::move(provider)); environmentProviders->append(std::move(provider));
@@ -421,63 +482,125 @@ std::optional<EnvironmentProvider> EnvironmentProvider::provider(const QByteArra
return std::nullopt; 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; ) { for (int i = values.size(); --i >= 0; ) {
const FilePath value = values.at(i); 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_dict.clear();
m_changeItems.append(Item{std::in_place_index_t<AppendToPath>(), value}); 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; if (m_dict.size() != 0)
change.m_changeItems.append(Item{std::in_place_index_t<SetFixedDictionary>(), dict}); return m_dict;
return change;
}
void EnvironmentChange::applyToEnvironment(Environment &env) const
{
for (const Item &item : m_changeItems) { for (const Item &item : m_changeItems) {
switch (item.index()) { switch (item.index()) {
case SetSystemEnvironment: case SetSystemEnvironment:
env = Environment::systemEnvironment(); m_dict = Environment::systemEnvironment().toDictionary();
break; break;
case SetFixedDictionary: case SetFixedDictionary:
env = Environment(std::get<SetFixedDictionary>(item)); m_dict = std::get<SetFixedDictionary>(item);
break; break;
case SetValue: { case SetValue: {
const QPair<QString, QString> data = std::get<SetValue>(item); auto [key, value, enabled] = std::get<SetValue>(item);
env.set(data.first, data.second); m_dict.set(key, value, enabled);
break; break;
} }
case UnsetValue: case UnsetValue:
env.unset(std::get<UnsetValue>(item)); m_dict.unset(std::get<UnsetValue>(item));
break; break;
case PrependToPath: case PrependOrSet: {
env.prependOrSetPath(std::get<PrependToPath>(item)); 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; 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; 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;
} }
/*! /*!

View File

@@ -22,27 +22,24 @@ namespace Utils {
class QTCREATOR_UTILS_EXPORT Environment final class QTCREATOR_UTILS_EXPORT Environment final
{ {
public: public:
Environment() : m_dict(HostOsInfo::hostOs()) {} Environment();
explicit Environment(OsType osType) : m_dict(osType) {} explicit Environment(OsType osType);
explicit Environment(const QStringList &env, OsType osType = HostOsInfo::hostOs()) explicit Environment(const QStringList &env, OsType osType = HostOsInfo::hostOs());
: m_dict(env, osType) {} explicit Environment(const NameValuePairs &nameValues);
explicit Environment(const NameValuePairs &nameValues) : m_dict(nameValues) {} explicit Environment(const NameValueDictionary &dict);
explicit Environment(const NameValueDictionary &dict) : m_dict(dict) {}
QString value(const QString &key) const { return m_dict.value(key); } QString value(const QString &key) const;
QString value_or(const QString &key, const QString &defaultValue) const QString value_or(const QString &key, const QString &defaultValue) const;
{ bool hasKey(const QString &key) const;
return m_dict.hasKey(key) ? m_dict.value(key) : defaultValue;
}
bool hasKey(const QString &key) const { return m_dict.hasKey(key); }
void set(const QString &key, const QString &value, bool enabled = true) { m_dict.set(key, value, enabled); } void set(const QString &key, const QString &value, bool enabled = true);
void unset(const QString &key) { m_dict.unset(key); } void unset(const QString &key);
void modify(const NameValueItems &items) { m_dict.modify(items); } void modify(const NameValueItems &items);
bool hasChanges() const; bool hasChanges() const;
QStringList toStringList() const { return m_dict.toStringList(); } OsType osType() const;
QStringList toStringList() const;
QProcessEnvironment toProcessEnvironment() const; QProcessEnvironment toProcessEnvironment() const;
void appendOrSet(const QString &key, const QString &value, const QString &sep = QString()); void appendOrSet(const QString &key, const QString &value, const QString &sep = QString());
@@ -54,6 +51,9 @@ public:
void prependOrSetLibrarySearchPath(const FilePath &value); void prependOrSetLibrarySearchPath(const FilePath &value);
void prependOrSetLibrarySearchPaths(const FilePaths &values); void prependOrSetLibrarySearchPaths(const FilePaths &values);
void prependToPath(const FilePaths &values);
void appendToPath(const FilePaths &values);
void setupEnglishOutput(); void setupEnglishOutput();
FilePath searchInPath(const QString &executable, FilePath searchInPath(const QString &executable,
@@ -74,76 +74,58 @@ public:
FilePath expandVariables(const FilePath &input) const; FilePath expandVariables(const FilePath &input) const;
QStringList expandVariables(const QStringList &input) const; QStringList expandVariables(const QStringList &input) const;
OsType osType() const { return m_dict.osType(); } NameValueDictionary toDictionary() const; // FIXME: avoid
NameValueDictionary toDictionary() const { return m_dict; } // FIXME: avoid
NameValueItems diff(const Environment &other, bool checkAppendPrepend = false) const; // FIXME: avoid NameValueItems diff(const Environment &other, bool checkAppendPrepend = false) const; // FIXME: avoid
void setCombineWithDeviceEnvironment(bool combine) { m_combineWithDeviceEnvironment = combine; }
bool combineWithDeviceEnvironment() const { return m_combineWithDeviceEnvironment; }
struct Entry { QString key; QString value; bool enabled; }; struct Entry { QString key; QString value; bool enabled; };
using FindResult = std::optional<Entry>; using FindResult = std::optional<Entry>;
FindResult find(const QString &name) const; // Note res->key may differ in case from name. FindResult find(const QString &name) const; // Note res->key may differ in case from name.
void forEachEntry(const std::function<void (const QString &, const QString &, bool)> &callBack) const; void forEachEntry(const std::function<void (const QString &, const QString &, bool)> &callBack) const;
friend bool operator!=(const Environment &first, const Environment &second) bool operator!=(const Environment &other) const;
{ bool operator==(const Environment &other) const;
return first.m_dict != second.m_dict;
}
friend bool operator==(const Environment &first, const Environment &second)
{
return first.m_dict == second.m_dict;
}
static Environment systemEnvironment(); static Environment systemEnvironment();
static void modifySystemEnvironment(const EnvironmentItems &list); // use with care!!! static void modifySystemEnvironment(const EnvironmentItems &list); // use with care!!!
static void setSystemEnvironment(const Environment &environment); // don't use at all!!! static void setSystemEnvironment(const Environment &environment); // don't use at all!!!
private:
NameValueDictionary m_dict;
bool m_combineWithDeviceEnvironment = true;
};
class QTCREATOR_UTILS_EXPORT EnvironmentChange final
{
public:
EnvironmentChange() = default;
enum Type { enum Type {
SetSystemEnvironment, SetSystemEnvironment,
SetFixedDictionary, SetFixedDictionary,
SetValue, SetValue,
UnsetValue, UnsetValue,
PrependToPath, PrependOrSet,
AppendToPath, AppendOrSet,
Modify,
SetupEnglishOutput,
}; };
using Item = std::variant< using Item = std::variant<
std::monostate, // SetSystemEnvironment dummy std::monostate, // SetSystemEnvironment dummy
NameValueDictionary, // SetFixedDictionary NameValueDictionary, // SetFixedDictionary
QPair<QString, QString>, // SetValue std::tuple<QString, QString, bool>, // SetValue (key, value, enabled)
QString, // UnsetValue QString, // UnsetValue (key)
FilePath, // PrependToPath std::tuple<QString, QString, QString>, // PrependOrSet (key, value, separator)
FilePath // AppendToPath std::tuple<QString, QString, QString>, // AppendOrSet (key, value, separator)
NameValueItems, // Modify
std::monostate // SetupEnglishOutput
>; >;
static EnvironmentChange fromDictionary(const NameValueDictionary &dict); void addItem(const Item &item);
void applyToEnvironment(Environment &) const; Environment appliedToEnvironment(const Environment &base) const;
void addSetValue(const QString &key, const QString &value); const NameValueDictionary &resolved() const;
void addUnsetValue(const QString &key);
void addPrependToPath(const FilePaths &values);
void addAppendToPath(const FilePaths &values);
private: private:
QList<Item> m_changeItems; mutable QList<Item> m_changeItems;
mutable NameValueDictionary m_dict; // Latest resolved.
}; };
using EnviromentChange = Environment;
class QTCREATOR_UTILS_EXPORT EnvironmentProvider class QTCREATOR_UTILS_EXPORT EnvironmentProvider
{ {
public: public:

View File

@@ -32,7 +32,6 @@ namespace Utils {
class DeviceFileAccess; class DeviceFileAccess;
class Environment; class Environment;
class EnvironmentChange;
enum class FileStreamHandle; enum class FileStreamHandle;
template <class ...Args> using Continuation = std::function<void(Args...)>; template <class ...Args> using Continuation = std::function<void(Args...)>;

View File

@@ -246,6 +246,8 @@ void CallerHandle::start(const QString &program, const QStringList &arguments)
p.command = m_command; p.command = m_command;
p.arguments = m_arguments; p.arguments = m_arguments;
p.env = m_setup->m_environment.toStringList(); p.env = m_setup->m_environment.toStringList();
if (p.env.isEmpty())
p.env = Environment::systemEnvironment().toStringList();
p.workingDir = m_setup->m_workingDirectory.path(); p.workingDir = m_setup->m_workingDirectory.path();
p.processMode = m_setup->m_processMode; p.processMode = m_setup->m_processMode;
p.writeData = m_setup->m_writeData; p.writeData = m_setup->m_writeData;

View File

@@ -171,7 +171,7 @@ public:
FilePath m_initialBrowsePathOverride; FilePath m_initialBrowsePathOverride;
QString m_defaultValue; QString m_defaultValue;
FilePath m_baseDirectory; FilePath m_baseDirectory;
EnvironmentChange m_environmentChange; Environment m_environment;
BinaryVersionToolTipEventFilter *m_binaryVersionToolTipEventFilter = nullptr; BinaryVersionToolTipEventFilter *m_binaryVersionToolTipEventFilter = nullptr;
QList<QAbstractButton *> m_buttons; QList<QAbstractButton *> m_buttons;
const MacroExpander *m_macroExpander = globalMacroExpander(); const MacroExpander *m_macroExpander = globalMacroExpander();
@@ -196,8 +196,7 @@ FilePath PathChooserPrivate::expandedPath(const FilePath &input) const
FilePath path = input; FilePath path = input;
Environment env = path.deviceEnvironment(); Environment env = m_environment.appliedToEnvironment(path.deviceEnvironment());
m_environmentChange.applyToEnvironment(env);
path = env.expandVariables(path); path = env.expandVariables(path);
if (m_macroExpander) if (m_macroExpander)
@@ -324,20 +323,15 @@ void PathChooser::setBaseDirectory(const FilePath &base)
triggerChanged(); triggerChanged();
} }
void PathChooser::setEnvironment(const Environment &env)
{
setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary()));
}
FilePath PathChooser::baseDirectory() const FilePath PathChooser::baseDirectory() const
{ {
return d->m_baseDirectory; return d->m_baseDirectory;
} }
void PathChooser::setEnvironmentChange(const EnvironmentChange &env) void PathChooser::setEnvironment(const Environment &env)
{ {
QString oldExpand = filePath().toString(); QString oldExpand = filePath().toString();
d->m_environmentChange = env; d->m_environment = env;
if (filePath().toString() != oldExpand) { if (filePath().toString() != oldExpand) {
triggerChanged(); triggerChanged();
emit rawPathChanged(); emit rawPathChanged();

View File

@@ -20,7 +20,6 @@ namespace Utils {
class CommandLine; class CommandLine;
class MacroExpander; class MacroExpander;
class Environment; class Environment;
class EnvironmentChange;
class PathChooserPrivate; class PathChooserPrivate;
class QTCREATOR_UTILS_EXPORT PathChooser : public QWidget class QTCREATOR_UTILS_EXPORT PathChooser : public QWidget
@@ -77,7 +76,6 @@ public:
void setBaseDirectory(const FilePath &base); void setBaseDirectory(const FilePath &base);
void setEnvironment(const Environment &env); void setEnvironment(const Environment &env);
void setEnvironmentChange(const EnvironmentChange &change);
/** Returns the suggested label title when used in a form layout. */ /** Returns the suggested label title when used in a form layout. */
static QString label(); static QString label();

View File

@@ -353,13 +353,18 @@ public:
return; return;
} }
QProcessEnvironment penv = m_setup.m_environment.toProcessEnvironment();
if (penv.isEmpty())
penv = Environment::systemEnvironment().toProcessEnvironment();
const QStringList senv = penv.toStringList();
bool startResult bool startResult
= m_ptyProcess->startProcess(program, = m_ptyProcess->startProcess(program,
HostOsInfo::isWindowsHost() HostOsInfo::isWindowsHost()
? QStringList{m_setup.m_nativeArguments} << arguments ? QStringList{m_setup.m_nativeArguments} << arguments
: arguments, : arguments,
m_setup.m_workingDirectory.nativePath(), m_setup.m_workingDirectory.nativePath(),
m_setup.m_environment.toProcessEnvironment().toStringList(), senv,
m_setup.m_ptyData->size().width(), m_setup.m_ptyData->size().width(),
m_setup.m_ptyData->size().height()); m_setup.m_ptyData->size().height());
@@ -458,7 +463,9 @@ private:
handler->setWindowsSpecificStartupFlags(m_setup.m_belowNormalPriority, handler->setWindowsSpecificStartupFlags(m_setup.m_belowNormalPriority,
m_setup.m_createConsoleOnWindows); m_setup.m_createConsoleOnWindows);
m_process->setProcessEnvironment(m_setup.m_environment.toProcessEnvironment()); const QProcessEnvironment penv = m_setup.m_environment.toProcessEnvironment();
if (!penv.isEmpty())
m_process->setProcessEnvironment(penv);
m_process->setWorkingDirectory(m_setup.m_workingDirectory.path()); m_process->setWorkingDirectory(m_setup.m_workingDirectory.path());
m_process->setStandardInputFile(m_setup.m_standardInputFile); m_process->setStandardInputFile(m_setup.m_standardInputFile);
m_process->setProcessChannelMode(m_setup.m_processChannelMode); m_process->setProcessChannelMode(m_setup.m_processChannelMode);
@@ -715,7 +722,6 @@ public:
, q(parent) , q(parent)
, m_killTimer(this) , m_killTimer(this)
{ {
m_setup.m_controlEnvironment = Environment::systemEnvironment();
m_killTimer.setSingleShot(true); m_killTimer.setSingleShot(true);
connect(&m_killTimer, &QTimer::timeout, this, [this] { connect(&m_killTimer, &QTimer::timeout, this, [this] {
m_killTimer.stop(); m_killTimer.stop();
@@ -769,22 +775,6 @@ public:
return rootCommand; return rootCommand;
} }
Environment fullEnvironment() const
{
Environment env = m_setup.m_environment;
if (!env.hasChanges() && env.combineWithDeviceEnvironment()) {
// FIXME: Either switch to using EnvironmentChange instead of full Environments, or
// feed the full environment into the QtcProcess instead of fixing it up here.
// qWarning("QtcProcess::start: Empty environment set when running '%s'.",
// qPrintable(m_setup.m_commandLine.executable().toString()));
env = m_setup.m_commandLine.executable().deviceEnvironment();
}
// TODO: needs SshSettings
// if (m_runAsRoot)
// RunControl::provideAskPassEntry(env);
return env;
}
QtcProcess *q; QtcProcess *q;
std::unique_ptr<ProcessBlockingInterface> m_blockingInterface; std::unique_ptr<ProcessBlockingInterface> m_blockingInterface;
std::unique_ptr<ProcessInterface> m_process; std::unique_ptr<ProcessInterface> m_process;
@@ -1227,7 +1217,6 @@ void QtcProcess::start()
d->m_state = QProcess::Starting; d->m_state = QProcess::Starting;
d->m_process->m_setup = d->m_setup; d->m_process->m_setup = d->m_setup;
d->m_process->m_setup.m_commandLine = d->fullCommandLine(); d->m_process->m_setup.m_commandLine = d->fullCommandLine();
d->m_process->m_setup.m_environment = d->fullEnvironment();
d->emitGuardedSignal(&QtcProcess::starting); d->emitGuardedSignal(&QtcProcess::starting);
d->m_process->start(); d->m_process->start();
} }

View File

@@ -482,16 +482,6 @@ static QHash<QString, QString> merge(const QHash<QString, QString> &first,
return result; return result;
} }
static Utils::Environment merge(const Utils::Environment &first, const Utils::Environment &second)
{
Utils::Environment result = first;
second.forEachEntry([&](const QString &key, const QString &value, bool) {
result.set(key, value);
});
return result;
}
static CMakeConfig merge(const CMakeConfig &first, const CMakeConfig &second) static CMakeConfig merge(const CMakeConfig &first, const CMakeConfig &second)
{ {
return Utils::setUnionMerge<CMakeConfig>( return Utils::setUnionMerge<CMakeConfig>(
@@ -549,7 +539,7 @@ void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other)
if (!environment && other.environment) if (!environment && other.environment)
environment = other.environment; environment = other.environment;
else if (environment && other.environment) else if (environment && other.environment)
environment = merge(other.environment.value(), environment.value()); environment = environment.value().appliedToEnvironment(other.environment.value());
if (!warnings && other.warnings) if (!warnings && other.warnings)
warnings = other.warnings; warnings = other.warnings;
@@ -575,7 +565,7 @@ void PresetsDetails::BuildPreset::inheritFrom(const BuildPreset &other)
if (!environment && other.environment) if (!environment && other.environment)
environment = other.environment; environment = other.environment;
else if (environment && other.environment) else if (environment && other.environment)
environment = merge(other.environment.value(), environment.value()); environment = environment.value().appliedToEnvironment(other.environment.value());
if (!configurePreset && other.configurePreset) if (!configurePreset && other.configurePreset)
configurePreset = other.configurePreset; configurePreset = other.configurePreset;

View File

@@ -438,9 +438,9 @@ void SystemSettingsWidget::resetFileBrowser()
void SystemSettingsWidget::updatePath() void SystemSettingsWidget::updatePath()
{ {
EnvironmentChange change; Environment env;
change.addAppendToPath(VcsManager::additionalToolsPath()); env.appendToPath(VcsManager::additionalToolsPath());
m_patchChooser->setEnvironmentChange(change); m_patchChooser->setEnvironment(env);
} }
void SystemSettingsWidget::updateEnvironmentChangesLabel() void SystemSettingsWidget::updateEnvironmentChangesLabel()

View File

@@ -570,19 +570,12 @@ void ExecutableAspect::setExpectedKind(const PathChooser::Kind expectedKind)
Sets the environment in which paths will be searched when the expected kind Sets the environment in which paths will be searched when the expected kind
of paths is chosen as PathChooser::Command or PathChooser::ExistingCommand of paths is chosen as PathChooser::Command or PathChooser::ExistingCommand
to \a env. to \a env.
\sa Utils::StringAspect::setEnvironmentChange()
*/ */
void ExecutableAspect::setEnvironmentChange(const EnvironmentChange &change)
{
m_executable.setEnvironmentChange(change);
if (m_alternativeExecutable)
m_alternativeExecutable->setEnvironmentChange(change);
}
void ExecutableAspect::setEnvironment(const Environment &env) void ExecutableAspect::setEnvironment(const Environment &env)
{ {
setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary())); m_executable.setEnvironment(env);
if (m_alternativeExecutable)
m_alternativeExecutable->setEnvironment(env);
} }
/*! /*!

View File

@@ -168,7 +168,6 @@ public:
void setPlaceHolderText(const QString &placeHolderText); void setPlaceHolderText(const QString &placeHolderText);
void setHistoryCompleter(const QString &historyCompleterKey); void setHistoryCompleter(const QString &historyCompleterKey);
void setExpectedKind(const Utils::PathChooser::Kind expectedKind); void setExpectedKind(const Utils::PathChooser::Kind expectedKind);
void setEnvironmentChange(const Utils::EnvironmentChange &change);
void setEnvironment(const Utils::Environment &env); void setEnvironment(const Utils::Environment &env);
void setDisplayStyle(Utils::StringAspect::DisplayStyle style); void setDisplayStyle(Utils::StringAspect::DisplayStyle style);

View File

@@ -338,9 +338,6 @@ Environment LinuxDevicePrivate::getEnvironment()
QtcProcess getEnvProc; QtcProcess getEnvProc;
getEnvProc.setCommand({FilePath("env").onDevice(q->rootPath()), {}}); getEnvProc.setCommand({FilePath("env").onDevice(q->rootPath()), {}});
Environment inEnv;
inEnv.setCombineWithDeviceEnvironment(false);
getEnvProc.setEnvironment(inEnv);
getEnvProc.runBlocking(); getEnvProc.runBlocking();
const QString remoteOutput = getEnvProc.cleanedStdOut(); const QString remoteOutput = getEnvProc.cleanedStdOut();
@@ -919,16 +916,11 @@ LinuxDevice::LinuxDevice()
d->m_terminals.removeOne(proc); d->m_terminals.removeOne(proc);
}); });
// We recreate the same way that QtcProcess uses to create the actual environment.
const Environment finalEnv = (!env.hasChanges() && env.combineWithDeviceEnvironment())
? d->getEnvironment()
: env;
// If we will not set any environment variables, we can leave out the shell executable // If we will not set any environment variables, we can leave out the shell executable
// as the "ssh ..." call will automatically launch the default shell if there are // as the "ssh ..." call will automatically launch the default shell if there are
// no arguments. But if we will set environment variables, we need to explicitly // no arguments. But if we will set environment variables, we need to explicitly
// specify the shell executable. // specify the shell executable.
const QString shell = finalEnv.hasChanges() ? finalEnv.value_or("SHELL", "/bin/sh") const QString shell = env.hasChanges() ? env.value_or("SHELL", "/bin/sh") : QString();
: QString();
proc->setCommand({filePath(shell), {}}); proc->setCommand({filePath(shell), {}});
proc->setTerminalMode(TerminalMode::On); proc->setTerminalMode(TerminalMode::On);

View File

@@ -135,9 +135,9 @@ CommonSettingsWidget::CommonSettingsWidget(CommonOptionsPage *page)
void CommonSettingsWidget::updatePath() void CommonSettingsWidget::updatePath()
{ {
EnvironmentChange change; Environment env;
change.addAppendToPath(Core::VcsManager::additionalToolsPath()); env.appendToPath(Core::VcsManager::additionalToolsPath());
m_page->settings().sshPasswordPrompt.setEnvironmentChange(change); m_page->settings().sshPasswordPrompt.setEnvironment(env);
} }
void CommonSettingsWidget::apply() void CommonSettingsWidget::apply()