forked from qt-creator/qt-creator
ProjectExplorer: Move BuildSystem owership to BuildConfiguration
... or Target. This patch moves build system from conceptually "one per project" to "one per target (i.e. per project-and-kit)" or "per BuildConfigurations" for targets where the builds differ significantly. Building requires usually items from the kit (Qt version, compiler, ...) so a target-agnostic build is practically almost always wrong. Moving the build system to the target also has the potential to solve issues caused by switching targets while parsing, that used Project::activeTarget() regularly, with potentially different results before and after the switch. This patch might create performance/size regressions when several targets are set up per project as the build system implementation's internal data are duplicated in this case. The idea is to fix that by sharing per-project pieces again in the project implementation once these problems occur. Change-Id: I87f640ce418b93175b5029124eaa55f3b8721dca Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -84,101 +84,17 @@ const char CONFIGURATION_KEY[] = "CMake.Configuration";
|
||||
|
||||
CMakeBuildConfiguration::CMakeBuildConfiguration(Target *parent, Core::Id id)
|
||||
: BuildConfiguration(parent, id)
|
||||
, m_buildDirManager(qobject_cast<CMakeProject *>(parent->project()))
|
||||
{
|
||||
m_buildSystem = new CMakeBuildSystem(this);
|
||||
setBuildDirectory(shadowBuildDirectory(project()->projectFilePath(),
|
||||
target()->kit(),
|
||||
displayName(),
|
||||
BuildConfiguration::Unknown));
|
||||
}
|
||||
|
||||
BuildSystem *bs = qobject_cast<CMakeBuildSystem *>(project()->buildSystem());
|
||||
|
||||
// BuildDirManager:
|
||||
connect(&m_buildDirManager, &BuildDirManager::requestReparse, this, [this, bs]() {
|
||||
if (isActive())
|
||||
bs->requestParse();
|
||||
});
|
||||
connect(&m_buildDirManager, &BuildDirManager::requestDelayedReparse, this, [this, bs]() {
|
||||
if (isActive())
|
||||
bs->requestDelayedParse();
|
||||
});
|
||||
connect(&m_buildDirManager,
|
||||
&BuildDirManager::dataAvailable,
|
||||
this,
|
||||
&CMakeBuildConfiguration::handleParsingSucceeded);
|
||||
connect(&m_buildDirManager,
|
||||
&BuildDirManager::errorOccured,
|
||||
this,
|
||||
&CMakeBuildConfiguration::handleParsingFailed);
|
||||
connect(&m_buildDirManager, &BuildDirManager::parsingStarted, this, [this]() {
|
||||
clearError(CMakeBuildConfiguration::ForceEnabledChanged::True);
|
||||
});
|
||||
|
||||
// Kit changed:
|
||||
connect(KitManager::instance(), &KitManager::kitUpdated, this, [this](Kit *k) {
|
||||
if (k != target()->kit())
|
||||
return; // not for us...
|
||||
// Build configuration has not changed, but Kit settings might have:
|
||||
// reparse and check the configuration, independent of whether the reader has changed
|
||||
m_buildDirManager.setParametersAndRequestParse(BuildDirParameters(this),
|
||||
BuildDirManager::REPARSE_CHECK_CONFIGURATION);
|
||||
});
|
||||
|
||||
// Became active/inactive:
|
||||
connect(project(), &Project::activeBuildConfigurationChanged, this, [this]() {
|
||||
if (isActive()) {
|
||||
// Build configuration has switched:
|
||||
// * Check configuration if reader changes due to it not existing yet:-)
|
||||
// * run cmake without configuration arguments if the reader stays
|
||||
m_buildDirManager
|
||||
.setParametersAndRequestParse(BuildDirParameters(this),
|
||||
BuildDirManager::REPARSE_CHECK_CONFIGURATION);
|
||||
} else {
|
||||
m_buildDirManager.stopParsingAndClearState();
|
||||
}
|
||||
});
|
||||
|
||||
// BuildConfiguration changed:
|
||||
connect(this, &CMakeBuildConfiguration::environmentChanged, this, [this]() {
|
||||
if (isActive()) {
|
||||
// The environment on our BC has changed:
|
||||
// * Error out if the reader updates, cannot happen since all BCs share a target/kit.
|
||||
// * run cmake without configuration arguments if the reader stays
|
||||
m_buildDirManager
|
||||
.setParametersAndRequestParse(BuildDirParameters(this),
|
||||
BuildDirManager::REPARSE_CHECK_CONFIGURATION);
|
||||
}
|
||||
});
|
||||
connect(this, &CMakeBuildConfiguration::buildDirectoryChanged, this, [this]() {
|
||||
if (isActive()) {
|
||||
// The build directory of our BC has changed:
|
||||
// * Error out if the reader updates, cannot happen since all BCs share a target/kit.
|
||||
// * run cmake without configuration arguments if the reader stays
|
||||
// If no configuration exists, then the arguments will get added automatically by
|
||||
// the reader.
|
||||
m_buildDirManager
|
||||
.setParametersAndRequestParse(BuildDirParameters(this),
|
||||
BuildDirManager::REPARSE_CHECK_CONFIGURATION);
|
||||
}
|
||||
});
|
||||
connect(this, &CMakeBuildConfiguration::configurationForCMakeChanged, this, [this]() {
|
||||
if (isActive()) {
|
||||
// The CMake configuration has changed on our BC:
|
||||
// * Error out if the reader updates, cannot happen since all BCs share a target/kit.
|
||||
// * run cmake with configuration arguments if the reader stays
|
||||
m_buildDirManager
|
||||
.setParametersAndRequestParse(BuildDirParameters(this),
|
||||
BuildDirManager::REPARSE_FORCE_CONFIGURATION);
|
||||
}
|
||||
});
|
||||
|
||||
connect(parent->project(), &Project::projectFileIsDirty, this, [this]() {
|
||||
if (isActive()) {
|
||||
m_buildDirManager
|
||||
.setParametersAndRequestParse(BuildDirParameters(this),
|
||||
BuildDirManager::REPARSE_DEFAULT);
|
||||
}
|
||||
});
|
||||
CMakeBuildConfiguration::~CMakeBuildConfiguration()
|
||||
{
|
||||
delete m_buildSystem;
|
||||
}
|
||||
|
||||
void CMakeBuildConfiguration::initialize()
|
||||
@@ -284,82 +200,11 @@ bool CMakeBuildConfiguration::fromMap(const QVariantMap &map)
|
||||
return true;
|
||||
}
|
||||
|
||||
const QList<BuildTargetInfo> CMakeBuildConfiguration::appTargets() const
|
||||
{
|
||||
QList<BuildTargetInfo> appTargetList;
|
||||
const bool forAndroid = DeviceTypeKitAspect::deviceTypeId(target()->kit())
|
||||
== Android::Constants::ANDROID_DEVICE_TYPE;
|
||||
for (const CMakeBuildTarget &ct : m_buildTargets) {
|
||||
if (ct.targetType == UtilityType)
|
||||
continue;
|
||||
|
||||
if (ct.targetType == ExecutableType || (forAndroid && ct.targetType == DynamicLibraryType)) {
|
||||
BuildTargetInfo bti;
|
||||
bti.displayName = ct.title;
|
||||
bti.targetFilePath = ct.executable;
|
||||
bti.projectFilePath = ct.sourceDirectory.stringAppended("/");
|
||||
bti.workingDirectory = ct.workingDirectory;
|
||||
bti.buildKey = ct.title;
|
||||
|
||||
// Workaround for QTCREATORBUG-19354:
|
||||
bti.runEnvModifier = [this](Environment &env, bool) {
|
||||
if (HostOsInfo::isWindowsHost()) {
|
||||
const Kit *k = target()->kit();
|
||||
if (const QtSupport::BaseQtVersion *qt = QtSupport::QtKitAspect::qtVersion(k))
|
||||
env.prependOrSetPath(qt->binPath().toString());
|
||||
}
|
||||
};
|
||||
|
||||
appTargetList.append(bti);
|
||||
}
|
||||
}
|
||||
|
||||
return appTargetList;
|
||||
}
|
||||
|
||||
DeploymentData CMakeBuildConfiguration::deploymentData() const
|
||||
{
|
||||
DeploymentData result;
|
||||
|
||||
QDir sourceDir = target()->project()->projectDirectory().toString();
|
||||
QDir buildDir = buildDirectory().toString();
|
||||
|
||||
QString deploymentPrefix;
|
||||
QString deploymentFilePath = sourceDir.filePath("QtCreatorDeployment.txt");
|
||||
|
||||
bool hasDeploymentFile = QFileInfo::exists(deploymentFilePath);
|
||||
if (!hasDeploymentFile) {
|
||||
deploymentFilePath = buildDir.filePath("QtCreatorDeployment.txt");
|
||||
hasDeploymentFile = QFileInfo::exists(deploymentFilePath);
|
||||
}
|
||||
if (!hasDeploymentFile)
|
||||
return result;
|
||||
|
||||
deploymentPrefix = result.addFilesFromDeploymentFile(deploymentFilePath,
|
||||
sourceDir.absolutePath());
|
||||
for (const CMakeBuildTarget &ct : m_buildTargets) {
|
||||
if (ct.targetType == ExecutableType || ct.targetType == DynamicLibraryType) {
|
||||
if (!ct.executable.isEmpty()
|
||||
&& result.deployableForLocalFile(ct.executable).localFilePath() != ct.executable) {
|
||||
result.addFile(ct.executable.toString(),
|
||||
deploymentPrefix + buildDir.relativeFilePath(ct.executable.toFileInfo().dir().path()),
|
||||
DeployableFile::TypeExecutable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList CMakeBuildConfiguration::buildTargetTitles() const
|
||||
{
|
||||
return transform(m_buildTargets, &CMakeBuildTarget::title);
|
||||
}
|
||||
|
||||
const QList<CMakeBuildTarget> &CMakeBuildConfiguration::buildTargets() const
|
||||
{
|
||||
return m_buildTargets;
|
||||
}
|
||||
|
||||
FilePath CMakeBuildConfiguration::shadowBuildDirectory(const FilePath &projectFilePath,
|
||||
const Kit *k,
|
||||
@@ -408,11 +253,6 @@ void CMakeBuildConfiguration::setConfigurationFromCMake(const CMakeConfig &confi
|
||||
m_configurationFromCMake = config;
|
||||
}
|
||||
|
||||
void CMakeBuildConfiguration::setBuildTargets(const QList<CMakeBuildTarget> &targets)
|
||||
{
|
||||
m_buildTargets = targets;
|
||||
}
|
||||
|
||||
void CMakeBuildConfiguration::setConfigurationForCMake(const QList<ConfigModel::DataItem> &items)
|
||||
{
|
||||
const CMakeConfig newConfig = Utils::transform(items, [](const ConfigModel::DataItem &i) {
|
||||
@@ -548,64 +388,6 @@ void CMakeBuildConfiguration::setWarning(const QString &message)
|
||||
emit warningOccured(m_warning);
|
||||
}
|
||||
|
||||
void CMakeBuildConfiguration::handleParsingSucceeded()
|
||||
{
|
||||
if (!isActive()) {
|
||||
m_buildDirManager.stopParsingAndClearState();
|
||||
return;
|
||||
}
|
||||
|
||||
clearError();
|
||||
|
||||
QString errorMessage;
|
||||
{
|
||||
const QList<CMakeBuildTarget> buildTargets = m_buildDirManager.takeBuildTargets(
|
||||
errorMessage);
|
||||
checkAndReportError(errorMessage);
|
||||
setBuildTargets(buildTargets);
|
||||
}
|
||||
|
||||
{
|
||||
const CMakeConfig cmakeConfig = m_buildDirManager.takeCMakeConfiguration(errorMessage);
|
||||
checkAndReportError(errorMessage);
|
||||
setConfigurationFromCMake(cmakeConfig);
|
||||
}
|
||||
|
||||
{
|
||||
target()->setApplicationTargets(appTargets());
|
||||
target()->setDeploymentData(deploymentData());
|
||||
}
|
||||
|
||||
static_cast<CMakeBuildSystem *>(project()->buildSystem())->handleParsingSuccess(this);
|
||||
}
|
||||
|
||||
void CMakeBuildConfiguration::handleParsingFailed(const QString &msg)
|
||||
{
|
||||
setError(msg);
|
||||
|
||||
QString errorMessage;
|
||||
setConfigurationFromCMake(m_buildDirManager.takeCMakeConfiguration(errorMessage));
|
||||
// ignore errorMessage here, we already got one.
|
||||
|
||||
static_cast<CMakeBuildSystem *>(project()->buildSystem())->handleParsingError(this);
|
||||
}
|
||||
|
||||
std::unique_ptr<CMakeProjectNode> CMakeBuildConfiguration::generateProjectTree(
|
||||
const QList<const FileNode *> &allFiles)
|
||||
{
|
||||
QString errorMessage;
|
||||
auto root = m_buildDirManager.generateProjectTree(allFiles, errorMessage);
|
||||
checkAndReportError(errorMessage);
|
||||
return root;
|
||||
}
|
||||
|
||||
void CMakeBuildConfiguration::checkAndReportError(QString &errorMessage)
|
||||
{
|
||||
if (!errorMessage.isEmpty()) {
|
||||
setError(errorMessage);
|
||||
errorMessage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
QString CMakeBuildConfiguration::error() const
|
||||
{
|
||||
@@ -775,5 +557,10 @@ CMakeProject *CMakeBuildConfiguration::project() const
|
||||
return qobject_cast<CMakeProject *>(BuildConfiguration::project());
|
||||
}
|
||||
|
||||
BuildSystem *CMakeBuildConfiguration::buildSystem() const
|
||||
{
|
||||
return m_buildSystem;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CMakeProjectManager
|
||||
|
||||
Reference in New Issue
Block a user