aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/generator/visualstudio/visualstudiogenerator.cpp
diff options
context:
space:
mode:
authorJake Petroules <jake.petroules@qt.io>2017-05-12 18:50:50 -0700
committerJake Petroules <jake.petroules@qt.io>2017-06-13 15:47:18 +0000
commit8b5e5af31e7c0860f8cf4f8fa50e7b322cadca62 (patch)
tree36a7e2fc38c7571cdf7e4c6b6449754e39cb993c /src/plugins/generator/visualstudio/visualstudiogenerator.cpp
parentd5dc7a2571027168d1a74cbb2578848dc843e20e (diff)
Transform the scanner plugin manager into a true generic plugin manager
...and fit the generator plugins into this new plugin structure. Plugins are now handled entirely by the build system in a generic manner and no part of qbscore (code or build files) has a direct reference to any plugin regardless of whether qbs is being built as shared or static libraries. Change-Id: I4a20546ce275df71083ee22c2cb67f781c4de764 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'src/plugins/generator/visualstudio/visualstudiogenerator.cpp')
-rw-r--r--src/plugins/generator/visualstudio/visualstudiogenerator.cpp365
1 files changed, 365 insertions, 0 deletions
diff --git a/src/plugins/generator/visualstudio/visualstudiogenerator.cpp b/src/plugins/generator/visualstudio/visualstudiogenerator.cpp
new file mode 100644
index 000000000..4b3667140
--- /dev/null
+++ b/src/plugins/generator/visualstudio/visualstudiogenerator.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "msbuildfiltersproject.h"
+#include "msbuildqbsgenerateproject.h"
+#include "msbuildsharedsolutionpropertiesproject.h"
+#include "msbuildsolutionpropertiesproject.h"
+#include "msbuildqbsproductproject.h"
+#include "msbuildutils.h"
+#include "visualstudiogenerator.h"
+#include "visualstudioguidpool.h"
+
+#include "msbuild/msbuildpropertygroup.h"
+#include "msbuild/msbuildproject.h"
+
+#include "solution/visualstudiosolution.h"
+#include "solution/visualstudiosolutionfileproject.h"
+#include "solution/visualstudiosolutionglobalsection.h"
+#include "solution/visualstudiosolutionfolderproject.h"
+
+#include "io/msbuildprojectwriter.h"
+#include "io/visualstudiosolutionwriter.h"
+
+#include <generators/generatableprojectiterator.h>
+#include <logging/translator.h>
+#include <tools/filesaver.h>
+#include <tools/qbsassert.h>
+#include <tools/shellutils.h>
+#include <tools/visualstudioversioninfo.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+
+namespace qbs {
+
+using namespace Internal;
+
+class VisualStudioGeneratorPrivate
+{
+ friend class SolutionDependenciesVisitor;
+public:
+ VisualStudioGeneratorPrivate(const Internal::VisualStudioVersionInfo &versionInfo)
+ : versionInfo(versionInfo) {}
+
+ Internal::VisualStudioVersionInfo versionInfo;
+
+ std::shared_ptr<VisualStudioGuidPool> guidPool;
+ std::shared_ptr<VisualStudioSolution> solution;
+ QString solutionFilePath;
+ QMap<QString, std::shared_ptr<MSBuildProject>> msbuildProjects;
+ QMap<QString, VisualStudioSolutionFileProject *> solutionProjects;
+ QMap<GeneratableProjectData::Id, VisualStudioSolutionFolderProject *> solutionFolders;
+ QList<std::pair<QString, bool>> propertySheetNames;
+
+ void reset();
+};
+
+void VisualStudioGeneratorPrivate::reset()
+{
+ guidPool.reset();
+ solution.reset();
+ solutionFilePath.clear();
+ msbuildProjects.clear();
+ solutionProjects.clear();
+ solutionFolders.clear();
+ propertySheetNames.clear();
+}
+
+class SolutionDependenciesVisitor : public IGeneratableProjectVisitor
+{
+public:
+ SolutionDependenciesVisitor(VisualStudioGenerator *generator)
+ : generator(generator) {
+ }
+
+ void visitProject(const GeneratableProject &project) override {
+ Q_UNUSED(project);
+ nestedProjects = new VisualStudioSolutionGlobalSection(
+ QStringLiteral("NestedProjects"), generator->d->solution.get());
+ generator->d->solution->appendGlobalSection(nestedProjects);
+ }
+
+ void visitProjectData(const GeneratableProject &project,
+ const GeneratableProjectData &parentProjectData,
+ const GeneratableProjectData &projectData) override {
+ Q_UNUSED(project);
+ // The root project will have a null GeneratableProjectData
+ // as its parent object (so skip giving it a parent folder)
+ if (!parentProjectData.name().isEmpty()) {
+ nestedProjects->appendProperty(
+ generator->d->solutionFolders.value(projectData.uniqueName())->guid()
+ .toString(),
+ generator->d->solutionFolders.value(parentProjectData.uniqueName())->guid()
+ .toString());
+ }
+ }
+
+ void visitProduct(const GeneratableProject &project,
+ const GeneratableProjectData &projectData,
+ const GeneratableProductData &productData) override {
+ Q_UNUSED(project);
+ Q_UNUSED(projectData);
+ for (const auto &dep : productData.dependencies()) {
+ generator->d->solution->addDependency(
+ generator->d->solutionProjects.value(productData.name()),
+ generator->d->solutionProjects.value(dep));
+ }
+
+ nestedProjects->appendProperty(
+ generator->d->solutionProjects.value(productData.name())->guid().toString(),
+ generator->d->solutionFolders.value(projectData.uniqueName())->guid()
+ .toString());
+ }
+
+private:
+ VisualStudioGenerator *generator;
+ VisualStudioSolutionGlobalSection *nestedProjects;
+};
+
+VisualStudioGenerator::VisualStudioGenerator(const VisualStudioVersionInfo &versionInfo)
+ : d(new VisualStudioGeneratorPrivate(versionInfo))
+{
+ if (d->versionInfo.usesVcBuild())
+ throw ErrorInfo(Tr::tr("VCBuild (Visual Studio 2008 and below) is not supported"));
+ else if (!d->versionInfo.usesMsBuild())
+ throw ErrorInfo(Tr::tr("Unknown/unsupported build engine"));
+ Q_ASSERT(d->versionInfo.usesSolutions());
+}
+
+VisualStudioGenerator::~VisualStudioGenerator()
+{
+}
+
+QString VisualStudioGenerator::generatorName() const
+{
+ return QStringLiteral("visualstudio%1").arg(d->versionInfo.marketingVersion());
+}
+
+void VisualStudioGenerator::addPropertySheets(const GeneratableProject &project)
+{
+ {
+ const auto fileName = QStringLiteral("qbs.props");
+ d->propertySheetNames.append({ fileName, true });
+ d->msbuildProjects.insert(project.baseBuildDirectory().absoluteFilePath(fileName),
+ std::make_shared<MSBuildSolutionPropertiesProject>(
+ d->versionInfo, project,
+ qbsExecutableFilePath(), qbsSettingsDir()));
+ }
+
+ {
+ const auto fileName = QStringLiteral("qbs-shared.props");
+ d->propertySheetNames.append({ fileName, false });
+ d->msbuildProjects.insert(project.baseBuildDirectory().absoluteFilePath(fileName),
+ std::make_shared<MSBuildSharedSolutionPropertiesProject>(
+ d->versionInfo, project,
+ qbsExecutableFilePath(), qbsSettingsDir()));
+ }
+}
+
+void VisualStudioGenerator::addPropertySheets(
+ const std::shared_ptr<MSBuildTargetProject> &targetProject)
+{
+ for (const auto &pair : d->propertySheetNames) {
+ targetProject->appendPropertySheet(
+ QStringLiteral("$(SolutionDir)\\") + pair.first, pair.second);
+ }
+}
+
+static QString targetFilePath(const QString &baseName, const QString &baseBuildDirectory)
+{
+ return QDir(baseBuildDirectory).absoluteFilePath(baseName + QStringLiteral(".vcxproj"));
+}
+
+static QString targetFilePath(const GeneratableProductData &product,
+ const QString &baseBuildDirectory)
+{
+ return targetFilePath(product.name(), baseBuildDirectory);
+}
+
+static void addDefaultGlobalSections(const GeneratableProject &topLevelProject,
+ VisualStudioSolution *solution)
+{
+ auto configurationPlatformsSection = new VisualStudioSolutionGlobalSection(
+ QStringLiteral("SolutionConfigurationPlatforms"), solution);
+ solution->appendGlobalSection(configurationPlatformsSection);
+ for (const auto &qbsProject : topLevelProject.projects)
+ configurationPlatformsSection->appendProperty(MSBuildUtils::fullName(qbsProject),
+ MSBuildUtils::fullName(qbsProject));
+
+ auto projectConfigurationPlatformsSection = new VisualStudioSolutionGlobalSection(
+ QStringLiteral("ProjectConfigurationPlatforms"), solution);
+ solution->appendGlobalSection(projectConfigurationPlatformsSection);
+ projectConfigurationPlatformsSection->setPost(true);
+ for (const auto project : solution->projects()) {
+ for (const auto &qbsProject : topLevelProject.projects) {
+ projectConfigurationPlatformsSection->appendProperty(
+ QStringLiteral("%1.%2.ActiveCfg")
+ .arg(project->guid().toString())
+ .arg(MSBuildUtils::fullDisplayName(qbsProject)),
+ MSBuildUtils::fullName(qbsProject));
+ projectConfigurationPlatformsSection->appendProperty(
+ QStringLiteral("%1.%2.Build.0")
+ .arg(project->guid().toString())
+ .arg(MSBuildUtils::fullDisplayName(qbsProject)),
+ MSBuildUtils::fullName(qbsProject));
+ }
+ }
+
+ auto solutionPropsSection = new VisualStudioSolutionGlobalSection(
+ QStringLiteral("SolutionProperties"), solution);
+ solution->appendGlobalSection(solutionPropsSection);
+ solutionPropsSection->appendProperty(QStringLiteral("HideSolutionNode"),
+ QStringLiteral("FALSE"));
+}
+
+static void writeProjectFiles(const QMap<QString, std::shared_ptr<MSBuildProject>> &projects)
+{
+ // Write out all the MSBuild project files to disk
+ QMapIterator<QString, std::shared_ptr<MSBuildProject>> it(projects);
+ while (it.hasNext()) {
+ it.next();
+ const auto projectFilePath = it.key();
+ Internal::FileSaver file(projectFilePath);
+ if (!file.open())
+ throw ErrorInfo(Tr::tr("Cannot open %s for writing").arg(projectFilePath));
+
+ std::shared_ptr<MSBuildProject> project = it.value();
+ MSBuildProjectWriter writer(file.device());
+ if (!(writer.write(project.get()) && file.commit()))
+ throw ErrorInfo(Tr::tr("Failed to generate %1").arg(projectFilePath));
+ }
+}
+
+static void writeSolution(const std::shared_ptr<VisualStudioSolution> &solution,
+ const QString &solutionFilePath)
+{
+ Internal::FileSaver file(solutionFilePath);
+ if (!file.open())
+ throw ErrorInfo(Tr::tr("Cannot open %s for writing").arg(solutionFilePath));
+
+ VisualStudioSolutionWriter writer(file.device());
+ writer.setProjectBaseDirectory(QFileInfo(solutionFilePath).path());
+ if (!(writer.write(solution.get()) && file.commit()))
+ throw ErrorInfo(Tr::tr("Failed to generate %1").arg(solutionFilePath));
+
+ qDebug() << "Generated" << qPrintable(QFileInfo(solutionFilePath).fileName());
+}
+
+void VisualStudioGenerator::generate()
+{
+ GeneratableProjectIterator it(project());
+ it.accept(this);
+
+ addDefaultGlobalSections(project(), d->solution.get());
+
+ // Second pass: connection solution project interdependencies and project nesting hierarchy
+ SolutionDependenciesVisitor solutionDependenciesVisitor(this);
+ it.accept(&solutionDependenciesVisitor);
+
+ writeProjectFiles(d->msbuildProjects);
+ writeSolution(d->solution, d->solutionFilePath);
+
+ d->reset();
+}
+
+void VisualStudioGenerator::visitProject(const GeneratableProject &project)
+{
+ addPropertySheets(project);
+
+ const auto buildDir = project.baseBuildDirectory();
+
+ d->guidPool = std::make_shared<VisualStudioGuidPool>(
+ buildDir.absoluteFilePath(project.name() + QStringLiteral(".guid.txt")));
+
+ d->solutionFilePath = buildDir.absoluteFilePath(project.name() + QStringLiteral(".sln"));
+ d->solution = std::make_shared<VisualStudioSolution>(d->versionInfo);
+
+ // Create a helper project to re-run qbs generate
+ const auto qbsGenerate = QStringLiteral("qbs-generate");
+ const auto projectFilePath = targetFilePath(qbsGenerate, buildDir.absolutePath());
+ const auto relativeProjectFilePath = QFileInfo(d->solutionFilePath).dir()
+ .relativeFilePath(projectFilePath);
+ auto targetProject = std::make_shared<MSBuildQbsGenerateProject>(project, d->versionInfo);
+ targetProject->setGuid(d->guidPool->drawProductGuid(relativeProjectFilePath));
+ d->msbuildProjects.insert(projectFilePath, targetProject);
+
+ addPropertySheets(targetProject);
+
+ auto solutionProject = new VisualStudioSolutionFileProject(
+ targetFilePath(qbsGenerate, project.baseBuildDirectory().absolutePath()),
+ d->solution.get());
+ solutionProject->setGuid(targetProject->guid());
+ d->solution->appendProject(solutionProject);
+ d->solutionProjects.insert(qbsGenerate, solutionProject);
+}
+
+void VisualStudioGenerator::visitProjectData(const GeneratableProject &project,
+ const GeneratableProjectData &projectData)
+{
+ Q_UNUSED(project);
+ auto solutionFolder = new VisualStudioSolutionFolderProject(d->solution.get());
+ solutionFolder->setName(projectData.name());
+ d->solution->appendProject(solutionFolder);
+ QBS_CHECK(!d->solutionFolders.contains(projectData.uniqueName()));
+ d->solutionFolders.insert(projectData.uniqueName(), solutionFolder);
+}
+
+void VisualStudioGenerator::visitProduct(const GeneratableProject &project,
+ const GeneratableProjectData &projectData,
+ const GeneratableProductData &productData)
+{
+ Q_UNUSED(projectData);
+ const auto projectFilePath = targetFilePath(productData,
+ project.baseBuildDirectory().absolutePath());
+ const auto relativeProjectFilePath = QFileInfo(d->solutionFilePath)
+ .dir().relativeFilePath(projectFilePath);
+ auto targetProject = std::make_shared<MSBuildQbsProductProject>(project, productData,
+ d->versionInfo);
+ targetProject->setGuid(d->guidPool->drawProductGuid(relativeProjectFilePath));
+
+ addPropertySheets(targetProject);
+
+ d->msbuildProjects.insert(projectFilePath, targetProject);
+ d->msbuildProjects.insert(projectFilePath + QStringLiteral(".filters"),
+ std::make_shared<MSBuildFiltersProject>(productData));
+
+ auto solutionProject = new VisualStudioSolutionFileProject(
+ targetFilePath(productData, project.baseBuildDirectory().absolutePath()),
+ d->solution.get());
+ solutionProject->setGuid(targetProject->guid());
+ d->solution->appendProject(solutionProject);
+ d->solutionProjects.insert(productData.name(), solutionProject);
+}
+
+} // namespace qbs