Restore a clean system environment

... in case we are being run from another instance of Qt Creator.
Remember which environment variables were amended to allow Qt Creator to
start and reset them to their original values on start-up. This makes it
possible to e.g. use Qt versions different from the ones that Qt Creator
itself was built with.

Change-Id: I6fc30823acb977f6cad4f67ff2e112c58792e30d
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2024-10-18 14:03:33 +02:00
parent 586fe96594
commit e281855dfa
11 changed files with 96 additions and 7 deletions

View File

@@ -552,6 +552,33 @@ int main(int argc, char **argv)
Options options = parseCommandLine(argc, argv);
applicationDirPath(argv[0]);
// Remove entries from environment variables that were set up by Qt Creator to run
// the application (in this case, us).
// TODO: We should be able to merge at least some of the stuff below with similar intent
// into a more generalized version of this.
EnvironmentItems specialItems;
EnvironmentItems diff;
Environment::systemEnvironment().forEachEntry(
[&specialItems](const QString &name, const QString &value, bool enabled) {
if (enabled && name.startsWith("_QTC_"))
specialItems.emplaceBack(name, value, EnvironmentItem::SetEnabled);
});
for (const EnvironmentItem &item : std::as_const(specialItems)) {
const QString varName = item.name.mid(5);
const FilePaths addedPaths
= Environment::pathListFromValue(item.value, HostOsInfo::hostOs());
FilePaths allPaths = Environment::systemEnvironment().pathListValue(varName);
Utils::eraseOne(allPaths, [&addedPaths](const FilePath &p) {
return addedPaths.contains(p);
});
diff.emplaceBack(
varName,
Environment::valueFromPathList(allPaths, HostOsInfo::hostOs()),
EnvironmentItem::SetEnabled);
diff.emplaceBack(item.name, "", EnvironmentItem::Unset);
}
Environment::modifySystemEnvironment(diff);
if (qEnvironmentVariableIsSet("QTC_DO_NOT_PROPAGATE_LD_PRELOAD"))
Environment::modifySystemEnvironment({{"LD_PRELOAD", "", EnvironmentItem::Unset}});

View File

@@ -26,6 +26,7 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser)
{
const QString toolDir = QLatin1String("QTTOOLDIR=\"");
Process proc;
proc.setEnvironment(qtChooser.deviceEnvironment());
proc.setCommand({qtChooser, {"-print-env"}});
proc.runBlocking(1s);
if (proc.result() != ProcessResult::FinishedWithSuccess)
@@ -107,6 +108,7 @@ QString BuildableHelperLibrary::qtVersionForQMake(const FilePath &qmakePath)
return QString();
Process qmake;
qmake.setEnvironment(qmakePath.deviceEnvironment());
qmake.setCommand({qmakePath, {"--version"}});
qmake.runBlocking(5s);
if (qmake.result() != ProcessResult::FinishedWithSuccess) {

View File

@@ -208,6 +208,12 @@ Environment Environment::systemEnvironment()
return *staticSystemEnvironment();
}
const Environment &Environment::originalSystemEnvironment()
{
static const Environment env(QProcessEnvironment::systemEnvironment().toStringList());
return env;
}
void Environment::setupEnglishOutput()
{
addItem(Item{std::in_place_index_t<SetupEnglishOutput>()});
@@ -238,8 +244,24 @@ FilePaths Environment::path() const
FilePaths Environment::pathListValue(const QString &varName) const
{
const QStringList pathComponents = expandedValueForKey(varName).split(
OsSpecificAspects::pathListSeparator(osType()), Qt::SkipEmptyParts);
return pathListFromValue(expandedValueForKey(varName), osType());
}
void Environment::setPathListValue(const QString &varName, const FilePaths &paths)
{
set(varName, valueFromPathList(paths, osType()));
}
QString Environment::valueFromPathList(const FilePaths &paths, OsType osType)
{
return transform(paths, &FilePath::toUserOutput)
.join(OsSpecificAspects::pathListSeparator(osType));
}
FilePaths Environment::pathListFromValue(const QString &value, OsType osType)
{
const QStringList pathComponents
= value.split(OsSpecificAspects::pathListSeparator(osType), Qt::SkipEmptyParts);
return transform(pathComponents, &FilePath::fromUserInput);
}

View File

@@ -72,6 +72,9 @@ public:
FilePaths path() const;
FilePaths pathListValue(const QString &varName) const;
void setPathListValue(const QString &varName, const FilePaths &paths);
static QString valueFromPathList(const FilePaths &paths, OsType osType);
static FilePaths pathListFromValue(const QString &value, OsType osType);
QString expandedValueForKey(const QString &key) const;
QString expandVariables(const QString &input) const;
@@ -91,6 +94,7 @@ public:
bool operator==(const Environment &other) const;
static Environment systemEnvironment();
static const Environment &originalSystemEnvironment();
static void modifySystemEnvironment(const EnvironmentItems &list); // use with care!!!
static void setSystemEnvironment(const Environment &environment); // don't use at all!!!

View File

@@ -52,8 +52,22 @@ protected:
environment.addModifier([this](Environment &env) {
BuildTargetInfo bti = buildTargetInfo();
if (bti.runEnvModifier)
if (bti.runEnvModifier) {
Environment old = env;
bti.runEnvModifier(env, useLibraryPaths());
const EnvironmentItems diff = old.diff(env, true);
for (const EnvironmentItem &i : diff) {
switch (i.operation) {
case EnvironmentItem::SetEnabled:
case EnvironmentItem::Prepend:
case EnvironmentItem::Append:
env.addItem(std::make_tuple("_QTC_" + i.name, i.value));
break;
default:
break;
}
}
}
});
setUpdater([this] { updateTargetInformation(); });

View File

@@ -80,8 +80,11 @@ void SshParameters::setupSshEnvironment(Process *process)
Environment env = process->controlEnvironment();
if (!env.hasChanges())
env = Environment::systemEnvironment();
if (SshSettings::askpassFilePath().exists()) {
env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput());
const FilePath askPass = SshSettings::askpassFilePath();
if (askPass.exists()) {
if (askPass.fileName().contains("qtc"))
env = Environment::originalSystemEnvironment();
env.set("SSH_ASKPASS", askPass.toUserOutput());
env.set("SSH_ASKPASS_REQUIRE", "force");
// OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform.

View File

@@ -15,6 +15,7 @@
#include <qmljstools/qmljstoolsconstants.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitaspect.h>
#include <utils/environment.h>
#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
@@ -224,6 +225,7 @@ QString QbsProfileManager::runQbsConfig(QbsConfigOp op, const QString &key, cons
if (qbsConfigExe.isEmpty() || !qbsConfigExe.exists())
return {};
Utils::Process qbsConfig;
qbsConfig.setEnvironment(QbsSettings::qbsProcessEnvironment());
qbsConfig.setCommand({qbsConfigExe, args});
qbsConfig.start();
using namespace std::chrono_literals;

View File

@@ -201,6 +201,7 @@ void QbsSession::initialize()
QTimer::singleShot(0, this, [this] { setError(Error::InvalidQbsExecutable); });
return;
}
d->qbsProcess->setEnvironment(QbsSettings::qbsProcessEnvironment());
d->qbsProcess->setCommand({qbsExe, {"session"}});
d->qbsProcess->start();
}

View File

@@ -28,12 +28,20 @@ const char QBS_EXE_KEY[] = "QbsProjectManager/QbsExecutable";
const char QBS_DEFAULT_INSTALL_DIR_KEY[] = "QbsProjectManager/DefaultInstallDir";
const char USE_CREATOR_SETTINGS_KEY[] = "QbsProjectManager/useCreatorDir";
static Environment getQbsProcessEnvironment(const FilePath &qbsExe)
{
if (qbsExe == QbsSettings::defaultQbsExecutableFilePath())
return Environment::originalSystemEnvironment();
return qbsExe.deviceEnvironment();
}
static QString getQbsVersion(const FilePath &qbsExe)
{
if (qbsExe.isEmpty() || !qbsExe.exists())
return {};
Process qbsProc;
qbsProc.setCommand({qbsExe, {"--version"}});
qbsProc.setEnvironment(getQbsProcessEnvironment(qbsExe));
qbsProc.start();
using namespace std::chrono_literals;
if (!qbsProc.waitForFinished(5s) || qbsProc.exitCode() != 0)
@@ -82,6 +90,11 @@ FilePath QbsSettings::qbsConfigFilePath()
return qbsConfig;
}
Environment QbsSettings::qbsProcessEnvironment()
{
return getQbsProcessEnvironment(qbsExecutableFilePath());
}
QString QbsSettings::defaultInstallDirTemplate()
{
return instance().m_settings.defaultInstallDirTemplate;

View File

@@ -9,6 +9,8 @@
#include <QVersionNumber>
namespace Utils { class Environment; }
namespace QbsProjectManager::Internal {
class QbsSettingsData
@@ -29,6 +31,7 @@ public:
static Utils::FilePath qbsExecutableFilePath();
static Utils::FilePath defaultQbsExecutableFilePath();
static Utils::FilePath qbsConfigFilePath();
static Utils::Environment qbsProcessEnvironment();
static bool hasQbsExecutable();
static QString defaultInstallDirTemplate();
static bool useCreatorSettingsDirForQbs();

View File

@@ -1337,8 +1337,6 @@ void QtVersionPrivate::updateVersionInfo()
m_qmakeIsExecutable = false;
qWarning("Cannot update Qt version information from %s: %s.",
qPrintable(m_qmakeCommand.displayName()), qPrintable(error));
qWarning("If this appears when running Qt Creator in Qt Creator make "
"sure to disable \"Add build library search path to LD_LIBRARY_PATH\"");
return;
}
m_qmakeIsExecutable = true;