Commit 79be54ed authored by Oswald Buddenhagen's avatar Oswald Buddenhagen
Browse files

replace env variable injection with pervasive expando support

do not inject SOURCEDIR and BUILDDIR into the environment of
build steps and run configurations any more.
instead, all custom executable paths, argument lists and working
directories now support the %{sourceDir} and %{buildDir} macros.
this approach is more elegant and more scalable.
parent 1e362b0f
......@@ -3217,9 +3217,6 @@
referenced using the platform's native syntax: $VARNAME or ${VARNAME} on
Unix and %VARNAME% on Windows.
\note Qt Creator sets SOURCEDIR and BUILDDIR as part of the build environment.
For more information, see \l{Build Environment}.
\section1 Clean Steps
You can use the cleaning process to remove intermediate files. This process
......
......@@ -83,8 +83,6 @@ bool CMakeBuildConfiguration::fromMap(const QVariantMap &map)
m_msvcVersion = map.value(QLatin1String(MSVC_VERSION_KEY)).toString();
m_buildDirectory = map.value(QLatin1String(BUILD_DIRECTORY_KEY), cmakeTarget()->defaultBuildDirectory()).toString();
environment().set("BUILDDIR", m_buildDirectory);
return true;
}
......@@ -143,7 +141,6 @@ void CMakeBuildConfiguration::setBuildDirectory(const QString &buildDirectory)
if (m_buildDirectory == buildDirectory)
return;
m_buildDirectory = buildDirectory;
environment().set("BUILDDIR", m_buildDirectory);
emit buildDirectoryChanged();
emit environmentChanged();
}
......
......@@ -131,7 +131,8 @@ ProjectExplorer::LocalApplicationRunConfiguration::RunMode CMakeRunConfiguration
QString CMakeRunConfiguration::workingDirectory() const
{
return environment().expandVariables(baseWorkingDirectory());
return QDir::cleanPath(environment().expandVariables(
Utils::expandMacros(baseWorkingDirectory(), macroExpander())));
}
QString CMakeRunConfiguration::baseWorkingDirectory() const
......@@ -143,7 +144,7 @@ QString CMakeRunConfiguration::baseWorkingDirectory() const
QString CMakeRunConfiguration::commandLineArguments() const
{
return m_arguments;
return Utils::QtcProcess::expandMacros(m_arguments, macroExpander());
}
QString CMakeRunConfiguration::title() const
......
......@@ -129,18 +129,20 @@ bool MakeStep::init()
{
CMakeBuildConfiguration *bc = cmakeBuildConfiguration();
setEnabled(true);
setWorkingDirectory(bc->buildDirectory());
setCommand(bc->toolChain()->makeCommand());
QString arguments = Utils::QtcProcess::joinArgs(m_buildTargets);
Utils::QtcProcess::addArgs(&arguments, additionalArguments());
setArguments(arguments);
setEnvironment(bc->environment());
setEnabled(true);
setIgnoreReturnValue(m_clean);
setOutputParser(new ProjectExplorer::GnuMakeParser(workingDirectory()));
ProcessParameters *pp = processParameters();
pp->setMacroExpander(bc->macroExpander());
pp->setEnvironment(bc->environment());
pp->setWorkingDirectory(bc->buildDirectory());
pp->setCommand(bc->toolChain()->makeCommand());
pp->setArguments(arguments);
setOutputParser(new ProjectExplorer::GnuMakeParser(pp->effectiveWorkingDirectory()));
if (bc->toolChain())
appendOutputParser(bc->toolChain()->outputParser());
......@@ -292,15 +294,22 @@ void MakeStepConfigWidget::buildTargetsChanged()
void MakeStepConfigWidget::updateDetails()
{
QString arguments = Utils::QtcProcess::joinArgs(m_makeStep->m_buildTargets);
Utils::QtcProcess::addArgs(&arguments, m_makeStep->additionalArguments());
CMakeBuildConfiguration *bc = m_makeStep->cmakeBuildConfiguration();
ProjectExplorer::ToolChain *tc = bc->toolChain();
if (tc)
m_summaryText = tr("<b>Make:</b> %1 %2").arg(tc->makeCommand(), arguments);
else
if (tc) {
QString arguments = Utils::QtcProcess::joinArgs(m_makeStep->m_buildTargets);
Utils::QtcProcess::addArgs(&arguments, m_makeStep->additionalArguments());
ProcessParameters param;
param.setMacroExpander(bc->macroExpander());
param.setEnvironment(bc->environment());
param.setWorkingDirectory(bc->buildDirectory());
param.setCommand(tc->makeCommand());
param.setArguments(arguments);
m_summaryText = param.summary(displayName());
} else {
m_summaryText = tr("<b>Unknown Toolchain</b>");
}
emit updateSummary();
}
......
......@@ -104,16 +104,14 @@ bool GenericMakeStep::init()
GenericBuildConfiguration *bc = genericBuildConfiguration();
setEnabled(true);
QString buildDir = bc->buildDirectory();
Utils::expandMacros(&buildDir, Core::VariableManager::instance()->macroExpander());
setWorkingDirectory(buildDir);
setCommand(makeCommand());
setArguments(replacedArguments());
setEnvironment(bc->environment());
setOutputParser(new ProjectExplorer::GnuMakeParser(buildDir));
ProjectExplorer::ProcessParameters *pp = processParameters();
pp->setMacroExpander(bc->macroExpander());
pp->setWorkingDirectory(bc->buildDirectory());
pp->setEnvironment(bc->environment());
pp->setCommand(makeCommand());
pp->setArguments(allArguments());
setOutputParser(new ProjectExplorer::GnuMakeParser(pp->effectiveWorkingDirectory()));
if (bc->genericTarget()->genericProject()->toolChain())
appendOutputParser(bc->genericTarget()->genericProject()->toolChain()->outputParser());
......@@ -139,13 +137,11 @@ bool GenericMakeStep::fromMap(const QVariantMap &map)
return BuildStep::fromMap(map);
}
QString GenericMakeStep::replacedArguments() const
QString GenericMakeStep::allArguments() const
{
QString replacedArguments = m_makeArguments;
Utils::QtcProcess::addArgs(&replacedArguments, m_buildTargets);
Utils::QtcProcess::expandMacros(&replacedArguments,
Core::VariableManager::instance()->macroExpander());
return replacedArguments;
QString args = m_makeArguments;
Utils::QtcProcess::addArgs(&args, m_buildTargets);
return args;
}
QString GenericMakeStep::makeCommand() const
......@@ -257,8 +253,15 @@ void GenericMakeStepConfigWidget::init()
void GenericMakeStepConfigWidget::updateDetails()
{
m_summaryText = tr("<b>Make:</b> %1 %2")
.arg(m_makeStep->makeCommand(), m_makeStep->replacedArguments());
GenericBuildConfiguration *bc = m_makeStep->genericBuildConfiguration();
ProjectExplorer::ProcessParameters param;
param.setMacroExpander(bc->macroExpander());
param.setWorkingDirectory(bc->buildDirectory());
param.setEnvironment(bc->environment());
param.setCommand(m_makeStep->makeCommand());
param.setArguments(m_makeStep->allArguments());
m_summaryText = param.summary(displayName());
emit updateSummary();
}
......
......@@ -67,7 +67,7 @@ public:
virtual bool immutable() const;
bool buildsTarget(const QString &target) const;
void setBuildTarget(const QString &target, bool on);
QString replacedArguments() const;
QString allArguments() const;
QString makeCommand() const;
QVariantMap toMap() const;
......
......@@ -66,21 +66,6 @@ AbstractProcessStep::~AbstractProcessStep()
delete m_outputParserChain;
}
void AbstractProcessStep::setCommand(const QString &cmd)
{
m_command = cmd;
}
QString AbstractProcessStep::command() const
{
return m_command;
}
QString AbstractProcessStep::workingDirectory() const
{
return m_workingDirectory;
}
void AbstractProcessStep::setOutputParser(ProjectExplorer::IOutputParser *parser)
{
delete m_outputParserChain;
......@@ -109,36 +94,11 @@ ProjectExplorer::IOutputParser *AbstractProcessStep::outputParser() const
return m_outputParserChain;
}
void AbstractProcessStep::setWorkingDirectory(const QString &workingDirectory)
{
m_workingDirectory = workingDirectory;
}
void AbstractProcessStep::setArguments(const QString &arguments)
{
m_arguments = arguments;
}
QString AbstractProcessStep::arguments() const
{
return m_arguments;
}
void AbstractProcessStep::setEnabled(bool b)
{
m_enabled = b;
}
void AbstractProcessStep::setIgnoreReturnValue(bool b)
{
m_ignoreReturnValue = b;
}
void AbstractProcessStep::setEnvironment(Utils::Environment env)
{
m_environment = env;
}
bool AbstractProcessStep::init()
{
return true;
......@@ -151,13 +111,13 @@ void AbstractProcessStep::run(QFutureInterface<bool> &fi)
fi.reportResult(true);
return;
}
QDir wd(m_environment.expandVariables(m_workingDirectory));
QDir wd(m_param.effectiveWorkingDirectory());
if (!wd.exists())
wd.mkpath(wd.absolutePath());
m_process = new Utils::QtcProcess();
m_process->setWorkingDirectory(wd.absolutePath());
m_process->setEnvironment(m_environment);
m_process->setEnvironment(m_param.environment());
connect(m_process, SIGNAL(readyReadStandardOutput()),
this, SLOT(processReadyReadStdOutput()),
......@@ -170,7 +130,7 @@ void AbstractProcessStep::run(QFutureInterface<bool> &fi)
this, SLOT(slotProcessFinished(int, QProcess::ExitStatus)),
Qt::DirectConnection);
m_process->setCommand(expandedCommand(), m_arguments);
m_process->setCommand(m_param.effectiveCommand(), m_param.effectiveArguments());
m_process->start();
if (!m_process->waitForStarted()) {
processStartupFailed();
......@@ -212,13 +172,14 @@ void AbstractProcessStep::run(QFutureInterface<bool> &fi)
void AbstractProcessStep::processStarted()
{
emit addOutput(tr("Starting: \"%1\" %2\n")
.arg(QDir::toNativeSeparators(expandedCommand()), expandedArguments()),
.arg(QDir::toNativeSeparators(m_param.effectiveCommand()),
m_param.prettyArguments()),
BuildStep::MessageOutput);
}
void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status)
{
QString command = QDir::toNativeSeparators(expandedCommand());
QString command = QDir::toNativeSeparators(m_param.effectiveCommand());
if (status == QProcess::NormalExit && exitCode == 0) {
emit addOutput(tr("The process \"%1\" exited normally.").arg(command),
BuildStep::MessageOutput);
......@@ -234,7 +195,8 @@ void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus sta
void AbstractProcessStep::processStartupFailed()
{
emit addOutput(tr("Could not start process \"%1\" %2")
.arg(QDir::toNativeSeparators(expandedCommand()), expandedArguments()),
.arg(QDir::toNativeSeparators(m_param.effectiveCommand()),
m_param.prettyArguments()),
BuildStep::ErrorMessageOutput);
}
......@@ -350,30 +312,3 @@ void AbstractProcessStep::slotProcessFinished(int, QProcess::ExitStatus)
m_eventLoop->exit(0);
}
QString AbstractProcessStep::expandedCommand() const
{
QString command = m_environment.searchInPath(
m_command, QStringList() << m_environment.expandVariables(m_workingDirectory));
if (command.isEmpty())
command = m_command;
return command;
}
QString AbstractProcessStep::expandedArguments() const
{
#ifdef Q_OS_WIN
QString args;
#else
QStringList args;
#endif
Utils::QtcProcess::SplitError err;
args = Utils::QtcProcess::prepareArgs(m_arguments, &err, &m_environment);
if (err != Utils::QtcProcess::SplitOk)
return m_arguments; // Sorry, too complex - just fall back.
#ifdef Q_OS_WIN
return args;
#else
return Utils::QtcProcess::joinArgs(args);
#endif
}
......@@ -31,6 +31,7 @@
#define ABSTRACTPROCESSSTEP_H
#include "buildstep.h"
#include "processparameters.h"
#include <utils/environment.h>
......@@ -52,7 +53,7 @@ class IOutputParser;
It should be used as a base class if your buildstep just needs to run a process.
Usage:
Use setCommand(), setArguments(), setWorkingDirectory() to specify the process you want to run
Use processParameters() to configure the process you want to run
(you need to do that before calling AbstractProcessStep::init()).
Inside YourBuildStep::init() call AbstractProcessStep::init().
Inside YourBuildStep::run() call AbstractProcessStep::run(), which automatically starts the proces
......@@ -83,34 +84,19 @@ public:
virtual BuildStepConfigWidget *createConfigWidget() = 0;
virtual bool immutable() const = 0;
/// setCommand() sets the executable to run in the \p buildConfiguration
/// should be called from init()
void setCommand(const QString &cmd);
QString command() const;
/// sets the workingDirectory for the process for a buildConfiguration
/// should be called from init()
void setWorkingDirectory(const QString &workingDirectory);
/// sets the command line arguments used by the process for a \p buildConfiguration
/// should be called from init()
void setArguments(const QString &arguments);
QString arguments() const;
/// enables or disables a BuildStep
/// Disabled BuildSteps immediately return true from their run method
/// should be called from init()
void setEnabled(bool b);
void setEnabled(bool b) { m_enabled = b; }
/// obtain a reference to the parameters for the actual process to run.
/// should be used in init()
ProcessParameters *processParameters() { return &m_param; }
/// If ignoreReturnValue is set to true, then the abstractprocess step will
/// return success even if the return value indicates otherwise
/// should be called from init
void setIgnoreReturnValue(bool b);
/// Set the Environment for running the command
/// should be called from init()
void setEnvironment(Utils::Environment env);
QString workingDirectory() const;
// derived classes needs to call this function
/// Delete all existing output parsers and start a new chain with the
......@@ -124,11 +110,6 @@ protected:
AbstractProcessStep(BuildStepList *bsl, const QString &id);
AbstractProcessStep(BuildStepList *bsl, AbstractProcessStep *bs);
/// Get the fully expanded command name to run:
QString expandedCommand() const;
/// Get the fully expanded command line args. This is for display purposes only!
QString expandedArguments() const;
/// Called after the process is started
/// the default implementation adds a process started message to the output message
virtual void processStarted();
......@@ -158,18 +139,15 @@ private slots:
void taskAdded(const ProjectExplorer::Task &task);
void outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format);
private:
private:
QTimer *m_timer;
QFutureInterface<bool> *m_futureInterface;
QString m_workingDirectory;
QString m_command;
QString m_arguments;
ProcessParameters m_param;
bool m_enabled;
bool m_ignoreReturnValue;
Utils::QtcProcess *m_process;
QEventLoop *m_eventLoop;
Utils::Environment m_environment;
ProjectExplorer::IOutputParser *m_outputParserChain;
};
......
......@@ -28,6 +28,11 @@
**************************************************************************/
#include "applicationrunconfiguration.h"
#include "buildconfiguration.h"
#include <coreplugin/variablemanager.h>
#include <utils/stringutils.h>
namespace ProjectExplorer {
......@@ -47,4 +52,26 @@ LocalApplicationRunConfiguration::~LocalApplicationRunConfiguration()
{
}
namespace Internal {
class VarManMacroExpander : public Utils::AbstractQtcMacroExpander {
public:
virtual bool resolveMacro(const QString &name, QString *ret)
{
*ret = Core::VariableManager::instance()->value(name);
return !ret->isEmpty();
}
};
} // namespace Internal
Utils::AbstractMacroExpander *LocalApplicationRunConfiguration::macroExpander() const
{
if (BuildConfiguration *bc = activeBuildConfiguration())
return bc->macroExpander();
static Internal::VarManMacroExpander mx;
return &mx;
}
} // namespace ProjectExplorer
......@@ -36,6 +36,7 @@
#include "applicationlauncher.h"
namespace Utils {
class AbstractMacroExpander;
class Environment;
}
......@@ -63,6 +64,8 @@ public:
protected:
explicit LocalApplicationRunConfiguration(Target *target, const QString &id);
explicit LocalApplicationRunConfiguration(Target *target, LocalApplicationRunConfiguration *rc);
Utils::AbstractMacroExpander *macroExpander() const;
};
} // namespace ProjectExplorer
......
......@@ -36,6 +36,8 @@
#include "target.h"
#include "project.h"
#include <coreplugin/variablemanager.h>
#include <QtCore/QProcess>
using namespace ProjectExplorer;
......@@ -51,7 +53,8 @@ const char * const USER_ENVIRONMENT_CHANGES_KEY("ProjectExplorer.BuildConfigurat
BuildConfiguration::BuildConfiguration(Target *target, const QString &id) :
ProjectConfiguration(target, id),
m_clearSystemEnvironment(false)
m_clearSystemEnvironment(false),
m_macroExpander(this)
{
Q_ASSERT(target);
BuildStepList *bsl = new BuildStepList(this, QLatin1String(Constants::BUILDSTEPS_BUILD));
......@@ -67,7 +70,8 @@ BuildConfiguration::BuildConfiguration(Target *target, const QString &id) :
BuildConfiguration::BuildConfiguration(Target *target, BuildConfiguration *source) :
ProjectConfiguration(target, source),
m_clearSystemEnvironment(source->m_clearSystemEnvironment),
m_userEnvironmentChanges(source->m_userEnvironmentChanges)
m_userEnvironmentChanges(source->m_userEnvironmentChanges),
m_macroExpander(this)
{
Q_ASSERT(target);
// Do not clone stepLists here, do that in the derived constructor instead
......@@ -148,10 +152,6 @@ Utils::Environment BuildConfiguration::baseEnvironment() const
Utils::Environment result;
if (useSystemEnvironment())
result = Utils::Environment(QProcess::systemEnvironment());
result.set(QLatin1String("BUILDDIR"), QDir::toNativeSeparators(target()->project()->projectDirectory()));
result.set(QLatin1String("SOURCEDIR"), QDir::toNativeSeparators(target()->project()->projectDirectory()));
return result;
}
......@@ -207,6 +207,20 @@ void BuildConfiguration::cloneSteps(BuildConfiguration *source)
}
}
bool BuildConfigMacroExpander::resolveMacro(const QString &name, QString *ret)
{
if (name == QLatin1String("sourceDir")) {
*ret = QDir::toNativeSeparators(m_bc->target()->project()->projectDirectory());
return true;
}
if (name == QLatin1String("buildDir")) {
*ret = QDir::toNativeSeparators(m_bc->buildDirectory());
return true;
}
*ret = Core::VariableManager::instance()->value(name);
return !ret->isEmpty();
}
///
// IBuildConfigurationFactory
///
......
......@@ -33,6 +33,7 @@
#include "projectexplorer_export.h"
#include "projectconfiguration.h"
#include <utils/stringutils.h>
#include <utils/environment.h>
#include <QtCore/QString>
......@@ -42,10 +43,19 @@
namespace ProjectExplorer {
class BuildConfiguration;
class BuildStepList;
class Target;
class IOutputParser;
class BuildConfigMacroExpander : public Utils::AbstractQtcMacroExpander {
public:
BuildConfigMacroExpander(BuildConfiguration *bc) : m_bc(bc) {}
virtual bool resolveMacro(const QString &name, QString *ret);
private:
BuildConfiguration *m_bc;
};
class PROJECTEXPLORER_EXPORT BuildConfiguration : public ProjectConfiguration
{
Q_OBJECT
......@@ -79,6 +89,8 @@ public:
Target *target() const;
Utils::AbstractMacroExpander *macroExpander() { return &m_macroExpander; }
signals:
void environmentChanged();
void buildDirectoryChanged();
......@@ -95,6 +107,7 @@ private:
bool m_clearSystemEnvironment;
QList<Utils::EnvironmentItem> m_userEnvironmentChanges;
QList<BuildStepList *> m_stepLists;
BuildConfigMacroExpander m_macroExpander;
};
class PROJECTEXPLORER_EXPORT IBuildConfigurationFactory :
......@@ -127,4 +140,7 @@ signals:
Q_DECLARE_METATYPE(ProjectExplorer::BuildConfiguration *);
// Default directory to run custom (build) commands in.
#define DEFAULT_WORKING_DIR "%{buildDir}"
#endif // BUILDCONFIGURATION_H
......@@ -256,7 +256,7 @@ void CustomExecutableConfigurationWidget::changed()
return;
m_executableChooser->setPath(m_runConfiguration->rawExecutable());
m_commandLineArgumentsLineEdit->setText(m_runConfiguration->commandLineArguments());
m_commandLineArgumentsLineEdit->setText(m_runConfiguration->rawCommandLineArguments());
m_workingDirectory->setPath(m_runConfiguration->baseWorkingDirectory());
m_useTerminalCheck->setChecked(m_runConfiguration->runMode() == LocalApplicationRunConfiguration::Console);
}
......
......@@ -36,6 +36,8 @@
#include <projectexplorer/debugginghelper.h>
#include <projectexplorer/target.h>
#include <utils/qtcprocess.h>
#include <QtGui/QDialog>
#include <QtGui/QDialogButtonBox>
#include <QtGui/QLabel>
......@@ -56,12 +58,6 @@ const char * const WORKING_DIRECTORY_KEY("ProjectExplorer.CustomExecutableRunCon
const char * const USE_TERMINAL_KEY("ProjectExplorer.CustomExecutableRunConfiguration.UseTerminal");
const char * const USER_ENVIRONMENT_CHANGES_KEY("ProjectExplorer.CustomExecutableRunConfiguration.UserEnvironmentChanges");
const char * const BASE_ENVIRONMENT_BASE_KEY("ProjectExplorer.CustomExecutableRunConfiguration.BaseEnvironmentBase");
#ifdef Q_OS_WIN
const