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:
@@ -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)
|
||||
|
||||
|
||||
@@ -243,7 +243,7 @@ def qdump__Utils__DictKey(d, value):
|
||||
d.putStringValue(value["name"])
|
||||
|
||||
|
||||
def qdump__Utils__NameValueDictionary(d, value):
|
||||
def x_qdump__Utils__NameValueDictionary(d, value):
|
||||
dptr = d.extractPointer(value)
|
||||
if d.qtVersion() >= 0x60000:
|
||||
if dptr == 0:
|
||||
|
||||
@@ -632,7 +632,7 @@ public:
|
||||
QString m_placeHolderText;
|
||||
QString m_historyCompleterKey;
|
||||
PathChooser::Kind m_expectedKind = PathChooser::File;
|
||||
EnvironmentChange m_environmentChange;
|
||||
Environment m_environment;
|
||||
QPointer<ElidingLabel> m_labelDisplay;
|
||||
QPointer<FancyLineEdit> m_lineEditDisplay;
|
||||
QPointer<PathChooser> m_pathChooserDisplay;
|
||||
@@ -975,16 +975,11 @@ void StringAspect::setExpectedKind(const PathChooser::Kind 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)
|
||||
{
|
||||
setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary()));
|
||||
d->m_environment = env;
|
||||
if (d->m_pathChooserDisplay)
|
||||
d->m_pathChooserDisplay->setEnvironment(env);
|
||||
}
|
||||
|
||||
void StringAspect::setBaseFileName(const FilePath &baseFileName)
|
||||
@@ -1082,7 +1077,7 @@ void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
|
||||
d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey);
|
||||
if (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->setOpenTerminalHandler(d->m_openTerminal);
|
||||
if (defaultValue() == value())
|
||||
|
||||
@@ -383,7 +383,6 @@ public:
|
||||
void setPlaceHolderText(const QString &placeHolderText);
|
||||
void setHistoryCompleter(const QString &historyCompleterKey);
|
||||
void setExpectedKind(const PathChooser::Kind expectedKind);
|
||||
void setEnvironmentChange(const EnvironmentChange &change);
|
||||
void setEnvironment(const Environment &env);
|
||||
void setBaseFileName(const FilePath &baseFileName);
|
||||
void setUndoRedoEnabled(bool readOnly);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -22,27 +22,24 @@ namespace Utils {
|
||||
class QTCREATOR_UTILS_EXPORT Environment final
|
||||
{
|
||||
public:
|
||||
Environment() : m_dict(HostOsInfo::hostOs()) {}
|
||||
explicit Environment(OsType osType) : m_dict(osType) {}
|
||||
explicit Environment(const QStringList &env, OsType osType = HostOsInfo::hostOs())
|
||||
: m_dict(env, osType) {}
|
||||
explicit Environment(const NameValuePairs &nameValues) : m_dict(nameValues) {}
|
||||
explicit Environment(const NameValueDictionary &dict) : m_dict(dict) {}
|
||||
Environment();
|
||||
explicit Environment(OsType osType);
|
||||
explicit Environment(const QStringList &env, OsType osType = HostOsInfo::hostOs());
|
||||
explicit Environment(const NameValuePairs &nameValues);
|
||||
explicit Environment(const NameValueDictionary &dict);
|
||||
|
||||
QString value(const QString &key) const { return m_dict.value(key); }
|
||||
QString value_or(const QString &key, const QString &defaultValue) const
|
||||
{
|
||||
return m_dict.hasKey(key) ? m_dict.value(key) : defaultValue;
|
||||
}
|
||||
bool hasKey(const QString &key) const { return m_dict.hasKey(key); }
|
||||
QString value(const QString &key) const;
|
||||
QString value_or(const QString &key, const QString &defaultValue) const;
|
||||
bool hasKey(const QString &key) const;
|
||||
|
||||
void set(const QString &key, const QString &value, bool enabled = true) { m_dict.set(key, value, enabled); }
|
||||
void unset(const QString &key) { m_dict.unset(key); }
|
||||
void modify(const NameValueItems &items) { m_dict.modify(items); }
|
||||
void set(const QString &key, const QString &value, bool enabled = true);
|
||||
void unset(const QString &key);
|
||||
void modify(const NameValueItems &items);
|
||||
|
||||
bool hasChanges() const;
|
||||
|
||||
QStringList toStringList() const { return m_dict.toStringList(); }
|
||||
OsType osType() const;
|
||||
QStringList toStringList() const;
|
||||
QProcessEnvironment toProcessEnvironment() const;
|
||||
|
||||
void appendOrSet(const QString &key, const QString &value, const QString &sep = QString());
|
||||
@@ -54,6 +51,9 @@ public:
|
||||
void prependOrSetLibrarySearchPath(const FilePath &value);
|
||||
void prependOrSetLibrarySearchPaths(const FilePaths &values);
|
||||
|
||||
void prependToPath(const FilePaths &values);
|
||||
void appendToPath(const FilePaths &values);
|
||||
|
||||
void setupEnglishOutput();
|
||||
|
||||
FilePath searchInPath(const QString &executable,
|
||||
@@ -74,76 +74,58 @@ public:
|
||||
FilePath expandVariables(const FilePath &input) const;
|
||||
QStringList expandVariables(const QStringList &input) const;
|
||||
|
||||
OsType osType() const { return m_dict.osType(); }
|
||||
|
||||
NameValueDictionary toDictionary() const { return m_dict; } // FIXME: avoid
|
||||
NameValueDictionary toDictionary() 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; };
|
||||
using FindResult = std::optional<Entry>;
|
||||
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;
|
||||
|
||||
friend bool operator!=(const Environment &first, const Environment &second)
|
||||
{
|
||||
return first.m_dict != second.m_dict;
|
||||
}
|
||||
|
||||
friend bool operator==(const Environment &first, const Environment &second)
|
||||
{
|
||||
return first.m_dict == second.m_dict;
|
||||
}
|
||||
bool operator!=(const Environment &other) const;
|
||||
bool operator==(const Environment &other) const;
|
||||
|
||||
static Environment systemEnvironment();
|
||||
|
||||
static void modifySystemEnvironment(const EnvironmentItems &list); // use with care!!!
|
||||
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 {
|
||||
SetSystemEnvironment,
|
||||
SetFixedDictionary,
|
||||
SetValue,
|
||||
UnsetValue,
|
||||
PrependToPath,
|
||||
AppendToPath,
|
||||
PrependOrSet,
|
||||
AppendOrSet,
|
||||
Modify,
|
||||
SetupEnglishOutput,
|
||||
};
|
||||
|
||||
using Item = std::variant<
|
||||
std::monostate, // SetSystemEnvironment dummy
|
||||
NameValueDictionary, // SetFixedDictionary
|
||||
QPair<QString, QString>, // SetValue
|
||||
QString, // UnsetValue
|
||||
FilePath, // PrependToPath
|
||||
FilePath // AppendToPath
|
||||
std::monostate, // SetSystemEnvironment dummy
|
||||
NameValueDictionary, // SetFixedDictionary
|
||||
std::tuple<QString, QString, bool>, // SetValue (key, value, enabled)
|
||||
QString, // UnsetValue (key)
|
||||
std::tuple<QString, QString, QString>, // PrependOrSet (key, value, separator)
|
||||
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);
|
||||
void addUnsetValue(const QString &key);
|
||||
void addPrependToPath(const FilePaths &values);
|
||||
void addAppendToPath(const FilePaths &values);
|
||||
const NameValueDictionary &resolved() const;
|
||||
|
||||
private:
|
||||
QList<Item> m_changeItems;
|
||||
mutable QList<Item> m_changeItems;
|
||||
mutable NameValueDictionary m_dict; // Latest resolved.
|
||||
};
|
||||
|
||||
using EnviromentChange = Environment;
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT EnvironmentProvider
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace Utils {
|
||||
|
||||
class DeviceFileAccess;
|
||||
class Environment;
|
||||
class EnvironmentChange;
|
||||
enum class FileStreamHandle;
|
||||
|
||||
template <class ...Args> using Continuation = std::function<void(Args...)>;
|
||||
|
||||
@@ -246,6 +246,8 @@ void CallerHandle::start(const QString &program, const QStringList &arguments)
|
||||
p.command = m_command;
|
||||
p.arguments = m_arguments;
|
||||
p.env = m_setup->m_environment.toStringList();
|
||||
if (p.env.isEmpty())
|
||||
p.env = Environment::systemEnvironment().toStringList();
|
||||
p.workingDir = m_setup->m_workingDirectory.path();
|
||||
p.processMode = m_setup->m_processMode;
|
||||
p.writeData = m_setup->m_writeData;
|
||||
|
||||
@@ -171,7 +171,7 @@ public:
|
||||
FilePath m_initialBrowsePathOverride;
|
||||
QString m_defaultValue;
|
||||
FilePath m_baseDirectory;
|
||||
EnvironmentChange m_environmentChange;
|
||||
Environment m_environment;
|
||||
BinaryVersionToolTipEventFilter *m_binaryVersionToolTipEventFilter = nullptr;
|
||||
QList<QAbstractButton *> m_buttons;
|
||||
const MacroExpander *m_macroExpander = globalMacroExpander();
|
||||
@@ -196,8 +196,7 @@ FilePath PathChooserPrivate::expandedPath(const FilePath &input) const
|
||||
|
||||
FilePath path = input;
|
||||
|
||||
Environment env = path.deviceEnvironment();
|
||||
m_environmentChange.applyToEnvironment(env);
|
||||
Environment env = m_environment.appliedToEnvironment(path.deviceEnvironment());
|
||||
path = env.expandVariables(path);
|
||||
|
||||
if (m_macroExpander)
|
||||
@@ -324,20 +323,15 @@ void PathChooser::setBaseDirectory(const FilePath &base)
|
||||
triggerChanged();
|
||||
}
|
||||
|
||||
void PathChooser::setEnvironment(const Environment &env)
|
||||
{
|
||||
setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary()));
|
||||
}
|
||||
|
||||
FilePath PathChooser::baseDirectory() const
|
||||
{
|
||||
return d->m_baseDirectory;
|
||||
}
|
||||
|
||||
void PathChooser::setEnvironmentChange(const EnvironmentChange &env)
|
||||
void PathChooser::setEnvironment(const Environment &env)
|
||||
{
|
||||
QString oldExpand = filePath().toString();
|
||||
d->m_environmentChange = env;
|
||||
d->m_environment = env;
|
||||
if (filePath().toString() != oldExpand) {
|
||||
triggerChanged();
|
||||
emit rawPathChanged();
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace Utils {
|
||||
class CommandLine;
|
||||
class MacroExpander;
|
||||
class Environment;
|
||||
class EnvironmentChange;
|
||||
class PathChooserPrivate;
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT PathChooser : public QWidget
|
||||
@@ -77,7 +76,6 @@ public:
|
||||
void setBaseDirectory(const FilePath &base);
|
||||
|
||||
void setEnvironment(const Environment &env);
|
||||
void setEnvironmentChange(const EnvironmentChange &change);
|
||||
|
||||
/** Returns the suggested label title when used in a form layout. */
|
||||
static QString label();
|
||||
|
||||
@@ -353,13 +353,18 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
QProcessEnvironment penv = m_setup.m_environment.toProcessEnvironment();
|
||||
if (penv.isEmpty())
|
||||
penv = Environment::systemEnvironment().toProcessEnvironment();
|
||||
const QStringList senv = penv.toStringList();
|
||||
|
||||
bool startResult
|
||||
= m_ptyProcess->startProcess(program,
|
||||
HostOsInfo::isWindowsHost()
|
||||
? QStringList{m_setup.m_nativeArguments} << arguments
|
||||
: arguments,
|
||||
m_setup.m_workingDirectory.nativePath(),
|
||||
m_setup.m_environment.toProcessEnvironment().toStringList(),
|
||||
senv,
|
||||
m_setup.m_ptyData->size().width(),
|
||||
m_setup.m_ptyData->size().height());
|
||||
|
||||
@@ -458,7 +463,9 @@ private:
|
||||
handler->setWindowsSpecificStartupFlags(m_setup.m_belowNormalPriority,
|
||||
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->setStandardInputFile(m_setup.m_standardInputFile);
|
||||
m_process->setProcessChannelMode(m_setup.m_processChannelMode);
|
||||
@@ -715,7 +722,6 @@ public:
|
||||
, q(parent)
|
||||
, m_killTimer(this)
|
||||
{
|
||||
m_setup.m_controlEnvironment = Environment::systemEnvironment();
|
||||
m_killTimer.setSingleShot(true);
|
||||
connect(&m_killTimer, &QTimer::timeout, this, [this] {
|
||||
m_killTimer.stop();
|
||||
@@ -769,22 +775,6 @@ public:
|
||||
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;
|
||||
std::unique_ptr<ProcessBlockingInterface> m_blockingInterface;
|
||||
std::unique_ptr<ProcessInterface> m_process;
|
||||
@@ -1227,7 +1217,6 @@ void QtcProcess::start()
|
||||
d->m_state = QProcess::Starting;
|
||||
d->m_process->m_setup = d->m_setup;
|
||||
d->m_process->m_setup.m_commandLine = d->fullCommandLine();
|
||||
d->m_process->m_setup.m_environment = d->fullEnvironment();
|
||||
d->emitGuardedSignal(&QtcProcess::starting);
|
||||
d->m_process->start();
|
||||
}
|
||||
|
||||
@@ -482,16 +482,6 @@ static QHash<QString, QString> merge(const QHash<QString, QString> &first,
|
||||
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)
|
||||
{
|
||||
return Utils::setUnionMerge<CMakeConfig>(
|
||||
@@ -549,7 +539,7 @@ void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other)
|
||||
if (!environment && other.environment)
|
||||
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)
|
||||
warnings = other.warnings;
|
||||
@@ -575,7 +565,7 @@ void PresetsDetails::BuildPreset::inheritFrom(const BuildPreset &other)
|
||||
if (!environment && other.environment)
|
||||
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)
|
||||
configurePreset = other.configurePreset;
|
||||
|
||||
@@ -438,9 +438,9 @@ void SystemSettingsWidget::resetFileBrowser()
|
||||
|
||||
void SystemSettingsWidget::updatePath()
|
||||
{
|
||||
EnvironmentChange change;
|
||||
change.addAppendToPath(VcsManager::additionalToolsPath());
|
||||
m_patchChooser->setEnvironmentChange(change);
|
||||
Environment env;
|
||||
env.appendToPath(VcsManager::additionalToolsPath());
|
||||
m_patchChooser->setEnvironment(env);
|
||||
}
|
||||
|
||||
void SystemSettingsWidget::updateEnvironmentChangesLabel()
|
||||
|
||||
@@ -570,19 +570,12 @@ void ExecutableAspect::setExpectedKind(const PathChooser::Kind expectedKind)
|
||||
Sets the environment in which paths will be searched when the expected kind
|
||||
of paths is chosen as PathChooser::Command or PathChooser::ExistingCommand
|
||||
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)
|
||||
{
|
||||
setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary()));
|
||||
m_executable.setEnvironment(env);
|
||||
if (m_alternativeExecutable)
|
||||
m_alternativeExecutable->setEnvironment(env);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -168,7 +168,6 @@ public:
|
||||
void setPlaceHolderText(const QString &placeHolderText);
|
||||
void setHistoryCompleter(const QString &historyCompleterKey);
|
||||
void setExpectedKind(const Utils::PathChooser::Kind expectedKind);
|
||||
void setEnvironmentChange(const Utils::EnvironmentChange &change);
|
||||
void setEnvironment(const Utils::Environment &env);
|
||||
void setDisplayStyle(Utils::StringAspect::DisplayStyle style);
|
||||
|
||||
|
||||
@@ -338,9 +338,6 @@ Environment LinuxDevicePrivate::getEnvironment()
|
||||
|
||||
QtcProcess getEnvProc;
|
||||
getEnvProc.setCommand({FilePath("env").onDevice(q->rootPath()), {}});
|
||||
Environment inEnv;
|
||||
inEnv.setCombineWithDeviceEnvironment(false);
|
||||
getEnvProc.setEnvironment(inEnv);
|
||||
getEnvProc.runBlocking();
|
||||
|
||||
const QString remoteOutput = getEnvProc.cleanedStdOut();
|
||||
@@ -919,16 +916,11 @@ LinuxDevice::LinuxDevice()
|
||||
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
|
||||
// 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
|
||||
// specify the shell executable.
|
||||
const QString shell = finalEnv.hasChanges() ? finalEnv.value_or("SHELL", "/bin/sh")
|
||||
: QString();
|
||||
const QString shell = env.hasChanges() ? env.value_or("SHELL", "/bin/sh") : QString();
|
||||
|
||||
proc->setCommand({filePath(shell), {}});
|
||||
proc->setTerminalMode(TerminalMode::On);
|
||||
|
||||
@@ -135,9 +135,9 @@ CommonSettingsWidget::CommonSettingsWidget(CommonOptionsPage *page)
|
||||
|
||||
void CommonSettingsWidget::updatePath()
|
||||
{
|
||||
EnvironmentChange change;
|
||||
change.addAppendToPath(Core::VcsManager::additionalToolsPath());
|
||||
m_page->settings().sshPasswordPrompt.setEnvironmentChange(change);
|
||||
Environment env;
|
||||
env.appendToPath(Core::VcsManager::additionalToolsPath());
|
||||
m_page->settings().sshPasswordPrompt.setEnvironment(env);
|
||||
}
|
||||
|
||||
void CommonSettingsWidget::apply()
|
||||
|
||||
Reference in New Issue
Block a user