aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2013-06-06 10:06:30 +0200
committerJoerg Bornemann <joerg.bornemann@digia.com>2013-06-17 14:23:19 +0200
commitab60f6e4c489ace089785c002d75018ac6a31e30 (patch)
tree91053ec752d9aa56df8061ba5e62c13431804a6b /src
parentd3877c67d060d454ac66fde7b6ad41a2bb50d808 (diff)
Allow projects to be nested.
This is nice to have for grouping products and a requirement for aggregating existing projects into a bigger one. Change-Id: I3394642e95ea57dbc6bf1603cfed6902a5906e4c Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/app/qbs/commandlinefrontend.cpp2
-rw-r--r--src/app/qbs/status.cpp2
-rw-r--r--src/lib/api/internaljobs.cpp23
-rw-r--r--src/lib/api/internaljobs.h20
-rw-r--r--src/lib/api/jobs.cpp9
-rw-r--r--src/lib/api/jobs.h8
-rw-r--r--src/lib/api/project.cpp63
-rw-r--r--src/lib/api/project.h2
-rw-r--r--src/lib/api/projectdata.cpp51
-rw-r--r--src/lib/api/projectdata.h5
-rw-r--r--src/lib/api/projectdata_p.h3
-rw-r--r--src/lib/buildgraph/artifact.cpp5
-rw-r--r--src/lib/buildgraph/artifact.h1
-rw-r--r--src/lib/buildgraph/artifactcleaner.cpp4
-rw-r--r--src/lib/buildgraph/artifactcleaner.h2
-rw-r--r--src/lib/buildgraph/artifactvisitor.cpp2
-rw-r--r--src/lib/buildgraph/automoc.cpp6
-rw-r--r--src/lib/buildgraph/buildgraph.cpp167
-rw-r--r--src/lib/buildgraph/buildgraph.h6
-rw-r--r--src/lib/buildgraph/cycledetector.cpp2
-rw-r--r--src/lib/buildgraph/executor.cpp6
-rw-r--r--src/lib/buildgraph/executor.h4
-rw-r--r--src/lib/buildgraph/inputartifactscanner.cpp8
-rw-r--r--src/lib/buildgraph/productinstaller.cpp20
-rw-r--r--src/lib/buildgraph/productinstaller.h4
-rw-r--r--src/lib/buildgraph/projectbuilddata.cpp17
-rw-r--r--src/lib/buildgraph/projectbuilddata.h10
-rw-r--r--src/lib/buildgraph/rulesapplicator.cpp11
-rw-r--r--src/lib/buildgraph/timestampsupdater.cpp2
-rw-r--r--src/lib/buildgraph/timestampsupdater.h2
-rw-r--r--src/lib/language/builtindeclarations.cpp14
-rw-r--r--src/lib/language/builtindeclarations.h1
-rw-r--r--src/lib/language/evaluator.cpp53
-rw-r--r--src/lib/language/evaluator.h9
-rw-r--r--src/lib/language/forward_decls.h4
-rw-r--r--src/lib/language/item.h25
-rw-r--r--src/lib/language/itemreaderastvisitor.cpp4
-rw-r--r--src/lib/language/language.cpp176
-rw-r--r--src/lib/language/language.h47
-rw-r--r--src/lib/language/loader.cpp4
-rw-r--r--src/lib/language/loader.h2
-rw-r--r--src/lib/language/moduleloader.cpp122
-rw-r--r--src/lib/language/moduleloader.h2
-rw-r--r--src/lib/language/projectresolver.cpp308
-rw-r--r--src/lib/language/projectresolver.h45
-rw-r--r--src/lib/language/tst_language.cpp14
-rw-r--r--src/lib/language/tst_language.h2
-rw-r--r--src/lib/tools/persistence.cpp2
-rw-r--r--src/lib/tools/weakpointer.h2
49 files changed, 874 insertions, 429 deletions
diff --git a/src/app/qbs/commandlinefrontend.cpp b/src/app/qbs/commandlinefrontend.cpp
index eab67ac44..f2175ab98 100644
--- a/src/app/qbs/commandlinefrontend.cpp
+++ b/src/app/qbs/commandlinefrontend.cpp
@@ -265,7 +265,7 @@ CommandLineFrontend::ProductMap CommandLineFrontend::productsToUse() const
foreach (const Project &project, m_projects) {
QList<ProductData> &productList = products[project];
const ProjectData projectData = project.projectData();
- foreach (const ProductData &product, projectData.products()) {
+ foreach (const ProductData &product, projectData.allProducts()) {
if (useAll || m_parser.products().contains(product.name())) {
productList << product;
productNames << product.name();
diff --git a/src/app/qbs/status.cpp b/src/app/qbs/status.cpp
index c09190f5e..303ea485c 100644
--- a/src/app/qbs/status.cpp
+++ b/src/app/qbs/status.cpp
@@ -118,7 +118,7 @@ int printStatus(const ProjectData &project)
QStringList untrackedFilesInProject = allFilesInProject(projectDirectory);
QStringList missingFiles;
- foreach (const ProductData &product, project.products()) {
+ foreach (const ProductData &product, project.allProducts()) {
qbsInfo() << "\nProduct: " << product.name()
<< " (" << product.location().fileName() << ":"
<< product.location().line() << ")";
diff --git a/src/lib/api/internaljobs.cpp b/src/lib/api/internaljobs.cpp
index 946a87fa6..2f67243d3 100644
--- a/src/lib/api/internaljobs.cpp
+++ b/src/lib/api/internaljobs.cpp
@@ -119,7 +119,7 @@ void InternalJob::cancel()
m_observer->cancel();
}
-void InternalJob::storeBuildGraph(const ResolvedProjectConstPtr &project)
+void InternalJob::storeBuildGraph(const TopLevelProjectConstPtr &project)
{
try {
project->store(logger());
@@ -154,7 +154,7 @@ void InternalSetupProjectJob::reportError(const Error &error)
Q_ARG(Internal::InternalJob *, this));
}
-ResolvedProjectPtr InternalSetupProjectJob::project() const
+TopLevelProjectPtr InternalSetupProjectJob::project() const
{
return m_project;
}
@@ -201,10 +201,6 @@ void InternalSetupProjectJob::execute()
loader.setProgressObserver(observer());
m_project = loader.loadProject(m_parameters);
}
- if (m_project->products.isEmpty()) {
- throw Error(Tr::tr("Project '%1' does not contain products.")
- .arg(m_parameters.projectFilePath()));
- }
}
// copy the environment from the platform config into the project's config
@@ -216,7 +212,7 @@ void InternalSetupProjectJob::execute()
foreach (const ResolvedProductConstPtr &p, m_project->products) {
logger().qbsDebug() << QString::fromLocal8Bit(" - [%1] %2 as %3")
.arg(p->fileTags.toStringList().join(QLatin1String(", ")))
- .arg(p->name).arg(p->project->id());
+ .arg(p->name).arg(p->topLevelProject()->id());
}
logger().qbsDebug() << '\n';
@@ -244,7 +240,7 @@ BuildGraphTouchingJob::~BuildGraphTouchingJob()
{
}
-void BuildGraphTouchingJob::setup(const ResolvedProjectPtr &project,
+void BuildGraphTouchingJob::setup(const TopLevelProjectPtr &project,
const QList<ResolvedProductPtr> &products, bool dryRun)
{
m_project = project;
@@ -263,7 +259,7 @@ InternalBuildJob::InternalBuildJob(const Logger &logger, QObject *parent)
{
}
-void InternalBuildJob::build(const ResolvedProjectPtr &project,
+void InternalBuildJob::build(const TopLevelProjectPtr &project,
const QList<ResolvedProductPtr> &products, const BuildOptions &buildOptions)
{
setup(project, products, buildOptions.dryRun());
@@ -308,7 +304,7 @@ InternalCleanJob::InternalCleanJob(const Logger &logger, QObject *parent)
{
}
-void InternalCleanJob::clean(const ResolvedProjectPtr &project,
+void InternalCleanJob::clean(const TopLevelProjectPtr &project,
const QList<ResolvedProductPtr> &products, const CleanOptions &options)
{
setup(project, products, options.dryRun());
@@ -350,9 +346,10 @@ InternalInstallJob::~InternalInstallJob()
{
}
-void InternalInstallJob::install(const QList<ResolvedProductPtr> &products,
- const InstallOptions &options)
+void InternalInstallJob::install(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const InstallOptions &options)
{
+ m_project = project;
m_products = products;
m_options = options;
setTimed(options.logElapsedTime());
@@ -374,7 +371,7 @@ void InternalInstallJob::start()
void InternalInstallJob::doInstall()
{
try {
- ProductInstaller(m_products, m_options, observer(), logger()).install();
+ ProductInstaller(m_project, m_products, m_options, observer(), logger()).install();
} catch (const Error &error) {
setError(error);
}
diff --git a/src/lib/api/internaljobs.h b/src/lib/api/internaljobs.h
index b94403fcb..37b2f607d 100644
--- a/src/lib/api/internaljobs.h
+++ b/src/lib/api/internaljobs.h
@@ -68,7 +68,7 @@ protected:
JobObserver *observer() const { return m_observer; }
void setError(const Error &error) { m_error = error; }
void setTimed(bool timed) { m_timed = timed; }
- void storeBuildGraph(const ResolvedProjectConstPtr &project);
+ void storeBuildGraph(const TopLevelProjectConstPtr &project);
signals:
void finished(Internal::InternalJob *job);
@@ -94,7 +94,7 @@ public:
void resolve(const SetupProjectParameters &parameters);
void reportError(const Error &error);
- ResolvedProjectPtr project() const;
+ TopLevelProjectPtr project() const;
private slots:
void start();
@@ -108,7 +108,7 @@ private:
QMutex m_runMutex;
QWaitCondition m_runWaitCondition;
- ResolvedProjectPtr m_project;
+ TopLevelProjectPtr m_project;
SetupProjectParameters m_parameters;
};
@@ -118,7 +118,7 @@ class BuildGraphTouchingJob : public InternalJob
Q_OBJECT
public:
const QList<ResolvedProductPtr> &products() const { return m_products; }
- const ResolvedProjectPtr &project() const { return m_project; }
+ const TopLevelProjectPtr &project() const { return m_project; }
signals:
void reportCommandDescription(const QString &highlight, const QString &message);
@@ -128,12 +128,12 @@ protected:
BuildGraphTouchingJob(const Logger &logger, QObject *parent = 0);
~BuildGraphTouchingJob();
- void setup(const ResolvedProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ void setup(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
bool dryRun);
void storeBuildGraph();
private:
- ResolvedProjectPtr m_project;
+ TopLevelProjectPtr m_project;
QList<ResolvedProductPtr> m_products;
bool m_dryRun;
};
@@ -145,7 +145,7 @@ class InternalBuildJob : public BuildGraphTouchingJob
public:
InternalBuildJob(const Logger &logger, QObject *parent = 0);
- void build(const ResolvedProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ void build(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
const BuildOptions &buildOptions);
private slots:
@@ -163,7 +163,7 @@ class InternalCleanJob : public BuildGraphTouchingJob
public:
InternalCleanJob(const Logger &logger, QObject *parent = 0);
- void clean(const ResolvedProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ void clean(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
const CleanOptions &options);
private slots:
@@ -185,7 +185,8 @@ public:
InternalInstallJob(const Logger &logger, QObject *parent = 0);
~InternalInstallJob();
- void install(const QList<ResolvedProductPtr> &products, const InstallOptions &options);
+ void install(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const InstallOptions &options);
private slots:
void handleFinished();
@@ -195,6 +196,7 @@ private:
void doInstall();
private:
+ TopLevelProjectPtr m_project;
QList<ResolvedProductPtr> m_products;
InstallOptions m_options;
};
diff --git a/src/lib/api/jobs.cpp b/src/lib/api/jobs.cpp
index a344cc5c0..47bda7191 100644
--- a/src/lib/api/jobs.cpp
+++ b/src/lib/api/jobs.cpp
@@ -242,7 +242,7 @@ BuildJob::BuildJob(const Logger &logger, QObject *parent)
this, SIGNAL(reportProcessResult(qbs::ProcessResult)));
}
-void BuildJob::build(const ResolvedProjectPtr &project, const QList<ResolvedProductPtr> &products,
+void BuildJob::build(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
const BuildOptions &options)
{
qobject_cast<InternalBuildJob *>(internalJob())->build(project, products, options);
@@ -259,7 +259,7 @@ CleanJob::CleanJob(const Logger &logger, QObject *parent)
{
}
-void CleanJob::clean(const ResolvedProjectPtr &project, const QList<ResolvedProductPtr> &products,
+void CleanJob::clean(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
const qbs::CleanOptions &options)
{
qobject_cast<InternalCleanJob *>(internalJob())->clean(project, products, options);
@@ -275,9 +275,10 @@ InstallJob::InstallJob(const Logger &logger, QObject *parent)
{
}
-void InstallJob::install(const QList<ResolvedProductPtr> &products, const InstallOptions &options)
+void InstallJob::install(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const InstallOptions &options)
{
- qobject_cast<InternalInstallJob *>(internalJob())->install(products, options);
+ qobject_cast<InternalInstallJob *>(internalJob())->install(project, products, options);
}
} // namespace qbs
diff --git a/src/lib/api/jobs.h b/src/lib/api/jobs.h
index 19d4f8dcb..791ad2af7 100644
--- a/src/lib/api/jobs.h
+++ b/src/lib/api/jobs.h
@@ -113,7 +113,7 @@ signals:
private:
BuildJob(const Internal::Logger &logger, QObject *parent);
- void build(const Internal::ResolvedProjectPtr &project,
+ void build(const Internal::TopLevelProjectPtr &project,
const QList<qbs::Internal::ResolvedProductPtr> &products,
const BuildOptions &options);
};
@@ -127,7 +127,7 @@ class QBS_EXPORT CleanJob : public AbstractJob
private:
CleanJob(const Internal::Logger &logger, QObject *parent);
- void clean(const Internal::ResolvedProjectPtr &project,
+ void clean(const Internal::TopLevelProjectPtr &project,
const QList<Internal::ResolvedProductPtr> &products, const CleanOptions &options);
};
@@ -138,8 +138,8 @@ class QBS_EXPORT InstallJob : public AbstractJob
private:
InstallJob(const Internal::Logger &logger, QObject *parent);
- void install(const QList<Internal::ResolvedProductPtr> &products,
- const InstallOptions &options);
+ void install(const Internal::TopLevelProjectPtr &project,
+ const QList<Internal::ResolvedProductPtr> &products, const InstallOptions &options);
};
} // namespace qbs
diff --git a/src/lib/api/project.cpp b/src/lib/api/project.cpp
index f9744d648..dca2104d8 100644
--- a/src/lib/api/project.cpp
+++ b/src/lib/api/project.cpp
@@ -91,7 +91,7 @@ static void loadPlugins(const QStringList &_pluginPaths, const Logger &logger)
class ProjectPrivate : public QSharedData
{
public:
- ProjectPrivate(const ResolvedProjectPtr &internalProject, const Logger &logger)
+ ProjectPrivate(const TopLevelProjectPtr &internalProject, const Logger &logger)
: internalProject(internalProject), logger(logger), m_projectDataRetrieved(false)
{
}
@@ -109,11 +109,12 @@ public:
QList<ResolvedProductPtr> allEnabledInternalProducts() const;
ResolvedProductPtr internalProduct(const ProductData &product) const;
- const ResolvedProjectPtr internalProject;
+ const TopLevelProjectPtr internalProject;
Logger logger;
private:
- void retrieveProjectData();
+ void retrieveProjectData(ProjectData &projectData,
+ const ResolvedProjectConstPtr &internalProject);
ProjectData m_projectData;
bool m_projectDataRetrieved;
@@ -121,8 +122,11 @@ private:
ProjectData ProjectPrivate::projectData()
{
- if (!m_projectDataRetrieved)
- retrieveProjectData();
+ if (!m_projectDataRetrieved) {
+ retrieveProjectData(m_projectData, internalProject);
+ m_projectData.d->buildDir = internalProject->buildDirectory;
+ m_projectDataRetrieved = true;
+ }
return m_projectData;
}
@@ -165,7 +169,7 @@ InstallJob *ProjectPrivate::installProducts(const QList<ResolvedProductPtr> &pro
if (needsDepencencyResolving)
addDependencies(productsToInstall);
InstallJob * const job = new InstallJob(logger, jobOwner);
- job->install(productsToInstall, options);
+ job->install(internalProject, productsToInstall, options);
return job;
}
@@ -179,30 +183,49 @@ QList<ResolvedProductPtr> ProjectPrivate::internalProducts(const QList<ProductDa
return internalProducts;
}
-QList<ResolvedProductPtr> ProjectPrivate::allEnabledInternalProducts() const
+static QList<ResolvedProductPtr> enabledInternalProducts(const ResolvedProjectConstPtr &project)
{
QList<ResolvedProductPtr> products;
- foreach (const ResolvedProductPtr &p, internalProject->products) {
+ foreach (const ResolvedProductPtr &p, project->products) {
if (p->enabled)
products << p;
}
+ foreach (const ResolvedProjectConstPtr &subProject, project->subProjects)
+ products << enabledInternalProducts(subProject);
return products;
}
-ResolvedProductPtr ProjectPrivate::internalProduct(const ProductData &product) const
+QList<ResolvedProductPtr> ProjectPrivate::allEnabledInternalProducts() const
+{
+ return enabledInternalProducts(internalProject);
+}
+
+static ResolvedProductPtr internalProductForProject(const ResolvedProjectConstPtr &project,
+ const ProductData &product)
{
- foreach (const ResolvedProductPtr &resolvedProduct, internalProject->products) {
+ foreach (const ResolvedProductPtr &resolvedProduct, project->products) {
if (product.name() == resolvedProduct->name)
return resolvedProduct;
}
- qFatal("No build product '%s'", qPrintable(product.name()));
+ foreach (const ResolvedProjectConstPtr &subProject, project->subProjects) {
+ const ResolvedProductPtr &p = internalProductForProject(subProject, product);
+ if (p)
+ return p;
+ }
return ResolvedProductPtr();
}
-void ProjectPrivate::retrieveProjectData()
+ResolvedProductPtr ProjectPrivate::internalProduct(const ProductData &product) const
+{
+ return internalProductForProject(internalProject, product);
+}
+
+void ProjectPrivate::retrieveProjectData(ProjectData &projectData,
+ const ResolvedProjectConstPtr &internalProject)
{
- m_projectData.d->location = internalProject->location;
- m_projectData.d->buildDir = internalProject->buildDirectory;
+ projectData.d->name = internalProject->name;
+ projectData.d->location = internalProject->location;
+ projectData.d->enabled = internalProject->enabled;
foreach (const ResolvedProductConstPtr &resolvedProduct, internalProject->products) {
ProductData product;
product.d->name = resolvedProduct->name;
@@ -227,9 +250,15 @@ void ProjectPrivate::retrieveProjectData()
product.d->groups << group;
}
qSort(product.d->groups);
- m_projectData.d->products << product;
+ projectData.d->products << product;
+ }
+ foreach (const ResolvedProjectConstPtr &internalSubProject, internalProject->subProjects) {
+ ProjectData subProject;
+ retrieveProjectData(subProject, internalSubProject);
+ projectData.d->subProjects << subProject;
}
- qSort(m_projectData.d->products);
+ qSort(projectData.d->products);
+ qSort(projectData.d->subProjects);
m_projectDataRetrieved = true;
}
@@ -242,7 +271,7 @@ using namespace Internal;
* \brief The \c Project class provides services related to a qbs project.
*/
-Project::Project(const ResolvedProjectPtr &internalProject, const Logger &logger)
+Project::Project(const TopLevelProjectPtr &internalProject, const Logger &logger)
: d(new ProjectPrivate(internalProject, logger))
{
}
diff --git a/src/lib/api/project.h b/src/lib/api/project.h
index 099b82d9a..48c3fd71b 100644
--- a/src/lib/api/project.h
+++ b/src/lib/api/project.h
@@ -107,7 +107,7 @@ public:
private:
Project();
- Project(const Internal::ResolvedProjectPtr &internalProject, const Internal::Logger &logger);
+ Project(const Internal::TopLevelProjectPtr &internalProject, const Internal::Logger &logger);
QExplicitlySharedDataPointer<Internal::ProjectPrivate> d;
};
diff --git a/src/lib/api/projectdata.cpp b/src/lib/api/projectdata.cpp
index 7b46f1829..9d48f5f2f 100644
--- a/src/lib/api/projectdata.cpp
+++ b/src/lib/api/projectdata.cpp
@@ -130,6 +130,11 @@ QStringList GroupData::allFilePaths() const
return d->filePaths + d->expandedWildcards;
}
+bool operator!=(const GroupData &lhs, const GroupData &rhs)
+{
+ return !(lhs == rhs);
+}
+
bool operator==(const GroupData &lhs, const GroupData &rhs)
{
return lhs.name() == rhs.name()
@@ -140,9 +145,9 @@ bool operator==(const GroupData &lhs, const GroupData &rhs)
&& lhs.isEnabled() == rhs.isEnabled();
}
-bool operator!=(const GroupData &lhs, const GroupData &rhs)
+bool operator<(const GroupData &lhs, const GroupData &rhs)
{
- return !(lhs == rhs);
+ return lhs.name() < rhs.name();
}
/*!
@@ -237,7 +242,7 @@ bool operator!=(const ProductData &lhs, const ProductData &rhs)
return !(lhs == rhs);
}
-bool operator<(const GroupData &lhs, const GroupData &rhs)
+bool operator<(const ProductData &lhs, const ProductData &rhs)
{
return lhs.name() < rhs.name();
}
@@ -271,6 +276,14 @@ ProjectData::~ProjectData()
}
/*!
+ * \brief The name of this project.
+ */
+QString ProjectData::name() const
+{
+ return d->name;
+}
+
+/*!
* \brief The location at which the project is defined in a qbs source file.
*/
CodeLocation ProjectData::location() const
@@ -279,7 +292,17 @@ CodeLocation ProjectData::location() const
}
/*!
+ * \brief Whether the project is enabled.
+ * \note Disabled projects never have any products or sub-projects.
+ */
+bool ProjectData::isEnabled() const
+{
+ return d->enabled;
+}
+
+/*!
* \brief The base directory under which the build artifacts of this project will be created.
+ * This is only valid for the top-level project.
*/
QString ProjectData::buildDirectory() const
{
@@ -295,9 +318,29 @@ QList<ProductData> ProjectData::products() const
return d->products;
}
+/*!
+ * The sub-projects of this project.
+ */
+QList<ProjectData> ProjectData::subProjects() const
+{
+ return d->subProjects;
+}
+
+/*!
+ * All products in this projects and its direct and indirect sub-projects.
+ */
+QList<ProductData> ProjectData::allProducts() const
+{
+ QList<ProductData> productList = products();
+ foreach (const ProjectData &pd, subProjects())
+ productList << pd.allProducts();
+ return productList;
+}
+
bool operator==(const ProjectData &lhs, const ProjectData &rhs)
{
return lhs.location() == rhs.location()
+ && lhs.subProjects() == rhs.subProjects()
&& lhs.products() == rhs.products();
}
@@ -306,7 +349,7 @@ bool operator!=(const ProjectData &lhs, const ProjectData &rhs)
return !(lhs == rhs);
}
-bool operator<(const ProductData &lhs, const ProductData &rhs)
+bool operator<(const ProjectData &lhs, const ProjectData &rhs)
{
return lhs.name() < rhs.name();
}
diff --git a/src/lib/api/projectdata.h b/src/lib/api/projectdata.h
index 1097c5e24..d7ae66b8f 100644
--- a/src/lib/api/projectdata.h
+++ b/src/lib/api/projectdata.h
@@ -139,9 +139,13 @@ public:
ProjectData &operator=(const ProjectData &other);
~ProjectData();
+ QString name() const;
CodeLocation location() const;
+ bool isEnabled() const;
QString buildDirectory() const;
QList<ProductData> products() const;
+ QList<ProjectData> subProjects() const;
+ QList<ProductData> allProducts() const;
private:
QExplicitlySharedDataPointer<Internal::ProjectDataPrivate> d;
@@ -149,6 +153,7 @@ private:
QBS_EXPORT bool operator==(const ProjectData &lhs, const ProjectData &rhs);
QBS_EXPORT bool operator!=(const ProjectData &lhs, const ProjectData &rhs);
+QBS_EXPORT bool operator<(const ProjectData &lhs, const ProjectData &rhs);
} // namespace qbs
diff --git a/src/lib/api/projectdata_p.h b/src/lib/api/projectdata_p.h
index c544dd9d9..14ed79efd 100644
--- a/src/lib/api/projectdata_p.h
+++ b/src/lib/api/projectdata_p.h
@@ -61,8 +61,11 @@ public:
class ProjectDataPrivate : public QSharedData
{
public:
+ QString name;
CodeLocation location;
+ bool enabled;
QList<ProductData> products;
+ QList<ProjectData> subProjects;
QString buildDir;
};
diff --git a/src/lib/buildgraph/artifact.cpp b/src/lib/buildgraph/artifact.cpp
index e2de97d75..60f62f768 100644
--- a/src/lib/buildgraph/artifact.cpp
+++ b/src/lib/buildgraph/artifact.cpp
@@ -143,5 +143,10 @@ void Artifact::disconnectAll(const Logger &logger)
disconnectParents(logger);
}
+TopLevelProject *Artifact::topLevelProject() const
+{
+ return project->topLevelProject();
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/buildgraph/artifact.h b/src/lib/buildgraph/artifact.h
index 03e6a9071..88d9b677f 100644
--- a/src/lib/buildgraph/artifact.h
+++ b/src/lib/buildgraph/artifact.h
@@ -102,6 +102,7 @@ public:
QString dirPath() const { return m_dirPath.toString(); }
QString fileName() const { return m_fileName.toString(); }
void disconnectAll(const Logger &logger);
+ TopLevelProject *topLevelProject() const;
private:
void load(PersistentPool &pool);
diff --git a/src/lib/buildgraph/artifactcleaner.cpp b/src/lib/buildgraph/artifactcleaner.cpp
index a7695b3fb..5fc5deace 100644
--- a/src/lib/buildgraph/artifactcleaner.cpp
+++ b/src/lib/buildgraph/artifactcleaner.cpp
@@ -61,7 +61,7 @@ static void invalidateArtifactTimestamp(Artifact *artifact)
{
if (artifact->timestamp.isValid()) {
artifact->timestamp.clear();
- artifact->project->buildData->isDirty = true;
+ artifact->topLevelProject()->buildData->isDirty = true;
}
}
@@ -134,7 +134,7 @@ ArtifactCleaner::ArtifactCleaner(const Logger &logger, ProgressObserver *observe
{
}
-void ArtifactCleaner::cleanup(const ResolvedProjectPtr &project,
+void ArtifactCleaner::cleanup(const TopLevelProjectPtr &project,
const QList<ResolvedProductPtr> &products, const CleanOptions &options)
{
m_hasError = false;
diff --git a/src/lib/buildgraph/artifactcleaner.h b/src/lib/buildgraph/artifactcleaner.h
index 5de78701c..516effb95 100644
--- a/src/lib/buildgraph/artifactcleaner.h
+++ b/src/lib/buildgraph/artifactcleaner.h
@@ -44,7 +44,7 @@ class ArtifactCleaner
{
public:
ArtifactCleaner(const Logger &logger, ProgressObserver *observer);
- void cleanup(const ResolvedProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ void cleanup(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
const CleanOptions &options);
private:
diff --git a/src/lib/buildgraph/artifactvisitor.cpp b/src/lib/buildgraph/artifactvisitor.cpp
index 7746ded85..24829c51e 100644
--- a/src/lib/buildgraph/artifactvisitor.cpp
+++ b/src/lib/buildgraph/artifactvisitor.cpp
@@ -66,6 +66,8 @@ void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project)
{
foreach (const ResolvedProductConstPtr &product, project->products)
visitProduct(product);
+ foreach (const ResolvedProjectConstPtr &subProject, project->subProjects)
+ visitProject(subProject);
}
} // namespace Internal
diff --git a/src/lib/buildgraph/automoc.cpp b/src/lib/buildgraph/automoc.cpp
index bf97caf50..1ff61c993 100644
--- a/src/lib/buildgraph/automoc.cpp
+++ b/src/lib/buildgraph/automoc.cpp
@@ -173,7 +173,7 @@ void AutoMoc::apply(const ResolvedProductPtr &product)
loggedConnect(outputOfHeader, pluginMetaDataFile, m_logger);
}
- product->project->buildData->updateNodesThatMustGetNewTransformer(m_logger);
+ product->topLevelProject()->buildData->updateNodesThatMustGetNewTransformer(m_logger);
}
QString AutoMoc::generateMocFileName(Artifact *artifact, FileType fileType)
@@ -344,7 +344,7 @@ void AutoMoc::unmoc(Artifact *artifact, const FileTag &mocFileTag)
m_logger.qbsTrace() << "[AUTOMOC] removing moc obj artifact "
<< relativeArtifactFileName(mocObjArtifact);
}
- artifact->project->buildData->removeArtifact(mocObjArtifact, m_logger);
+ artifact->topLevelProject()->buildData->removeArtifact(mocObjArtifact, m_logger);
}
}
@@ -352,7 +352,7 @@ void AutoMoc::unmoc(Artifact *artifact, const FileTag &mocFileTag)
m_logger.qbsTrace() << "[AUTOMOC] removing generated artifact "
<< relativeArtifactFileName(generatedMocArtifact);
}
- artifact->project->buildData->removeArtifact(generatedMocArtifact, m_logger);
+ artifact->topLevelProject()->buildData->removeArtifact(generatedMocArtifact, m_logger);
delete generatedMocArtifact;
}
diff --git a/src/lib/buildgraph/buildgraph.cpp b/src/lib/buildgraph/buildgraph.cpp
index 91da59941..c9e8c0af4 100644
--- a/src/lib/buildgraph/buildgraph.cpp
+++ b/src/lib/buildgraph/buildgraph.cpp
@@ -255,7 +255,7 @@ void connect(Artifact *p, Artifact *c)
QBS_CHECK(p != c);
p->children.insert(c);
c->parents.insert(p);
- p->project->buildData->isDirty = true;
+ p->topLevelProject()->buildData->isDirty = true;
}
void loggedConnect(Artifact *u, Artifact *v, const Logger &logger)
@@ -327,7 +327,7 @@ void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger)
QString relativeArtifactFileName(const Artifact *n)
{
- const QString &buildDir = n->project->buildDirectory;
+ const QString &buildDir = n->topLevelProject()->buildDirectory;
QString str = n->filePath();
if (str.startsWith(buildDir))
str.remove(0, buildDir.count());
@@ -340,7 +340,7 @@ Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &
const QString &fileName)
{
QList<Artifact *> artifacts
- = product->project->buildData->lookupArtifacts(dirPath, fileName);
+ = product->topLevelProject()->buildData->lookupArtifacts(dirPath, fileName);
for (QList<Artifact *>::const_iterator it = artifacts.constBegin();
it != artifacts.constEnd(); ++it) {
if ((*it)->product == product)
@@ -396,8 +396,8 @@ void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const
#endif
product->buildData->artifacts.insert(artifact);
artifact->product = product;
- product->project->buildData->insertIntoArtifactLookupTable(artifact);
- product->project->buildData->isDirty = true;
+ product->topLevelProject()->buildData->insertIntoArtifactLookupTable(artifact);
+ product->topLevelProject()->buildData->isDirty = true;
if (logger.traceEnabled()) {
logger.qbsTrace() << QString::fromLocal8Bit("[BG] insert artifact '%1'")
@@ -459,15 +459,33 @@ static bool isConfigCompatible(const QVariantMap &userCfg, const QVariantMap &pr
return true;
}
+void restoreBackPointers(const ResolvedProjectPtr &project)
+{
+ foreach (const ResolvedProductPtr &product, project->products) {
+ product->project = project;
+ if (!product->buildData)
+ continue;
+ foreach (Artifact * const a, product->buildData->artifacts) {
+ a->project = project;
+ project->topLevelProject()->buildData->insertIntoArtifactLookupTable(a);
+ }
+ }
+
+ foreach (const ResolvedProjectPtr &subProject, project->subProjects) {
+ subProject->parentProject = project;
+ restoreBackPointers(subProject);
+ }
+}
+
BuildGraphLoader::LoadResult BuildGraphLoader::load(const SetupProjectParameters &parameters,
const RulesEvaluationContextPtr &evalContext)
{
m_result = LoadResult();
m_evalContext = evalContext;
- const QString projectId = ResolvedProject::deriveId(parameters.buildConfiguration());
+ const QString projectId = TopLevelProject::deriveId(parameters.buildConfiguration());
const QString buildDir
- = ResolvedProject::deriveBuildDirectory(parameters.buildRoot(), projectId);
+ = TopLevelProject::deriveBuildDirectory(parameters.buildRoot(), projectId);
const QString buildGraphFilePath
= ProjectBuildData::deriveBuildGraphFilePath(buildDir, projectId);
@@ -481,23 +499,13 @@ BuildGraphLoader::LoadResult BuildGraphLoader::load(const SetupProjectParameters
return m_result;
}
- const ResolvedProjectPtr project = ResolvedProject::create();
+ const TopLevelProjectPtr project = TopLevelProject::create();
// TODO: Store some meta data that will enable us to show actual progress (e.g. number of products).
evalContext->initializeObserver(Tr::tr("Restoring build graph from disk"), 1);
project->load(pool);
project->buildData->evaluationContext = evalContext;
- foreach (Artifact * const a, project->buildData->dependencyArtifacts)
- a->project = project;
- foreach (const ResolvedProductPtr &product, project->products) {
- if (!product->buildData)
- continue;
- foreach (Artifact * const a, product->buildData->artifacts) {
- a->project = project;
- project->buildData->insertIntoArtifactLookupTable(a);
- }
- }
if (QFileInfo(project->location.fileName()) != QFileInfo(parameters.projectFilePath())) {
QString errorMessage = Tr::tr("Stored build graph is for project file '%1', but "
@@ -513,9 +521,13 @@ BuildGraphLoader::LoadResult BuildGraphLoader::load(const SetupProjectParameters
errorMessage += Tr::tr("Ignoring.");
m_logger.qbsWarning() << errorMessage;
}
- foreach (const ResolvedProductPtr &p, project->products)
- p->project = project;
- project->location = CodeLocation(parameters.projectFilePath(), 1, 1);
+
+ restoreBackPointers(project);
+ foreach (Artifact * const a, project->buildData->dependencyArtifacts)
+ a->project = project;
+
+ project->location = CodeLocation(parameters.projectFilePath(), project->location.line(),
+ project->location.column());
project->setBuildConfiguration(pool.headData().projectConfig);
project->buildDirectory = buildDir;
m_result.loadedProject = project;
@@ -524,14 +536,77 @@ BuildGraphLoader::LoadResult BuildGraphLoader::load(const SetupProjectParameters
return m_result;
}
+static void exchangeDependencies(const ResolvedProductPtr &newProduct,
+ const QList<ResolvedProductPtr> &allRestoredProducts,
+ const QList<ResolvedProductPtr> &addedProducts)
+{
+ QSet<ResolvedProductPtr> newDependencies;
+ foreach (const ResolvedProductPtr &dependency, newProduct->dependencies) {
+ if (addedProducts.contains(dependency)) {
+ newDependencies << dependency;
+ exchangeDependencies(dependency, allRestoredProducts, addedProducts);
+ continue;
+ }
+ bool counterPartFound = false;
+ foreach (const ResolvedProductPtr &restoredProduct, allRestoredProducts) {
+ if (restoredProduct->name == dependency->name) {
+ newDependencies << restoredProduct;
+ counterPartFound = true;
+ break;
+ }
+ }
+ QBS_CHECK(counterPartFound);
+ }
+ newProduct->dependencies = newDependencies;
+}
+
+static void manipulateAddedProducts(const QList<ResolvedProjectPtr> &allRestoredProjects,
+ const QList<ResolvedProductPtr> &allRestoredProducts,
+ const QList<ResolvedProductPtr> &addedProducts)
+{
+ foreach (const ResolvedProductPtr &product, addedProducts) {
+ // Find the right existing (sub-)project to put the new product into.
+ bool projectFound = false;
+ foreach (const ResolvedProjectPtr &project, allRestoredProjects) {
+ if (project->location != product->project->location)
+ continue;
+ product->project = project;
+ project->products.append(product);
+ projectFound = true;
+ break;
+ }
+ QBS_CHECK(projectFound);
+
+ // Exchange dependencies that are not new with their counterparts from the restored project.
+ exchangeDependencies(product, allRestoredProducts, addedProducts);
+ }
+}
+
+// TODO: Pay more attention to project metadata such as name and location. If these change,
+// we must transfer them over to the old build graph.
void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &parameters,
- const QString &buildGraphFilePath, const ResolvedProjectPtr &restoredProject)
+ const QString &buildGraphFilePath, const TopLevelProjectPtr &restoredProject)
{
const FileInfo bgfi(buildGraphFilePath);
- const bool projectFileChanged
- = bgfi.lastModified() < FileInfo(parameters.projectFilePath()).lastModified();
+ const QList<ResolvedProjectPtr> allRestoredProjects
+ = restoredProject->allSubProjects() << restoredProject;
+ bool projectFileChanged = false;
+ bool subProjectRemoved = false;
+ foreach (const ResolvedProjectConstPtr &p, allRestoredProjects) {
+ FileInfo fi(p->location.fileName());
+ if (!fi.exists()) {
+ subProjectRemoved = true;
+ break;
+ }
+ if (bgfi.lastModified() < fi.lastModified()) {
+ projectFileChanged = true;
+ break;
+ }
+ }
+ if (subProjectRemoved)
+ m_logger.qbsTrace() << "A sub-project was removed, must re-resolve project";
if (projectFileChanged)
- m_logger.qbsTrace() << "Project file changed, must re-resolve project.";
+ m_logger.qbsTrace() << "A project file changed, must re-resolve project.";
bool environmentChanged = false;
for (QHash<QString, QString>::ConstIterator it = restoredProject->usedEnvironment.constBegin();
@@ -541,12 +616,13 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &paramet
if (environmentChanged)
m_logger.qbsTrace() << "A relevant environment variable changed, must re-resolve project.";
- bool referencedProductRemoved = false;
+ bool productRemoved = false;
+ const QList<ResolvedProductPtr> allRestoredProducts = restoredProject->allProducts();
QList<ResolvedProductPtr> changedProducts;
- foreach (const ResolvedProductPtr &product, restoredProject->products) {
+ foreach (const ResolvedProductPtr &product, allRestoredProducts) {
const FileInfo pfi(product->location.fileName());
if (!pfi.exists()) {
- referencedProductRemoved = true;
+ productRemoved = true;
} else if (bgfi.lastModified() < pfi.lastModified()) {
changedProducts += product;
} else {
@@ -566,7 +642,7 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &paramet
}
}
- if (!environmentChanged && !projectFileChanged && !referencedProductRemoved
+ if (!environmentChanged && !projectFileChanged && !subProjectRemoved && !productRemoved
&& changedProducts.isEmpty()) {
return;
}
@@ -576,11 +652,13 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &paramet
m_result.newlyResolvedProject = ldr.loadProject(parameters);
QMap<QString, ResolvedProductPtr> freshProductsByName;
- foreach (const ResolvedProductPtr &cp, m_result.newlyResolvedProject->products)
+ const QList<ResolvedProductPtr> allNewlyResolvedProducts
+ = m_result.newlyResolvedProject->allProducts();
+ foreach (const ResolvedProductPtr &cp, allNewlyResolvedProducts)
freshProductsByName.insert(cp->name, cp);
QSet<TransformerPtr> seenTransformers;
- foreach (const ResolvedProductPtr &product, restoredProject->products) {
+ foreach (const ResolvedProductPtr &product, allRestoredProducts) {
if (!product->buildData)
continue;
foreach (Artifact *artifact, product->buildData->artifacts) {
@@ -605,14 +683,14 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &paramet
}
QSet<QString> oldProductNames, newProductNames;
- foreach (const ResolvedProductConstPtr &product, restoredProject->products)
+ foreach (const ResolvedProductConstPtr &product, allRestoredProducts)
oldProductNames += product->name;
- foreach (const ResolvedProductConstPtr &product, m_result.newlyResolvedProject->products)
+ foreach (const ResolvedProductConstPtr &product, allNewlyResolvedProducts)
newProductNames += product->name;
const QSet<QString> removedProductsNames = oldProductNames - newProductNames;
if (!removedProductsNames.isEmpty()) {
- foreach (const ResolvedProductPtr &product, restoredProject->products) {
+ foreach (const ResolvedProductPtr &product, allRestoredProducts) {
if (removedProductsNames.contains(product->name))
onProductRemoved(product);
}
@@ -626,10 +704,7 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &paramet
addedProducts.append(freshProduct);
}
if (!addedProducts.isEmpty()) {
- foreach (const ResolvedProductPtr &product, addedProducts) {
- product->project = restoredProject;
- restoredProject->products.append(product);
- }
+ manipulateAddedProducts(allRestoredProjects, allRestoredProducts, addedProducts);
BuildDataResolver bpr(m_logger);
bpr.resolveProductBuildDataForExistingProject(restoredProject, addedProducts);
}
@@ -641,7 +716,7 @@ void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product)
{
m_logger.qbsDebug() << "[BG] product '" << product->name << "' removed.";
- product->project->buildData->isDirty = true;
+ product->topLevelProject()->buildData->isDirty = true;
product->project->products.removeOne(product);
// delete all removed artifacts physically from the disk
@@ -681,7 +756,7 @@ void BuildGraphLoader::onProductChanged(const ResolvedProductPtr &product,
} else {
newArtifact = createArtifact(product, a, m_logger);
foreach (Artifact *oldArtifact,
- product->project->buildData->lookupArtifacts(newArtifact->filePath())) {
+ product->topLevelProject()->buildData->lookupArtifacts(newArtifact->filePath())) {
if (oldArtifact == newArtifact
|| oldArtifact->artifactType != Artifact::FileDependency) {
// The source file already exists in another product.
@@ -747,8 +822,8 @@ void BuildGraphLoader::onProductChanged(const ResolvedProductPtr &product,
// parents of removed artifacts must update their transformers
foreach (Artifact *removedArtifact, artifactsToRemove)
foreach (Artifact *parent, removedArtifact->parents)
- product->project->buildData->artifactsThatMustGetNewTransformers += parent;
- product->project->buildData->updateNodesThatMustGetNewTransformer(m_logger);
+ product->topLevelProject()->buildData->artifactsThatMustGetNewTransformers += parent;
+ product->topLevelProject()->buildData->updateNodesThatMustGetNewTransformer(m_logger);
// delete all removed artifacts physically from the disk
foreach (Artifact *artifact, artifactsToRemove) {
@@ -773,14 +848,14 @@ void BuildGraphLoader::removeArtifactAndExclusiveDependents(Artifact *artifact,
if (parent->children.isEmpty()) {
removeParent = true;
} else if (parent->transformer) {
- artifact->project->buildData->artifactsThatMustGetNewTransformers += parent;
+ artifact->topLevelProject()->buildData->artifactsThatMustGetNewTransformers += parent;
parent->transformer->inputs.remove(artifact);
removeParent = parent->transformer->inputs.isEmpty();
}
if (removeParent)
removeArtifactAndExclusiveDependents(parent, removedArtifacts);
}
- artifact->project->buildData->removeArtifact(artifact, m_logger);
+ artifact->topLevelProject()->buildData->removeArtifact(artifact, m_logger);
}
bool BuildGraphLoader::checkForPropertyChanges(const TransformerPtr &restoredTrafo,
@@ -824,8 +899,8 @@ void BuildGraphLoader::replaceFileDependencyWithArtifact(Artifact *filedep, Arti
artifactInProduct->fileDependencies.remove(filedep);
}
}
- filedep->project->buildData->dependencyArtifacts.remove(filedep);
- filedep->project->buildData->removeFromArtifactLookupTable(filedep);
+ filedep->topLevelProject()->buildData->dependencyArtifacts.remove(filedep);
+ filedep->topLevelProject()->buildData->removeFromArtifactLookupTable(filedep);
delete filedep;
}
diff --git a/src/lib/buildgraph/buildgraph.h b/src/lib/buildgraph/buildgraph.h
index bf1256756..47798d73a 100644
--- a/src/lib/buildgraph/buildgraph.h
+++ b/src/lib/buildgraph/buildgraph.h
@@ -90,8 +90,8 @@ public:
public:
LoadResult() : discardLoadedProject(false) {}
- ResolvedProjectPtr newlyResolvedProject;
- ResolvedProjectPtr loadedProject;
+ TopLevelProjectPtr newlyResolvedProject;
+ TopLevelProjectPtr loadedProject;
bool discardLoadedProject;
};
@@ -101,7 +101,7 @@ public:
private:
void trackProjectChanges(const SetupProjectParameters &parameters,
const QString &buildGraphFilePath,
- const ResolvedProjectPtr &restoredProject);
+ const TopLevelProjectPtr &restoredProject);
void onProductRemoved(const ResolvedProductPtr &product);
void onProductChanged(const ResolvedProductPtr &product,
const ResolvedProductPtr &changedProduct);
diff --git a/src/lib/buildgraph/cycledetector.cpp b/src/lib/buildgraph/cycledetector.cpp
index 3e39b70c9..28e027d93 100644
--- a/src/lib/buildgraph/cycledetector.cpp
+++ b/src/lib/buildgraph/cycledetector.cpp
@@ -46,7 +46,7 @@ CycleDetector::CycleDetector(const Logger &logger)
void CycleDetector::visitProject(const ResolvedProjectConstPtr &project)
{
const QString description = QString::fromLocal8Bit("Cycle detection for project '%1'")
- .arg(project->id());
+ .arg(project->name);
TimedActivityLogger timeLogger(m_logger, description, QLatin1String("[BG] "), LoggerTrace);
ArtifactVisitor::visitProject(project);
}
diff --git a/src/lib/buildgraph/executor.cpp b/src/lib/buildgraph/executor.cpp
index a312c4785..4b00e1008 100644
--- a/src/lib/buildgraph/executor.cpp
+++ b/src/lib/buildgraph/executor.cpp
@@ -161,7 +161,7 @@ void Executor::build()
}
}
-void Executor::setProject(const ResolvedProjectPtr &project)
+void Executor::setProject(const TopLevelProjectPtr &project)
{
m_project = project;
}
@@ -632,7 +632,7 @@ void Executor::doSanityChecks()
QBS_CHECK(!m_productsToBuild.isEmpty());
foreach (const ResolvedProductConstPtr &product, m_productsToBuild) {
QBS_CHECK(product->buildData);
- QBS_CHECK(product->project == m_project);
+ QBS_CHECK(product->topLevelProject() == m_project);
}
}
@@ -711,7 +711,7 @@ void Executor::onProcessSuccess()
QBS_CHECK(processedArtifact);
// Update the timestamps of the outputs of the transformer we just executed.
- processedArtifact->project->buildData->isDirty = true;
+ processedArtifact->topLevelProject()->buildData->isDirty = true;
foreach (Artifact *artifact, processedArtifact->transformer->outputs) {
if (artifact->alwaysUpdated)
artifact->timestamp = FileTime::currentTime();
diff --git a/src/lib/buildgraph/executor.h b/src/lib/buildgraph/executor.h
index 0cece1cd2..7a55adf64 100644
--- a/src/lib/buildgraph/executor.h
+++ b/src/lib/buildgraph/executor.h
@@ -62,7 +62,7 @@ public:
Executor(const Logger &logger, QObject *parent = 0);
~Executor();
- void setProject(const ResolvedProjectPtr &project);
+ void setProject(const TopLevelProjectPtr &project);
void setProducts(const QList<ResolvedProductPtr> &productsToBuild);
void setBuildOptions(const BuildOptions &buildOptions);
void setProgressObserver(ProgressObserver *observer) { m_progressObserver = observer; }
@@ -120,7 +120,7 @@ private:
QList<ExecutorJob*> m_availableJobs;
QHash<ExecutorJob*, Artifact *> m_processingJobs;
ExecutorState m_state;
- ResolvedProjectPtr m_project;
+ TopLevelProjectPtr m_project;
QList<ResolvedProductPtr> m_productsToBuild;
QList<Artifact *> m_roots;
QList<Artifact *> m_leaves;
diff --git a/src/lib/buildgraph/inputartifactscanner.cpp b/src/lib/buildgraph/inputartifactscanner.cpp
index f2ea1220d..f6863bc3b 100644
--- a/src/lib/buildgraph/inputartifactscanner.cpp
+++ b/src/lib/buildgraph/inputartifactscanner.cpp
@@ -88,12 +88,12 @@ static void resolveWithIncludePath(const QString &includePath,
if (!dependency.isClean())
absDirPath = QDir::cleanPath(absDirPath);
- const ResolvedProject *project = product->project.data();
+ ResolvedProject *project = product->project.data();
Artifact *fileDependencyArtifact = 0;
Artifact *dependencyInProduct = 0;
Artifact *dependencyInOtherProduct = 0;
- foreach (Artifact *artifact,
- project->buildData->lookupArtifacts(absDirPath, dependency.fileName())) {
+ foreach (Artifact *artifact, project->topLevelProject()
+ ->buildData->lookupArtifacts(absDirPath, dependency.fileName())) {
if (artifact->artifactType == Artifact::FileDependency)
fileDependencyArtifact = artifact;
else if (artifact->product == product)
@@ -319,7 +319,7 @@ void InputArtifactScanner::handleDependency(ResolvedDependency &dependency)
dependency.artifact->artifactType = Artifact::FileDependency;
dependency.artifact->properties = m_artifact->properties;
dependency.artifact->setFilePath(dependency.filePath);
- m_artifact->project->buildData->insertFileDependency(dependency.artifact);
+ m_artifact->topLevelProject()->buildData->insertFileDependency(dependency.artifact);
} else if (dependency.artifact->artifactType == Artifact::FileDependency) {
// The dependency exists in the project's list of file dependencies.
if (m_logger.traceEnabled()) {
diff --git a/src/lib/buildgraph/productinstaller.cpp b/src/lib/buildgraph/productinstaller.cpp
index 310f45786..93ac80ffa 100644
--- a/src/lib/buildgraph/productinstaller.cpp
+++ b/src/lib/buildgraph/productinstaller.cpp
@@ -35,6 +35,7 @@
#include <tools/qbsassert.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
+#include <tools/propertyfinder.h>
#include <tools/progressobserver.h>
#include <tools/qbsassert.h>
@@ -44,8 +45,9 @@
namespace qbs {
namespace Internal {
-ProductInstaller::ProductInstaller(const QList<ResolvedProductPtr> &products,
- const InstallOptions &options, ProgressObserver *observer, const Logger &logger)
+ProductInstaller::ProductInstaller(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const InstallOptions &options,
+ ProgressObserver *observer, const Logger &logger)
: m_products(products), m_options(options), m_observer(observer), m_logger(logger)
{
if (!m_options.installRoot().isEmpty()) {
@@ -61,18 +63,14 @@ ProductInstaller::ProductInstaller(const QList<ResolvedProductPtr> &products,
return;
}
- if (m_products.isEmpty())
- throw Error(Tr::tr("Cannot deduce install root, because there are no products."));
-
- const ResolvedProductConstPtr &product = m_products.first();
if (m_options.installIntoSysroot()) {
if (m_options.removeExistingInstallation())
throw Error(Tr::tr("Refusing to remove sysroot."));
- m_options.setInstallRoot(product->properties
- ->qbsPropertyValue(QLatin1String("sysroot")).toString());
+ m_options.setInstallRoot(PropertyFinder().propertyValue(project->buildConfiguration(),
+ QLatin1String("qbs"), QLatin1String("sysroot")).toString());
} else {
- m_options.setInstallRoot(product->project->buildDirectory
- + QLatin1Char('/') + InstallOptions::defaultInstallRoot());
+ m_options.setInstallRoot(project->buildDirectory + QLatin1Char('/') +
+ InstallOptions::defaultInstallRoot());
}
}
@@ -119,7 +117,7 @@ void ProductInstaller::copyFile(const Artifact *artifact)
{
if (m_observer->canceled()) {
throw Error(Tr::tr("Installation canceled for configuration '%1'.")
- .arg(m_products.first()->project->id()));
+ .arg(m_products.first()->project->topLevelProject()->id()));
}
const QString relativeInstallDir
= artifact->properties->qbsPropertyValue(QLatin1String("installDir")).toString();
diff --git a/src/lib/buildgraph/productinstaller.h b/src/lib/buildgraph/productinstaller.h
index acea496f1..1c711f02a 100644
--- a/src/lib/buildgraph/productinstaller.h
+++ b/src/lib/buildgraph/productinstaller.h
@@ -44,8 +44,8 @@ class ProgressObserver;
class ProductInstaller
{
public:
- ProductInstaller(const QList<ResolvedProductPtr> &products, const InstallOptions &options,
- ProgressObserver *observer, const Logger &logger);
+ ProductInstaller(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const InstallOptions &options, ProgressObserver *observer, const Logger &logger);
void install();
private:
diff --git a/src/lib/buildgraph/projectbuilddata.cpp b/src/lib/buildgraph/projectbuilddata.cpp
index c2adde294..b885254ac 100644
--- a/src/lib/buildgraph/projectbuilddata.cpp
+++ b/src/lib/buildgraph/projectbuilddata.cpp
@@ -46,7 +46,7 @@
namespace qbs {
namespace Internal {
-ProjectBuildData::ProjectBuildData() : isDirty(false)
+ProjectBuildData::ProjectBuildData() : isDirty(true)
{
}
@@ -187,16 +187,17 @@ BuildDataResolver::BuildDataResolver(const Logger &logger) : m_logger(logger)
{
}
-void BuildDataResolver::resolveBuildData(const ResolvedProjectPtr &resolvedProject,
+void BuildDataResolver::resolveBuildData(const TopLevelProjectPtr &resolvedProject,
const RulesEvaluationContextPtr &evalContext)
{
QBS_CHECK(!resolvedProject->buildData);
m_project = resolvedProject;
resolvedProject->buildData.reset(new ProjectBuildData);
resolvedProject->buildData->evaluationContext = evalContext;
+ const QList<ResolvedProductPtr> allProducts = resolvedProject->allProducts();
evalContext->initializeObserver(Tr::tr("Setting up build graph for configuration %1")
- .arg(resolvedProject->id()), resolvedProject->products.count());
- foreach (ResolvedProductPtr rProduct, resolvedProject->products) {
+ .arg(resolvedProject->id()), allProducts.count());
+ foreach (ResolvedProductPtr rProduct, allProducts) {
if (rProduct->enabled)
resolveProductBuildData(rProduct);
evalContext->incrementProgressValue();
@@ -204,7 +205,7 @@ void BuildDataResolver::resolveBuildData(const ResolvedProjectPtr &resolvedProje
CycleDetector(m_logger).visitProject(m_project);
}
-void BuildDataResolver::resolveProductBuildDataForExistingProject(const ResolvedProjectPtr &project,
+void BuildDataResolver::resolveProductBuildDataForExistingProject(const TopLevelProjectPtr &project,
const QList<ResolvedProductPtr> &freshProducts)
{
m_project = project;
@@ -217,8 +218,8 @@ void BuildDataResolver::resolveProductBuildDataForExistingProject(const Resolved
* - dependencies between artifacts,
* - time stamps of artifacts, if their commands have not changed.
*/
-void BuildDataResolver::rescueBuildData(const ResolvedProjectConstPtr &source,
- const ResolvedProjectPtr &target, Logger logger)
+void BuildDataResolver::rescueBuildData(const TopLevelProjectConstPtr &source,
+ const TopLevelProjectPtr &target, Logger logger)
{
QHash<QString, ResolvedProductConstPtr> sourceProductsByName;
foreach (const ResolvedProductConstPtr &product, source->products)
@@ -295,7 +296,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
//add qbsFile artifact
Artifact *qbsFileArtifact = lookupArtifact(product, product->location.fileName());
if (!qbsFileArtifact) {
- qbsFileArtifact = new Artifact(m_project);
+ qbsFileArtifact = new Artifact(product->project);
qbsFileArtifact->artifactType = Artifact::SourceFile;
qbsFileArtifact->setFilePath(product->location.fileName());
qbsFileArtifact->properties = product->properties;
diff --git a/src/lib/buildgraph/projectbuilddata.h b/src/lib/buildgraph/projectbuilddata.h
index ee656af7e..334cf8e75 100644
--- a/src/lib/buildgraph/projectbuilddata.h
+++ b/src/lib/buildgraph/projectbuilddata.h
@@ -81,13 +81,13 @@ class BuildDataResolver
{
public:
BuildDataResolver(const Logger &logger);
- void resolveBuildData(const ResolvedProjectPtr &resolvedProject,
+ void resolveBuildData(const TopLevelProjectPtr &resolvedProject,
const RulesEvaluationContextPtr &evalContext);
- void resolveProductBuildDataForExistingProject(const ResolvedProjectPtr &project,
+ void resolveProductBuildDataForExistingProject(const TopLevelProjectPtr &project,
const QList<ResolvedProductPtr> &freshProducts);
- static void rescueBuildData(const ResolvedProjectConstPtr &source,
- const ResolvedProjectPtr &target, Logger logger);
+ static void rescueBuildData(const TopLevelProjectConstPtr &source,
+ const TopLevelProjectPtr &target, Logger logger);
private:
void resolveProductBuildData(const ResolvedProductPtr &product);
@@ -95,7 +95,7 @@ private:
ScriptEngine *engine() const;
QScriptValue scope() const;
- ResolvedProjectPtr m_project;
+ TopLevelProjectPtr m_project;
Logger m_logger;
};
diff --git a/src/lib/buildgraph/rulesapplicator.cpp b/src/lib/buildgraph/rulesapplicator.cpp
index 96054c328..11405a716 100644
--- a/src/lib/buildgraph/rulesapplicator.cpp
+++ b/src/lib/buildgraph/rulesapplicator.cpp
@@ -59,7 +59,7 @@ RulesApplicator::RulesApplicator(const ResolvedProductPtr &product,
void RulesApplicator::applyAllRules()
{
- RulesEvaluationContext::Scope s(m_product->project->buildData->evaluationContext.data());
+ RulesEvaluationContext::Scope s(m_product->topLevelProject()->buildData->evaluationContext.data());
foreach (const RuleConstPtr &rule, m_product->topSortedRules())
applyRule(rule);
}
@@ -147,7 +147,8 @@ void RulesApplicator::doApply(const ArtifactList &inputArtifacts)
}
m_transformer->outputs.insert(outputArtifact);
- m_product->project->buildData->artifactsThatMustGetNewTransformers -= outputArtifact;
+ m_product->topLevelProject()->buildData->artifactsThatMustGetNewTransformers
+ -= outputArtifact;
}
m_transformer->setupInputs(engine(), scope());
@@ -199,7 +200,7 @@ void RulesApplicator::setupScriptEngineForArtifact(Artifact *artifact)
QDir sourceDir(m_product->sourceDirectory);
basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
} else {
- QDir buildDir(m_product->project->buildDirectory);
+ QDir buildDir(m_product->topLevelProject()->buildDirectory);
basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
}
@@ -303,7 +304,7 @@ Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &rule
QString RulesApplicator::resolveOutPath(const QString &path) const
{
- QString buildDir = m_product->project->buildDirectory;
+ QString buildDir = m_product->topLevelProject()->buildDirectory;
QString result = FileInfo::resolvePath(buildDir, path);
result = QDir::cleanPath(result);
return result;
@@ -311,7 +312,7 @@ QString RulesApplicator::resolveOutPath(const QString &path) const
RulesEvaluationContextPtr RulesApplicator::evalContext() const
{
- return m_product->project->buildData->evaluationContext;
+ return m_product->topLevelProject()->buildData->evaluationContext;
}
ScriptEngine *RulesApplicator::engine() const
diff --git a/src/lib/buildgraph/timestampsupdater.cpp b/src/lib/buildgraph/timestampsupdater.cpp
index 9c338a965..44130b06f 100644
--- a/src/lib/buildgraph/timestampsupdater.cpp
+++ b/src/lib/buildgraph/timestampsupdater.cpp
@@ -70,7 +70,7 @@ private:
FileTime m_now;
};
-void TimestampsUpdater::updateTimestamps(const ResolvedProjectPtr &project,
+void TimestampsUpdater::updateTimestamps(const TopLevelProjectPtr &project,
const QList<ResolvedProductPtr> &products, const Logger &logger)
{
TimestampsUpdateVisitor v;
diff --git a/src/lib/buildgraph/timestampsupdater.h b/src/lib/buildgraph/timestampsupdater.h
index 561714ea4..8505b8f34 100644
--- a/src/lib/buildgraph/timestampsupdater.h
+++ b/src/lib/buildgraph/timestampsupdater.h
@@ -40,7 +40,7 @@ class Logger;
class TimestampsUpdater
{
public:
- void updateTimestamps(const ResolvedProjectPtr &project,
+ void updateTimestamps(const TopLevelProjectPtr &project,
const QList<ResolvedProductPtr> &products, const Logger &logger);
};
diff --git a/src/lib/language/builtindeclarations.cpp b/src/lib/language/builtindeclarations.cpp
index 9697c82d8..1df7649a9 100644
--- a/src/lib/language/builtindeclarations.cpp
+++ b/src/lib/language/builtindeclarations.cpp
@@ -251,6 +251,8 @@ void BuiltinDeclarations::addProductItem()
void BuiltinDeclarations::addProjectItem()
{
QList<PropertyDeclaration> properties;
+ properties += nameProperty();
+ properties += conditionProperty();
properties += PropertyDeclaration(QLatin1String("references"), PropertyDeclaration::Variant,
PropertyDeclaration::PropertyNotAvailableInConfig);
properties += PropertyDeclaration(QLatin1String("moduleSearchPaths"),
@@ -280,6 +282,18 @@ void BuiltinDeclarations::addRuleItem()
m_builtins[QLatin1String("Rule")] = properties;
}
+void BuiltinDeclarations::addSubprojectItem()
+{
+ QList<PropertyDeclaration> properties;
+ properties += PropertyDeclaration(QLatin1String("filePath"), PropertyDeclaration::Path);
+ PropertyDeclaration inheritProperty;
+ inheritProperty.name = QLatin1String("inheritProperties");
+ inheritProperty.type = PropertyDeclaration::Boolean;
+ inheritProperty.initialValueSource = QLatin1String("true");
+ properties += inheritProperty;
+ m_builtins[QLatin1String("SubProject")] = properties;
+}
+
void BuiltinDeclarations::addTransformerItem()
{
QList<PropertyDeclaration> properties;
diff --git a/src/lib/language/builtindeclarations.h b/src/lib/language/builtindeclarations.h
index 1f5869b53..7f5ddeda6 100644
--- a/src/lib/language/builtindeclarations.h
+++ b/src/lib/language/builtindeclarations.h
@@ -58,6 +58,7 @@ private:
void addProjectItem();
void addPropertyOptionsItem();
void addRuleItem();
+ void addSubprojectItem();
void addTransformerItem();
QString m_languageVersion;
diff --git a/src/lib/language/evaluator.cpp b/src/lib/language/evaluator.cpp
index 12ec26d7b..e80351ca3 100644
--- a/src/lib/language/evaluator.cpp
+++ b/src/lib/language/evaluator.cpp
@@ -31,10 +31,12 @@
#include "evaluationdata.h"
#include "evaluatorscriptclass.h"
#include "filecontext.h"
+#include "filetags.h"
#include "item.h"
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/qbsassert.h>
+#include <tools/scripttools.h>
#include <QDebug>
#include <QScriptEngine>
@@ -82,6 +84,57 @@ QScriptValue Evaluator::property(const Item *item, const QStringList &nameParts)
return property(targetItem, nameParts.last());
}
+bool Evaluator::boolValue(const Item *item, const QString &name, bool defaultValue,
+ bool *propertyWasSet)
+{
+ QScriptValue v = property(item, name);
+ if (Q_UNLIKELY(v.isError())) {
+ ValuePtr value = item->property(name);
+ throw Error(v.toString(), value ? value->location() : CodeLocation());
+ }
+ if (!v.isValid() || v.isUndefined()) {
+ if (propertyWasSet)
+ *propertyWasSet = false;
+ return defaultValue;
+ }
+ if (propertyWasSet)
+ *propertyWasSet = true;
+ return v.toBool();
+}
+
+FileTags Evaluator::fileTagsValue(const Item *item, const QString &name)
+{
+ return FileTags::fromStringList(stringListValue(item, name));
+}
+
+QString Evaluator::stringValue(const Item *item, const QString &name,
+ const QString &defaultValue, bool *propertyWasSet)
+{
+ QScriptValue v = property(item, name);
+ if (Q_UNLIKELY(v.isError())) {
+ ValuePtr value = item->property(name);
+ throw Error(v.toString(), value ? value->location() : CodeLocation());
+ }
+ if (!v.isValid() || v.isUndefined()) {
+ if (propertyWasSet)
+ *propertyWasSet = false;
+ return defaultValue;
+ }
+ if (propertyWasSet)
+ *propertyWasSet = true;
+ return v.toString();
+}
+
+QStringList Evaluator::stringListValue(const Item *item, const QString &name)
+{
+ QScriptValue v = property(item, name);
+ if (Q_UNLIKELY(v.isError())) {
+ ValuePtr value = item->property(name);
+ throw Error(v.toString(), value ? value->location() : CodeLocation());
+ }
+ return toStringList(v);
+}
+
QScriptValue Evaluator::scriptValue(const Item *item)
{
QScriptValue &scriptValue = m_scriptValueMap[item];
diff --git a/src/lib/language/evaluator.h b/src/lib/language/evaluator.h
index 9262cc48d..54ae6b24d 100644
--- a/src/lib/language/evaluator.h
+++ b/src/lib/language/evaluator.h
@@ -39,6 +39,7 @@
namespace qbs {
namespace Internal {
+class FileTags;
class EvaluatorScriptClass;
@@ -53,6 +54,14 @@ public:
ScriptEngine *engine() const;
QScriptValue property(const Item *item, const QString &name);
QScriptValue property(const Item *item, const QStringList &nameParts);
+
+ bool boolValue(const Item *item, const QString &name, bool defaultValue = false,
+ bool *propertyWasSet = 0);
+ FileTags fileTagsValue(const Item *item, const QString &name);
+ QString stringValue(const Item *item, const QString &name,
+ const QString &defaultValue = QString(), bool *propertyWasSet = 0);
+ QStringList stringListValue(const Item *item, const QString &name);
+
QScriptValue scriptValue(const Item *item);
QScriptValue fileScope(const FileContextConstPtr &file);
diff --git a/src/lib/language/forward_decls.h b/src/lib/language/forward_decls.h
index cd6117412..f05148902 100644
--- a/src/lib/language/forward_decls.h
+++ b/src/lib/language/forward_decls.h
@@ -74,6 +74,10 @@ class ResolvedProject;
typedef QSharedPointer<ResolvedProject> ResolvedProjectPtr;
typedef QSharedPointer<const ResolvedProject> ResolvedProjectConstPtr;
+class TopLevelProject;
+typedef QSharedPointer<TopLevelProject> TopLevelProjectPtr;
+typedef QSharedPointer<const TopLevelProject> TopLevelProjectConstPtr;
+
class Rule;
typedef QSharedPointer<Rule> RulePtr;
typedef QSharedPointer<const Rule> RuleConstPtr;
diff --git a/src/lib/language/item.h b/src/lib/language/item.h
index 12380aaec..d298c9788 100644
--- a/src/lib/language/item.h
+++ b/src/lib/language/item.h
@@ -37,6 +37,7 @@
#include "propertydeclaration.h"
#include <parser/qmljsmemorypool_p.h>
#include <tools/codelocation.h>
+#include <tools/error.h>
#include <tools/weakpointer.h>
#include <QList>
@@ -70,6 +71,8 @@ public:
Item *item;
};
typedef QList<Module> Modules;
+ typedef QMap<QString, PropertyDeclaration> PropertyDeclarationMap;
+ typedef QMap<QString, ValuePtr> PropertyMap;
static Item *create(ItemPool *pool);
Item *clone(ItemPool *pool) const;
@@ -85,10 +88,11 @@ public:
Item *parent() const;
const FileContextPtr file() const;
QList<Item *> children() const;
- const QMap<QString, ValuePtr> &properties() const;
- const QMap<QString, PropertyDeclaration> &propertyDeclarations() const;
+ const PropertyMap &properties() const;
+ const PropertyDeclarationMap &propertyDeclarations() const;
const Modules &modules() const;
Modules &modules();
+ const Error &error() const { return m_error; }
bool hasProperty(const QString &name) const;
bool hasOwnProperty(const QString &name) const;
@@ -97,6 +101,7 @@ public:
JSSourceValuePtr sourceProperty(const QString &name) const;
void setPropertyObserver(ItemObserver *observer) const;
void setProperty(const QString &name, const ValuePtr &value);
+ void setPropertyDeclaration(const QString &name, const PropertyDeclaration &declaration);
void setTypeName(const QString &name);
void setLocation(const CodeLocation &location);
void setPrototype(Item *prototype);
@@ -107,6 +112,7 @@ public:
void setChildren(const QList<Item *> &children);
void setParent(Item *item);
static void addChild(Item *parent, Item *child);
+ void setError(const Error &error) { m_error = error; }
private:
ItemPool *m_pool;
@@ -121,10 +127,11 @@ private:
Item *m_parent;
QList<Item *> m_children;
FileContextPtr m_file;
- QMap<QString, ValuePtr> m_properties;
- QMap<QString, PropertyDeclaration> m_propertyDeclarations;
+ PropertyMap m_properties;
+ PropertyDeclarationMap m_propertyDeclarations;
QList<FunctionDeclaration> m_functions;
Modules m_modules;
+ Error m_error; // For SubProject items. May or may not be reported depending on their condition.
};
inline ItemPool *Item::pool() const
@@ -182,12 +189,12 @@ inline QList<Item *> Item::children() const
return m_children;
}
-inline const QMap<QString, ValuePtr> &Item::properties() const
+inline const Item::PropertyMap &Item::properties() const
{
return m_properties;
}
-inline const QMap<QString, PropertyDeclaration> &Item::propertyDeclarations() const
+inline const Item::PropertyDeclarationMap &Item::propertyDeclarations() const
{
return m_propertyDeclarations;
}
@@ -199,6 +206,12 @@ inline void Item::setProperty(const QString &name, const ValuePtr &value)
m_propertyObserver->onItemPropertyChanged(this);
}
+inline void Item::setPropertyDeclaration(const QString &name,
+ const PropertyDeclaration &declaration)
+{
+ m_propertyDeclarations.insert(name, declaration);
+}
+
inline void Item::setTypeName(const QString &name)
{
m_typeName = name;
diff --git a/src/lib/language/itemreaderastvisitor.cpp b/src/lib/language/itemreaderastvisitor.cpp
index e3fcaac78..dd4be9350 100644
--- a/src/lib/language/itemreaderastvisitor.cpp
+++ b/src/lib/language/itemreaderastvisitor.cpp
@@ -252,8 +252,10 @@ bool ItemReaderASTVisitor::visit(AST::UiObjectDefinition *ast)
}
}
- if (item->typeName() != QLatin1String("Properties"))
+ if (item->typeName() != QLatin1String("Properties")
+ && item->typeName() != QLatin1String("SubProject")) {
setupAlternatives(item);
+ }
// resolve inheritance
const QStringList fullTypeName = toStringList(ast->qualifiedTypeNameId);
diff --git a/src/lib/language/language.cpp b/src/lib/language/language.cpp
index 25f09df5e..51586ef57 100644
--- a/src/lib/language/language.cpp
+++ b/src/lib/language/language.cpp
@@ -441,7 +441,7 @@ enum EnvType
static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType envType,
const QList<ResolvedModuleConstPtr> &modules,
const PropertyMapConstPtr &productConfiguration,
- ResolvedProject *project,
+ TopLevelProject *project,
const QProcessEnvironment &env)
{
QProcessEnvironment procenv = env;
@@ -541,7 +541,8 @@ void ResolvedProduct::setupBuildEnvironment(ScriptEngine *engine, const QProcess
if (!buildEnvironment.isEmpty())
return;
- buildEnvironment = getProcessEnvironment(engine, BuildEnv, modules, properties, project, env);
+ buildEnvironment = getProcessEnvironment(engine, BuildEnv, modules, properties,
+ topLevelProject(), env);
}
void ResolvedProduct::setupRunEnvironment(ScriptEngine *engine, const QProcessEnvironment &env) const
@@ -549,7 +550,8 @@ void ResolvedProduct::setupRunEnvironment(ScriptEngine *engine, const QProcessEn
if (!runEnvironment.isEmpty())
return;
- runEnvironment = getProcessEnvironment(engine, RunEnv, modules, properties, project, env);
+ runEnvironment = getProcessEnvironment(engine, RunEnv, modules, properties,
+ topLevelProject(), env);
}
const QList<RuleConstPtr> &ResolvedProduct::topSortedRules() const
@@ -568,8 +570,107 @@ const QList<RuleConstPtr> &ResolvedProduct::topSortedRules() const
return buildData->topSortedRules;
}
+TopLevelProject *ResolvedProduct::topLevelProject() const
+{
+ return project->topLevelProject();
+}
+
+
+ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(0)
+{
+}
+
+TopLevelProject *ResolvedProject::topLevelProject()
+{
+ if (m_topLevelProject)
+ return m_topLevelProject;
+ TopLevelProject *tlp = dynamic_cast<TopLevelProject *>(this);
+ if (tlp) {
+ m_topLevelProject = tlp;
+ return m_topLevelProject;
+ }
+ QBS_CHECK(!parentProject.isNull());
+ m_topLevelProject = parentProject->topLevelProject();
+ return m_topLevelProject;
+}
+
+QList<ResolvedProjectPtr> ResolvedProject::allSubProjects() const
+{
+ QList<ResolvedProjectPtr> projectList = subProjects;
+ foreach (const ResolvedProjectConstPtr &subProject, subProjects)
+ projectList << subProject->allSubProjects();
+ return projectList;
+}
+
+QList<ResolvedProductPtr> ResolvedProject::allProducts() const
+{
+ QList<ResolvedProductPtr> productList = products;
+ foreach (const ResolvedProjectConstPtr &subProject, subProjects)
+ productList << subProject->allProducts();
+ return productList;
+}
+
+void ResolvedProject::load(PersistentPool &pool)
+{
+ name = pool.idLoadString();
+ const QString fileName = pool.idLoadString();
+ int line;
+ int column;
+ pool.stream() >> line;
+ pool.stream() >> column;
+ location = CodeLocation(fileName, line, column);
+ pool.stream() >> enabled;
-QString ResolvedProject::deriveId(const QVariantMap &config)
+ int count;
+ pool.stream() >> count;
+ products.clear();
+ products.reserve(count);
+ for (; --count >= 0;) {
+ ResolvedProductPtr rProduct = pool.idLoadS<ResolvedProduct>();
+ if (rProduct->buildData) {
+ foreach (Artifact * const a, rProduct->buildData->artifacts)
+ a->product = rProduct;
+ }
+ products.append(rProduct);
+ }
+
+ pool.stream() >> count;
+ subProjects.clear();
+ subProjects.reserve(count);
+ for (; --count >= 0;) {
+ ResolvedProjectPtr p = pool.idLoadS<ResolvedProject>();
+ subProjects.append(p);
+ }
+
+ pool.stream() >> m_projectProperties;
+}
+
+void ResolvedProject::store(PersistentPool &pool) const
+{
+ pool.storeString(name);
+ pool.storeString(location.fileName());
+ pool.stream() << location.line();
+ pool.stream() << location.column();
+ pool.stream() << enabled;
+ pool.stream() << products.count();
+ foreach (const ResolvedProductConstPtr &product, products)
+ pool.store(product);
+ pool.stream() << subProjects.count();
+ foreach (const ResolvedProjectConstPtr &project, subProjects)
+ pool.store(project);
+ pool.stream() << m_projectProperties;
+}
+
+
+TopLevelProject::TopLevelProject()
+{
+}
+
+TopLevelProject::~TopLevelProject()
+{
+}
+
+QString TopLevelProject::deriveId(const QVariantMap &config)
{
const QVariantMap qbsProperties = config.value(QLatin1String("qbs")).toMap();
const QString buildVariant = qbsProperties.value(QLatin1String("buildVariant")).toString();
@@ -577,28 +678,23 @@ QString ResolvedProject::deriveId(const QVariantMap &config)
return profile + QLatin1Char('-') + buildVariant;
}
-QString ResolvedProject::deriveBuildDirectory(const QString &buildRoot, const QString &id)
+QString TopLevelProject::deriveBuildDirectory(const QString &buildRoot, const QString &id)
{
return buildRoot + QLatin1Char('/') + id;
}
-void ResolvedProject::setBuildConfiguration(const QVariantMap &config)
+void TopLevelProject::setBuildConfiguration(const QVariantMap &config)
{
m_buildConfiguration = config;
m_id = deriveId(config);
}
-void ResolvedProject::setProjectProperties(const QVariantMap &config)
-{
- m_projectProperties = config;
-}
-
-QString ResolvedProject::buildGraphFilePath() const
+QString TopLevelProject::buildGraphFilePath() const
{
return ProjectBuildData::deriveBuildGraphFilePath(buildDirectory, id());
}
-void ResolvedProject::store(const Logger &logger) const
+void TopLevelProject::store(const Logger &logger) const
{
// TODO: Use progress observer here.
@@ -619,65 +715,29 @@ void ResolvedProject::store(const Logger &logger) const
buildData->isDirty = false;
}
-ResolvedProject::ResolvedProject()
-{
-}
-
-ResolvedProject::~ResolvedProject()
+void TopLevelProject::load(PersistentPool &pool)
{
-}
-
-void ResolvedProject::load(PersistentPool &pool)
-{
- const QString fileName = pool.idLoadString();
- int line;
- int column;
- pool.stream() >> line;
- pool.stream() >> column;
- location = CodeLocation(fileName, line, column);
+ ResolvedProject::load(pool);
pool.stream() >> m_id;
pool.stream() >> platformEnvironment;
-
- int count;
- pool.stream() >> count;
- products.clear();
- products.reserve(count);
- for (; --count >= 0;) {
- ResolvedProductPtr rProduct = pool.idLoadS<ResolvedProduct>();
- if (rProduct->buildData) {
- foreach (Artifact * const a, rProduct->buildData->artifacts)
- a->product = rProduct;
- }
- products.append(rProduct);
- }
+ pool.stream() >> usedEnvironment;
QHash<QString, QString> envHash;
- pool.stream()
- >> usedEnvironment
- >> m_projectProperties
- >> envHash;
+ pool.stream() >> envHash;
for (QHash<QString, QString>::const_iterator i = envHash.begin(); i != envHash.end(); ++i)
environment.insert(i.key(), i.value());
buildData.reset(pool.idLoad<ProjectBuildData>());
+ buildData->isDirty = false;
}
-void ResolvedProject::store(PersistentPool &pool) const
+void TopLevelProject::store(PersistentPool &pool) const
{
- pool.storeString(location.fileName());
- pool.stream() << location.line();
- pool.stream() << location.column();
+ ResolvedProject::store(pool);
pool.stream() << m_id;
- pool.stream() << platformEnvironment;
-
- pool.stream() << products.count();
- foreach (const ResolvedProductConstPtr &product, products)
- pool.store(product);
+ pool.stream() << platformEnvironment << usedEnvironment;
QHash<QString, QString> envHash;
foreach (const QString &key, environment.keys())
envHash.insert(key, environment.value(key));
- pool.stream()
- << usedEnvironment
- << m_projectProperties
- << envHash;
+ pool.stream() << envHash;
pool.store(buildData.data());
}
diff --git a/src/lib/language/language.h b/src/lib/language/language.h
index e41f8d997..4fb477e86 100644
--- a/src/lib/language/language.h
+++ b/src/lib/language/language.h
@@ -273,7 +273,7 @@ private:
ResolvedTransformer() {}
};
-class ResolvedProject;
+class TopLevelProject;
class ScriptEngine;
class ResolvedProduct : public PersistentObject
@@ -313,6 +313,7 @@ public:
void setupRunEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const;
const QList<RuleConstPtr> &topSortedRules() const;
+ TopLevelProject *topLevelProject() const;
private:
ResolvedProduct();
@@ -321,43 +322,67 @@ private:
void store(PersistentPool &pool) const;
};
-class ResolvedProject: public PersistentObject
+class ResolvedProject : public PersistentObject
+{
+public:
+ static ResolvedProjectPtr create() { return ResolvedProjectPtr(new ResolvedProject); }
+
+ QString name;
+ CodeLocation location;
+ bool enabled;
+ QList<ResolvedProductPtr> products;
+ QList<ResolvedProjectPtr> subProjects;
+ WeakPointer<ResolvedProject> parentProject;
+
+ void setProjectProperties(const QVariantMap &config) { m_projectProperties = config; }
+ const QVariantMap &projectProperties() const { return m_projectProperties; }
+
+ TopLevelProject *topLevelProject();
+ QList<ResolvedProjectPtr> allSubProjects() const;
+ QList<ResolvedProductPtr> allProducts() const;
+
+protected:
+ ResolvedProject();
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+private:
+ QVariantMap m_projectProperties;
+ TopLevelProject *m_topLevelProject;
+};
+
+class TopLevelProject : public ResolvedProject
{
friend class BuildGraphLoader;
public:
- ~ResolvedProject();
+ ~TopLevelProject();
- static ResolvedProjectPtr create() { return ResolvedProjectPtr(new ResolvedProject); }
+ static TopLevelProjectPtr create() { return TopLevelProjectPtr(new TopLevelProject); }
static QString deriveId(const QVariantMap &config);
static QString deriveBuildDirectory(const QString &buildRoot, const QString &id);
- CodeLocation location;
QString buildDirectory; // Not saved
QProcessEnvironment environment;
QVariantMap platformEnvironment;
QHash<QString, QString> usedEnvironment; // Environment variables requested by the project while resolving.
- QList<ResolvedProductPtr> products;
QScopedPointer<ProjectBuildData> buildData;
void setBuildConfiguration(const QVariantMap &config);
const QVariantMap &buildConfiguration() const { return m_buildConfiguration; }
- void setProjectProperties(const QVariantMap &config);
- const QVariantMap &projectProperties() const { return m_projectProperties; }
QString id() const { return m_id; }
QString buildGraphFilePath() const;
void store(const Logger &logger) const;
private:
- ResolvedProject();
+ TopLevelProject();
void load(PersistentPool &pool);
void store(PersistentPool &pool) const;
- QVariantMap m_buildConfiguration;
- QVariantMap m_projectProperties;
QString m_id;
+ QVariantMap m_buildConfiguration;
};
} // namespace Internal
diff --git a/src/lib/language/loader.cpp b/src/lib/language/loader.cpp
index 10e36efb8..22a286b48 100644
--- a/src/lib/language/loader.cpp
+++ b/src/lib/language/loader.cpp
@@ -82,7 +82,7 @@ void Loader::setSearchPaths(const QStringList &_searchPaths)
m_moduleLoader->setSearchPaths(searchPaths);
}
-ResolvedProjectPtr Loader::loadProject(const SetupProjectParameters &parameters)
+TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters &parameters)
{
QBS_CHECK(QFileInfo(parameters.projectFilePath()).isAbsolute());
@@ -92,7 +92,7 @@ ResolvedProjectPtr Loader::loadProject(const SetupProjectParameters &parameters)
// we have enough information.
if (m_progressObserver) {
m_progressObserver->initialize(Tr::tr("Resolving project for configuration %1")
- .arg(ResolvedProject::deriveId(parameters.buildConfiguration())), 1);
+ .arg(TopLevelProject::deriveId(parameters.buildConfiguration())), 1);
}
ModuleLoaderResult loadResult
= m_moduleLoader->load(parameters.projectFilePath(),
diff --git a/src/lib/language/loader.h b/src/lib/language/loader.h
index 12f73b2e7..30ecba75b 100644
--- a/src/lib/language/loader.h
+++ b/src/lib/language/loader.h
@@ -54,7 +54,7 @@ public:
void setProgressObserver(ProgressObserver *observer);
void setSearchPaths(const QStringList &searchPaths);
- ResolvedProjectPtr loadProject(const SetupProjectParameters &parameters);
+ TopLevelProjectPtr loadProject(const SetupProjectParameters &parameters);
QByteArray qmlTypeInfo();
private:
diff --git a/src/lib/language/moduleloader.cpp b/src/lib/language/moduleloader.cpp
index 041226316..56b8284e8 100644
--- a/src/lib/language/moduleloader.cpp
+++ b/src/lib/language/moduleloader.cpp
@@ -29,6 +29,7 @@
#include "moduleloader.h"
+#include "builtindeclarations.h"
#include "builtinvalue.h"
#include "evaluator.h"
#include "filecontext.h"
@@ -135,23 +136,36 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item)
foreach (Item *child, item->children()) {
child->setScope(projectContext.scope);
- if (child->typeName() == QLatin1String("Product"))
+ if (child->typeName() == QLatin1String("Product")) {
handleProduct(&projectContext, child);
+ } else if (child->typeName() == QLatin1String("SubProject")) {
+ handleSubProject(&projectContext, child);
+ } else if (child->typeName() == QLatin1String("Project")) {
+ copyProperties(item, child);
+ handleProject(loadResult, child);
+ }
}
const QString projectFileDirPath = FileInfo::path(item->file()->filePath());
const QStringList refs = toStringList(m_evaluator->property(item, "references"));
foreach (const QString &filePath, refs) {
const QString absReferencePath = FileInfo::resolvePath(projectFileDirPath, filePath);
- Item *subprj = m_reader->readFile(absReferencePath);
- if (subprj->typeName() != "Product")
- throw Error(Tr::tr("%1 is not a product.").arg(subprj->typeName()), subprj->location());
- subprj->setScope(projectContext.scope);
- subprj->setParent(projectContext.item);
+ Item *subItem = m_reader->readFile(absReferencePath);
+ subItem->setScope(projectContext.scope);
+ subItem->setParent(projectContext.item);
QList<Item *> projectChildren = projectContext.item->children();
- projectChildren += subprj;
+ projectChildren += subItem;
projectContext.item->setChildren(projectChildren);
- handleProduct(&projectContext, subprj);
+ if (subItem->typeName() == "Product") {
+ handleProduct(&projectContext, subItem);
+ } else if (subItem->typeName() == "Project") {
+ copyProperties(item, subItem);
+ handleProject(loadResult, subItem);
+ } else {
+ throw Error(Tr::tr("The top-level item of a file in a \"references\" list must be "
+ "a Product or a Project, but it is \"%1\".").arg(subItem->typeName()),
+ subItem->location());
+ }
}
}
@@ -192,6 +206,59 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item)
projectContext->result->productInfos.insert(item, productContext.info);
}
+void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext, Item *item)
+{
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[MODLDR] handleSubProject " << item->file()->filePath();
+
+ Item *propertiesItem = 0;
+ foreach (Item * const subItem, item->children()) {
+ if (subItem->typeName() == QLatin1String("Properties")) {
+ propertiesItem = subItem;
+ break;
+ }
+ }
+
+ bool subProjectEnabled = true;
+ if (propertiesItem)
+ subProjectEnabled = checkItemCondition(propertiesItem);
+ if (!subProjectEnabled)
+ return;
+
+ const QString projectFileDirPath = FileInfo::path(item->file()->filePath());
+ QString subProjectFilePath = m_evaluator->property(item, "filePath").toString();
+ subProjectFilePath = FileInfo::resolvePath(projectFileDirPath, subProjectFilePath);
+ Item *loadedItem = m_reader->readFile(subProjectFilePath);
+ if (loadedItem->typeName() == QLatin1String("Product"))
+ loadedItem = wrapWithProject(loadedItem);
+ const bool inheritProperties
+ = m_evaluator->boolValue(item, QLatin1String("inheritProperties"), true);
+
+ if (inheritProperties)
+ copyProperties(item->parent(), loadedItem);
+ if (propertiesItem) {
+ const Item::PropertyMap &overriddenProperties = propertiesItem->properties();
+ for (Item::PropertyMap::ConstIterator it = overriddenProperties.constBegin();
+ it != overriddenProperties.constEnd(); ++it) {
+ loadedItem->setProperty(it.key(), overriddenProperties.value(it.key()));
+ }
+ }
+
+ if (loadedItem->typeName() != QLatin1String("Project")) {
+ Error error;
+ error.append(Tr::tr("Expected Project item, but encountered '%1'.")
+ .arg(loadedItem->typeName()), loadedItem->location());
+ const ValuePtr &filePathProperty = item->properties().value(QLatin1String("filePath"));
+ error.append(Tr::tr("The problematic file was referenced from here."),
+ filePathProperty->location());
+ throw error;
+ }
+
+ Item::addChild(item, loadedItem);
+ item->setScope(projectContext->scope);
+ handleProject(projectContext->result, loadedItem);
+}
+
void ModuleLoader::createAdditionalModuleInstancesInProduct(ProductContext *productContext)
{
Item::Modules modulesToCheck;
@@ -723,27 +790,13 @@ void ModuleLoader::checkCancelation() const
{
if (m_progressObserver && m_progressObserver->canceled()) {
throw Error(Tr::tr("Project resolving canceled for configuration %1.")
- .arg(ResolvedProject::deriveId(m_userProperties)));
+ .arg(TopLevelProject::deriveId(m_userProperties)));
}
}
bool ModuleLoader::checkItemCondition(Item *item)
{
- QScriptValue value = m_evaluator->property(item, QLatin1String("condition"));
- if (!value.isValid() || value.isUndefined()) {
- // Item doesn't have a condition binding. Handled as true.
- return true;
- }
- if (Q_UNLIKELY(value.isError())) {
- CodeLocation location;
- ValuePtr prop = item->property("condition");
- if (prop && prop->type() == Value::JSSourceValueType)
- location = prop.staticCast<JSSourceValue>()->location();
- Error e(Tr::tr("Error in condition."), location);
- e.append(value.toString());
- throw e;
- }
- return value.toBool();
+ return m_evaluator->boolValue(item, QLatin1String("condition"), true);
}
QStringList ModuleLoader::readExtraSearchPaths(Item *item)
@@ -755,6 +808,27 @@ QStringList ModuleLoader::readExtraSearchPaths(Item *item)
return result;
}
+void ModuleLoader::copyProperties(const Item *sourceProject, Item *targetProject)
+{
+ if (!sourceProject)
+ return;
+ const QList<PropertyDeclaration> &builtinProjectProperties
+ = m_reader->builtins()->declarationsForType(QLatin1String("Project"));
+ QSet<QString> builtinProjectPropertyNames;
+ foreach (const PropertyDeclaration &p, builtinProjectProperties)
+ builtinProjectPropertyNames << p.name;
+ for (Item::PropertyDeclarationMap::ConstIterator it
+ = sourceProject->propertyDeclarations().constBegin();
+ it != sourceProject->propertyDeclarations().constEnd(); ++it) {
+ if (builtinProjectPropertyNames.contains(it.key()))
+ continue; // Ignore built-ins.
+ if (targetProject->propertyDeclarations().contains(it.key()))
+ continue; // Ignore stuff the target project already has.
+ targetProject->setPropertyDeclaration(it.key(), it.value());
+ copyProperty(it.key(), sourceProject, targetProject);
+ }
+}
+
Item *ModuleLoader::wrapWithProject(Item *item)
{
Item *prj = Item::create(item->pool());
diff --git a/src/lib/language/moduleloader.h b/src/lib/language/moduleloader.h
index 9a0075ba5..8ea9448fe 100644
--- a/src/lib/language/moduleloader.h
+++ b/src/lib/language/moduleloader.h
@@ -145,6 +145,7 @@ private:
void handleProject(ModuleLoaderResult *loadResult, Item *item);
void handleProduct(ProjectContext *projectContext, Item *item);
+ void handleSubProject(ProjectContext *projectContext, Item *item);
void createAdditionalModuleInstancesInProduct(ProductContext *productContext);
void handleGroup(ProductContext *productContext, Item *group);
void handleArtifact(ProductContext *productContext, Item *item);
@@ -171,6 +172,7 @@ private:
void checkCancelation() const;
bool checkItemCondition(Item *item);
QStringList readExtraSearchPaths(Item *item);
+ void copyProperties(const Item *sourceProject, Item *targetProject);
static Item *wrapWithProject(Item *item);
static QString findExistingModulePath(const QString &searchPath,
const QStringList &moduleName);
diff --git a/src/lib/language/projectresolver.cpp b/src/lib/language/projectresolver.cpp
index 85d8df6bc..2444c62a8 100644
--- a/src/lib/language/projectresolver.cpp
+++ b/src/lib/language/projectresolver.cpp
@@ -33,7 +33,6 @@
#include "builtindeclarations.h"
#include "evaluator.h"
#include "filecontext.h"
-#include "item.h"
#include "moduleloader.h"
#include "propertymapinternal.h"
#include "scriptengine.h"
@@ -48,6 +47,7 @@
#include <QFileInfo>
#include <QDir>
+#include <QSet>
#include <set>
namespace qbs {
@@ -67,9 +67,8 @@ ProjectResolver::ProjectResolver(ModuleLoader *ldr, const BuiltinDeclarations *b
, m_logger(logger)
, m_engine(m_evaluator->engine())
, m_progressObserver(0)
+ , m_builtinDeclarations(builtins)
{
- foreach (const PropertyDeclaration &pd, builtins->declarationsForType("Group"))
- m_groupPropertyDeclarations += pd.name;
}
ProjectResolver::~ProjectResolver()
@@ -81,12 +80,12 @@ void ProjectResolver::setProgressObserver(ProgressObserver *observer)
m_progressObserver = observer;
}
-ResolvedProjectPtr ProjectResolver::resolve(ModuleLoaderResult &loadResult,
+TopLevelProjectPtr ProjectResolver::resolve(ModuleLoaderResult &loadResult,
const QString &buildRoot,
const QVariantMap &buildConfiguration,
const QProcessEnvironment &environment)
{
- QBS_ASSERT(FileInfo::isAbsolute(buildRoot), return ResolvedProjectPtr());
+ QBS_ASSERT(FileInfo::isAbsolute(buildRoot), return TopLevelProjectPtr());
if (m_logger.traceEnabled())
m_logger.qbsTrace() << "[PR] resolving " << loadResult.root->file()->filePath();
@@ -95,64 +94,20 @@ ResolvedProjectPtr ProjectResolver::resolve(ModuleLoaderResult &loadResult,
m_buildRoot = buildRoot;
m_buildConfiguration = buildConfiguration;
m_environment = environment;
- m_projectContext = &projectContext;
m_productContext = 0;
m_moduleContext = 0;
- resolveProject(loadResult.root);
- m_projectContext = 0;
- projectContext.project->usedEnvironment = m_engine->usedEnvironment();
- projectContext.project->environment = m_environment;
- return projectContext.project;
+ resolveTopLevelProject(loadResult.root, &projectContext);
+ return projectContext.project.staticCast<TopLevelProject>();
}
void ProjectResolver::checkCancelation() const
{
if (m_progressObserver && m_progressObserver->canceled()) {
throw Error(Tr::tr("Project resolving canceled for configuration %1.")
- .arg(ResolvedProject::deriveId(m_buildConfiguration)));
+ .arg(TopLevelProject::deriveId(m_buildConfiguration)));
}
}
-bool ProjectResolver::boolValue(const Item *item, const QString &name, bool defaultValue) const
-{
- QScriptValue v = m_evaluator->property(item, name);
- if (Q_UNLIKELY(v.isError())) {
- ValuePtr value = item->property(name);
- throw Error(v.toString(), value ? value->location() : CodeLocation());
- }
- if (!v.isValid() || v.isUndefined())
- return defaultValue;
- return v.toBool();
-}
-
-FileTags ProjectResolver::fileTagsValue(const Item *item, const QString &name) const
-{
- return FileTags::fromStringList(stringListValue(item, name));
-}
-
-QString ProjectResolver::stringValue(const Item *item, const QString &name,
- const QString &defaultValue) const
-{
- QScriptValue v = m_evaluator->property(item, name);
- if (Q_UNLIKELY(v.isError())) {
- ValuePtr value = item->property(name);
- throw Error(v.toString(), value ? value->location() : CodeLocation());
- }
- if (!v.isValid() || v.isUndefined())
- return defaultValue;
- return v.toString();
-}
-
-QStringList ProjectResolver::stringListValue(const Item *item, const QString &name) const
-{
- QScriptValue v = m_evaluator->property(item, name);
- if (Q_UNLIKELY(v.isError())) {
- ValuePtr value = item->property(name);
- throw Error(v.toString(), value ? value->location() : CodeLocation());
- }
- return toStringList(v);
-}
-
QString ProjectResolver::verbatimValue(const ValueConstPtr &value) const
{
QString result;
@@ -168,22 +123,66 @@ QString ProjectResolver::verbatimValue(Item *item, const QString &name) const
return verbatimValue(item->property(name));
}
-void ProjectResolver::ignoreItem(Item *item)
+void ProjectResolver::ignoreItem(Item *item, ProjectContext *projectContext)
{
Q_UNUSED(item);
+ Q_UNUSED(projectContext);
}
-void ProjectResolver::resolveProject(Item *item)
+static void makeSubProjectNamesUniqe(const ResolvedProjectPtr &parentProject)
{
- checkCancelation();
+ QSet<QString> subProjectNames;
+ QSet<ResolvedProjectPtr> projectsInNeedOfNameChange;
+ foreach (const ResolvedProjectPtr &p, parentProject->subProjects) {
+ if (subProjectNames.contains(p->name))
+ projectsInNeedOfNameChange << p;
+ else
+ subProjectNames << p->name;
+ makeSubProjectNamesUniqe(p);
+ }
+ while (!projectsInNeedOfNameChange.isEmpty()) {
+ QSet<ResolvedProjectPtr>::Iterator it = projectsInNeedOfNameChange.begin();
+ while (it != projectsInNeedOfNameChange.end()) {
+ const ResolvedProjectPtr p = *it;
+ p->name += QLatin1Char('_');
+ if (!subProjectNames.contains(p->name)) {
+ subProjectNames << p->name;
+ it = projectsInNeedOfNameChange.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+}
- ResolvedProjectPtr project = ResolvedProject::create();
- m_projectContext->project = project;
- m_projectContext->dummyModule = ResolvedModule::create();
- m_projectContext->dummyModule->jsImports = item->file()->jsImports();
- project->location = item->location();
+void ProjectResolver::resolveTopLevelProject(Item *item, ProjectContext *projectContext)
+{
+ const TopLevelProjectPtr project = TopLevelProject::create();
project->setBuildConfiguration(m_buildConfiguration);
- project->buildDirectory = ResolvedProject::deriveBuildDirectory(m_buildRoot, project->id());
+ project->buildDirectory = TopLevelProject::deriveBuildDirectory(m_buildRoot, project->id());
+ projectContext->project = project;
+ resolveProject(item, projectContext);
+ project->usedEnvironment = m_engine->usedEnvironment();
+ project->environment = m_environment;
+ makeSubProjectNamesUniqe(project);
+ resolveProductDependencies(projectContext);
+}
+
+void ProjectResolver::resolveProject(Item *item, ProjectContext *projectContext)
+{
+ checkCancelation();
+
+ projectContext->project->name = m_evaluator->stringValue(item, QLatin1String("name"));
+ projectContext->project->location = item->location();
+ if (projectContext->project->name.isEmpty())
+ projectContext->project->name = FileInfo::baseName(item->location().fileName()); // FIXME: Must also be changed in item?
+ projectContext->project->enabled
+ = m_evaluator->boolValue(item, QLatin1String("condition"), true);
+ if (!projectContext->project->enabled)
+ return;
+
+ projectContext->dummyModule = ResolvedModule::create();
+ projectContext->dummyModule->jsImports = item->file()->jsImports();
m_evaluator->engine()->setEnvironment(m_environment);
@@ -197,32 +196,36 @@ void ProjectResolver::resolveProject(Item *item)
QBS_ASSERT(v && v->type() != Value::ItemValueType, continue);
projectProperties.insert(it.key(), m_evaluator->property(item, it.key()).toVariant());
}
- project->setProjectProperties(projectProperties);
+ projectContext->project->setProjectProperties(projectProperties);
ItemFuncMap mapping;
+ mapping["Project"] = &ProjectResolver::resolveProject;
+ mapping["SubProject"] = &ProjectResolver::resolveSubProject;
mapping["Product"] = &ProjectResolver::resolveProduct;
mapping["FileTagger"] = &ProjectResolver::resolveFileTagger;
mapping["Rule"] = &ProjectResolver::resolveRule;
- if (m_progressObserver)
- m_progressObserver->setMaximum(item->children().count() + 2);
foreach (Item *child, item->children()) {
- callItemFunction(mapping, child);
+ callItemFunction(mapping, child, projectContext);
if (m_progressObserver)
m_progressObserver->incrementProgressValue();
}
- foreach (const ResolvedProductPtr &product, project->products)
- postProcess(product);
- if (m_progressObserver)
- m_progressObserver->incrementProgressValue();
+ foreach (const ResolvedProductPtr &product, projectContext->project->products)
+ postProcess(product, projectContext);
+}
+
+void ProjectResolver::resolveSubProject(Item *item, ProjectResolver::ProjectContext *projectContext)
+{
+ ProjectContext subProjectContext = createProjectContext(projectContext);
- resolveProductDependencies();
- if (m_progressObserver)
- m_progressObserver->incrementProgressValue();
+ foreach (Item * const subItem, item->children()) {
+ if (subItem->typeName() == QLatin1String("Project"))
+ resolveProject(subItem, &subProjectContext);
+ }
}
-void ProjectResolver::resolveProduct(Item *item)
+void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext)
{
checkCancelation();
ProductContext productContext;
@@ -231,28 +234,30 @@ void ProjectResolver::resolveProduct(Item *item)
const QString productSourceDirectory = QFileInfo(item->file()->filePath()).absolutePath();
item->setProperty(QLatin1String("sourceDirectory"),
VariantValue::create(productSourceDirectory));
- item->setProperty(QLatin1String("buildDirectory"),
- VariantValue::create(m_projectContext->project->buildDirectory));
+ item->setProperty(QLatin1String("buildDirectory"), VariantValue::create(projectContext
+ ->project->topLevelProject()->buildDirectory));
ResolvedProductPtr product = ResolvedProduct::create();
- m_projectContext->productItemMap.insert(product, item);
- m_projectContext->project->products += product;
+ product->project = projectContext->project;
+ m_productItemMap.insert(product, item);
+ projectContext->project->products += product;
productContext.product = product;
- product->name = stringValue(item, QLatin1String("name"));
+ product->name = m_evaluator->stringValue(item, QLatin1String("name"));
if (product->name.isEmpty()) {
product->name = FileInfo::completeBaseName(item->file()->filePath());
item->setProperty("name", VariantValue::create(product->name));
}
m_logger.qbsTrace() << "[PR] resolveProduct " << product->name;
ModuleLoader::overrideItemProperties(item, product->name, m_buildConfiguration);
- m_projectContext->productsByName.insert(product->name, product);
- product->enabled = boolValue(item, QLatin1String("condition"), true);
- product->additionalFileTags = fileTagsValue(item, QLatin1String("additionalFileTags"));
- product->fileTags = fileTagsValue(item, QLatin1String("type"));
- product->targetName = stringValue(item, QLatin1String("targetName"));
+ m_productsByName.insert(product->name, product);
+ product->enabled = m_evaluator->boolValue(item, QLatin1String("condition"), true);
+ product->additionalFileTags
+ = m_evaluator->fileTagsValue(item, QLatin1String("additionalFileTags"));
+ product->fileTags = m_evaluator->fileTagsValue(item, QLatin1String("type"));
+ product->targetName = m_evaluator->stringValue(item, QLatin1String("targetName"));
product->sourceDirectory = productSourceDirectory;
- product->destinationDirectory = stringValue(item, QLatin1String("destinationDirectory"));
+ product->destinationDirectory
+ = m_evaluator->stringValue(item, QLatin1String("destinationDirectory"));
product->location = item->location();
- product->project = m_projectContext->project;
product->properties = PropertyMapInternal::create();
product->properties->setValue(createProductConfig());
ModuleProperties::init(m_evaluator->scriptValue(item), product);
@@ -283,15 +288,16 @@ void ProjectResolver::resolveProduct(Item *item)
mapping["Probe"] = &ProjectResolver::ignoreItem;
foreach (Item *child, subItems)
- callItemFunction(mapping, child);
+ callItemFunction(mapping, child, projectContext);
foreach (const Item::Module &module, item->modules())
- resolveModule(module.name, module.item);
+ resolveModule(module.name, module.item, projectContext);
m_productContext = 0;
}
-void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item)
+void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item,
+ ProjectContext *projectContext)
{
checkCancelation();
ModuleContext moduleContext;
@@ -304,7 +310,7 @@ void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item)
module->setupRunEnvironmentScript = verbatimValue(item, "setupRunEnvironment");
m_productContext->product->additionalFileTags
- += fileTagsValue(item, "additionalProductFileTags");
+ += m_evaluator->fileTagsValue(item, "additionalProductFileTags");
// TODO: instead of jsImports, we need the file context for both setup scripts separately.
// ATM the setup scripts must be in the first file of the inheritance chain.
@@ -323,7 +329,7 @@ void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item)
mapping["Depends"] = &ProjectResolver::ignoreItem;
mapping["Probe"] = &ProjectResolver::ignoreItem;
foreach (Item *child, item->children())
- callItemFunction(mapping, child);
+ callItemFunction(mapping, child, projectContext);
m_moduleContext = 0;
}
@@ -360,8 +366,9 @@ static bool isSomeModulePropertySet(Item *group)
return false;
}
-void ProjectResolver::resolveGroup(Item *item)
+void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext)
{
+ Q_UNUSED(projectContext);
checkCancelation();
PropertyMapPtr properties = m_productContext->product->properties;
if (isSomeModulePropertySet(item)) {
@@ -369,8 +376,9 @@ void ProjectResolver::resolveGroup(Item *item)
properties->setValue(evaluateModuleValues(item));
}
- QStringList files = stringListValue(item, QLatin1String("files"));
- const QStringList fileTagsFilter = stringListValue(item, QLatin1String("fileTagsFilter"));
+ QStringList files = m_evaluator->stringListValue(item, QLatin1String("files"));
+ const QStringList fileTagsFilter
+ = m_evaluator->stringListValue(item, QLatin1String("fileTagsFilter"));
if (!fileTagsFilter.isEmpty()) {
if (Q_UNLIKELY(!files.isEmpty()))
throw Error(Tr::tr("Group.files and Group.fileTagsFilters are exclusive."),
@@ -395,21 +403,21 @@ void ProjectResolver::resolveGroup(Item *item)
if (FileInfo::isPattern(files[i]))
patterns.append(files.takeAt(i));
}
- prefix = stringValue(item, QLatin1String("prefix"));
+ prefix = m_evaluator->stringValue(item, QLatin1String("prefix"));
if (!prefix.isEmpty()) {
for (int i = files.count(); --i >= 0;)
files[i].prepend(prefix);
}
- FileTags fileTags = fileTagsValue(item, QLatin1String("fileTags"));
- bool overrideTags = boolValue(item, QLatin1String("overrideTags"), true);
+ FileTags fileTags = m_evaluator->fileTagsValue(item, QLatin1String("fileTags"));
+ bool overrideTags = m_evaluator->boolValue(item, QLatin1String("overrideTags"), true);
GroupPtr group = ResolvedGroup::create();
group->location = item->location();
- group->enabled = boolValue(item, QLatin1String("condition"), true);
+ group->enabled = m_evaluator->boolValue(item, QLatin1String("condition"), true);
if (!patterns.isEmpty()) {
SourceWildCards::Ptr wildcards = SourceWildCards::create();
- wildcards->excludePatterns = stringListValue(item, "excludeFiles");
+ wildcards->excludePatterns = m_evaluator->stringListValue(item, "excludeFiles");
wildcards->prefix = prefix;
wildcards->patterns = patterns;
QSet<QString> files = wildcards->expandPatterns(group, m_productContext->product->sourceDirectory);
@@ -432,7 +440,7 @@ void ProjectResolver::resolveGroup(Item *item)
if (!fileError.entries().isEmpty())
throw Error(fileError);
- group->name = stringValue(item, "name");
+ group->name = m_evaluator->stringValue(item, "name");
if (group->name.isEmpty())
group->name = Tr::tr("Group %1").arg(m_productContext->product->groups.count());
group->properties = properties;
@@ -451,11 +459,11 @@ static QString sourceCodeAsFunction(const JSSourceValueConstPtr &value)
}
}
-void ProjectResolver::resolveRule(Item *item)
+void ProjectResolver::resolveRule(Item *item, ProjectContext *projectContext)
{
checkCancelation();
- if (!boolValue(item, QLatin1String("condition"), true))
+ if (!m_evaluator->boolValue(item, QLatin1String("condition"), true))
return;
RulePtr rule = Rule::create();
@@ -484,15 +492,15 @@ void ProjectResolver::resolveRule(Item *item)
rule->jsImports = item->file()->jsImports();
rule->script = prepareScript;
- rule->multiplex = boolValue(item, "multiplex", false);
- rule->inputs = fileTagsValue(item, "inputs");
- rule->usings = fileTagsValue(item, "usings");
- rule->explicitlyDependsOn = fileTagsValue(item, "explicitlyDependsOn");
- rule->module = m_moduleContext ? m_moduleContext->module : m_projectContext->dummyModule;
+ rule->multiplex = m_evaluator->boolValue(item, "multiplex", false);
+ rule->inputs = m_evaluator->fileTagsValue(item, "inputs");
+ rule->usings = m_evaluator->fileTagsValue(item, "usings");
+ rule->explicitlyDependsOn = m_evaluator->fileTagsValue(item, "explicitlyDependsOn");
+ rule->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
if (m_productContext)
m_productContext->product->rules += rule;
else
- m_projectContext->rules += rule;
+ projectContext->rules += rule;
}
class StringListLess
@@ -524,8 +532,8 @@ void ProjectResolver::resolveRuleArtifact(const RulePtr &rule, Item *item,
RuleArtifactPtr artifact = RuleArtifact::create();
rule->artifacts += artifact;
artifact->fileName = verbatimValue(item, "fileName");
- artifact->fileTags = fileTagsValue(item, "fileTags");
- artifact->alwaysUpdated = boolValue(item, "alwaysUpdated", true);
+ artifact->fileTags = m_evaluator->fileTagsValue(item, "fileTags");
+ artifact->alwaysUpdated = m_evaluator->boolValue(item, "alwaysUpdated", true);
if (artifact->alwaysUpdated)
*hasAlwaysUpdatedArtifact = true;
@@ -571,27 +579,27 @@ void ProjectResolver::resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArti
}
}
-void ProjectResolver::resolveFileTagger(Item *item)
+void ProjectResolver::resolveFileTagger(Item *item, ProjectContext *projectContext)
{
checkCancelation();
QSet<FileTaggerConstPtr> &fileTaggers = m_productContext
- ? m_productContext->product->fileTaggers : m_projectContext->fileTaggers;
- fileTaggers += FileTagger::create(QRegExp(stringValue(item,"pattern")),
- fileTagsValue(item, "fileTags"));
+ ? m_productContext->product->fileTaggers : projectContext->fileTaggers;
+ fileTaggers += FileTagger::create(QRegExp(m_evaluator->stringValue(item,"pattern")),
+ m_evaluator->fileTagsValue(item, "fileTags"));
}
-void ProjectResolver::resolveTransformer(Item *item)
+void ProjectResolver::resolveTransformer(Item *item, ProjectContext *projectContext)
{
checkCancelation();
- if (!boolValue(item, "condition", true)) {
+ if (!m_evaluator->boolValue(item, "condition", true)) {
m_logger.qbsTrace() << "[PR] transformer condition is false";
return;
}
ResolvedTransformer::Ptr rtrafo = ResolvedTransformer::create();
- rtrafo->module = m_moduleContext ? m_moduleContext->module : m_projectContext->dummyModule;
+ rtrafo->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
rtrafo->jsImports = item->file()->jsImports();
- rtrafo->inputs = stringListValue(item, "inputs");
+ rtrafo->inputs = m_evaluator->stringListValue(item, "inputs");
for (int i = 0; i < rtrafo->inputs.count(); ++i)
rtrafo->inputs[i] = FileInfo::resolvePath(m_productContext->product->sourceDirectory, rtrafo->inputs.at(i));
const PrepareScriptPtr transform = PrepareScript::create();
@@ -605,12 +613,12 @@ void ProjectResolver::resolveTransformer(Item *item)
throw Error(Tr::tr("Transformer: wrong child type '%0'.").arg(child->typeName()));
SourceArtifactPtr artifact = SourceArtifact::create();
artifact->properties = m_productContext->product->properties;
- QString fileName = stringValue(child, "fileName");
+ QString fileName = m_evaluator->stringValue(child, "fileName");
if (Q_UNLIKELY(fileName.isEmpty()))
throw Error(Tr::tr("Artifact fileName must not be empty."));
- artifact->absoluteFilePath = FileInfo::resolvePath(m_productContext->product->project->buildDirectory,
+ artifact->absoluteFilePath = FileInfo::resolvePath(m_productContext->product->topLevelProject()->buildDirectory,
fileName);
- artifact->fileTags = fileTagsValue(child, "fileTags");
+ artifact->fileTags = m_evaluator->fileTagsValue(child, "fileTags");
if (artifact->fileTags.isEmpty())
artifact->fileTags.insert(unknownFileTag());
rtrafo->outputs += artifact;
@@ -619,11 +627,12 @@ void ProjectResolver::resolveTransformer(Item *item)
m_productContext->product->transformers += rtrafo;
}
-void ProjectResolver::resolveExport(Item *item)
+void ProjectResolver::resolveExport(Item *item, ProjectContext *projectContext)
{
+ Q_UNUSED(projectContext);
checkCancelation();
const QString &productName = m_productContext->product->name;
- m_projectContext->exports[productName] = evaluateModuleValues(item);
+ m_exports[productName] = evaluateModuleValues(item);
}
static void insertExportedConfig(const QString &usedProductName,
@@ -655,26 +664,27 @@ static void addUsedProducts(ModuleLoaderResult::ProductInfo *productInfo,
*productsAdded = (oldCount != productInfo->usedProducts.count());
}
-void ProjectResolver::resolveProductDependencies()
+void ProjectResolver::resolveProductDependencies(ProjectContext *projectContext)
{
// Collect product dependencies from Export items.
bool productDependenciesAdded;
+ QList<ResolvedProductPtr> allProducts = projectContext->project->allProducts();
do {
productDependenciesAdded = false;
- foreach (ResolvedProductPtr rproduct, m_projectContext->project->products) {
- Item *productItem = m_projectContext->productItemMap.value(rproduct);
+ foreach (const ResolvedProductPtr &rproduct, allProducts) {
+ Item *productItem = m_productItemMap.value(rproduct);
ModuleLoaderResult::ProductInfo &productInfo
- = m_projectContext->loadResult->productInfos[productItem];
+ = projectContext->loadResult->productInfos[productItem];
foreach (const ModuleLoaderResult::ProductInfo::Dependency &dependency,
productInfo.usedProducts) {
ResolvedProductPtr usedProduct
- = m_projectContext->productsByName.value(dependency.name);
+ = m_productsByName.value(dependency.name);
if (Q_UNLIKELY(!usedProduct))
throw Error(Tr::tr("Product dependency '%1' not found.").arg(dependency.name),
productItem->location());
- Item *usedProductItem = m_projectContext->productItemMap.value(usedProduct);
+ Item *usedProductItem = m_productItemMap.value(usedProduct);
const ModuleLoaderResult::ProductInfo usedProductInfo
- = m_projectContext->loadResult->productInfos.value(usedProductItem);
+ = projectContext->loadResult->productInfos.value(usedProductItem);
bool added;
addUsedProducts(&productInfo, usedProductInfo, &added);
if (added)
@@ -684,21 +694,19 @@ void ProjectResolver::resolveProductDependencies()
} while (productDependenciesAdded);
// Resolve all inter-product dependencies.
- foreach (ResolvedProductPtr rproduct, m_projectContext->project->products) {
- Item *productItem = m_projectContext->productItemMap.value(rproduct);
+ foreach (const ResolvedProductPtr &rproduct, allProducts) {
+ Item *productItem = m_productItemMap.value(rproduct);
foreach (const ModuleLoaderResult::ProductInfo::Dependency &dependency,
- m_projectContext->loadResult->productInfos.value(productItem).usedProducts) {
+ projectContext->loadResult->productInfos.value(productItem).usedProducts) {
const QString &usedProductName = dependency.name;
- ResolvedProductPtr usedProduct
- = m_projectContext->productsByName.value(usedProductName);
+ ResolvedProductPtr usedProduct = m_productsByName.value(usedProductName);
if (Q_UNLIKELY(!usedProduct))
throw Error(Tr::tr("Product dependency '%1' not found.").arg(usedProductName),
productItem->location());
rproduct->dependencies.insert(usedProduct);
// insert the configuration of the Export item into the product's configuration
- const QVariantMap exportedConfig
- = m_projectContext->exports.value(usedProductName);
+ const QVariantMap exportedConfig = m_exports.value(usedProductName);
if (exportedConfig.isEmpty())
continue;
@@ -714,10 +722,11 @@ void ProjectResolver::resolveProductDependencies()
}
}
-void ProjectResolver::postProcess(const ResolvedProductPtr &product) const
+void ProjectResolver::postProcess(const ResolvedProductPtr &product,
+ ProjectContext *projectContext) const
{
- product->fileTaggers += m_projectContext->fileTaggers;
- foreach (const RulePtr &rule, m_projectContext->rules)
+ product->fileTaggers += projectContext->fileTaggers;
+ foreach (const RulePtr &rule, projectContext->rules)
product->rules += rule;
applyFileTaggers(product);
}
@@ -855,8 +864,8 @@ QStringList ProjectResolver::convertPathListProperty(const QStringList &paths,
return result;
}
-void ProjectResolver::callItemFunction(const ItemFuncMap &mappings,
- Item *item)
+void ProjectResolver::callItemFunction(const ItemFuncMap &mappings, Item *item,
+ ProjectContext *projectContext)
{
const QByteArray typeName = item->typeName().toLocal8Bit();
ItemFuncPtr f = mappings.value(typeName);
@@ -864,7 +873,22 @@ void ProjectResolver::callItemFunction(const ItemFuncMap &mappings,
const QString msg = Tr::tr("Unexpected item type '%1'.");
throw Error(msg.arg(item->typeName()), item->location());
}
- (this->*f)(item);
+ if (typeName == "Project") {
+ ProjectContext subProjectContext = createProjectContext(projectContext);
+ (this->*f)(item, &subProjectContext);
+ } else {
+ (this->*f)(item, projectContext);
+ }
+}
+
+ProjectResolver::ProjectContext ProjectResolver::createProjectContext(ProjectContext *parentProjectContext) const
+{
+ ProjectContext subProjectContext;
+ subProjectContext.project = ResolvedProject::create();
+ parentProjectContext->project->subProjects += subProjectContext.project;
+ subProjectContext.project->parentProject = parentProjectContext->project;
+ subProjectContext.loadResult = parentProjectContext->loadResult;
+ return subProjectContext;
}
} // namespace Internal
diff --git a/src/lib/language/projectresolver.h b/src/lib/language/projectresolver.h
index bf4350ceb..dacc839ed 100644
--- a/src/lib/language/projectresolver.h
+++ b/src/lib/language/projectresolver.h
@@ -32,6 +32,7 @@
#include "evaluator.h"
#include "filetags.h"
+#include "item.h"
#include "language.h"
#include <logging/logger.h>
@@ -56,7 +57,7 @@ public:
~ProjectResolver();
void setProgressObserver(ProgressObserver *observer);
- ResolvedProjectPtr resolve(ModuleLoaderResult &loadResult, const QString &buildRoot,
+ TopLevelProjectPtr resolve(ModuleLoaderResult &loadResult, const QString &buildRoot,
const QVariantMap &buildConfiguration,
const QProcessEnvironment &environment);
@@ -68,9 +69,6 @@ private:
ModuleLoaderResult *loadResult;
QList<RulePtr> rules;
ResolvedModulePtr dummyModule;
- QMap<QString, ResolvedProductPtr> productsByName;
- QHash<ResolvedProductPtr, Item *> productItemMap;
- QMap<QString, QVariantMap> exports;
};
struct ProductContext
@@ -85,27 +83,25 @@ private:
};
void checkCancelation() const;
- bool boolValue(const Item *item, const QString &name, bool defaultValue = false) const;
- FileTags fileTagsValue(const Item *item, const QString &name) const;
- QString stringValue(const Item *item, const QString &name, const QString &defaultValue = QString()) const;
- QStringList stringListValue(const Item *item, const QString &name) const;
QString verbatimValue(const ValueConstPtr &value) const;
QString verbatimValue(Item *item, const QString &name) const;
- void ignoreItem(Item *item);
- void resolveProject(Item *item);
- void resolveProduct(Item *item);
- void resolveModule(const QStringList &moduleName, Item *item);
- void resolveGroup(Item *item);
- void resolveRule(Item *item);
+ void ignoreItem(Item *item, ProjectContext *projectContext);
+ void resolveTopLevelProject(Item *item, ProjectContext *projectContext);
+ void resolveProject(Item *item, ProjectContext *projectContext);
+ void resolveSubProject(Item *item, ProjectContext *projectContext);
+ void resolveProduct(Item *item, ProjectContext *projectContext);
+ void resolveModule(const QStringList &moduleName, Item *item, ProjectContext *projectContext);
+ void resolveGroup(Item *item, ProjectContext *projectContext);
+ void resolveRule(Item *item, ProjectContext *projectContext);
void resolveRuleArtifact(const RulePtr &rule, Item *item, bool *hasAlwaysUpdatedArtifact);
static void resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact, Item *item,
const QStringList &namePrefix,
StringListSet *seenBindings);
- void resolveFileTagger(Item *item);
- void resolveTransformer(Item *item);
- void resolveExport(Item *item);
- void resolveProductDependencies();
- void postProcess(const ResolvedProductPtr &product) const;
+ void resolveFileTagger(Item *item, ProjectContext *projectContext);
+ void resolveTransformer(Item *item, ProjectContext *projectContext);
+ void resolveExport(Item *item, ProjectContext *projectContext);
+ void resolveProductDependencies(ProjectContext *projectContext);
+ void postProcess(const ResolvedProductPtr &product, ProjectContext *projectContext) const;
void applyFileTaggers(const ResolvedProductPtr &product) const;
void applyFileTaggers(const SourceArtifactPtr &artifact,
const ResolvedProductConstPtr &product) const;
@@ -117,6 +113,7 @@ private:
QVariantMap createProductConfig() const;
QString convertPathProperty(const QString &path, const QString &dirPath) const;
QStringList convertPathListProperty(const QStringList &paths, const QString &dirPath) const;
+ ProjectContext createProjectContext(ProjectContext *parentProjectContext) const;
Evaluator *m_evaluator;
Logger m_logger;
@@ -124,15 +121,17 @@ private:
ProgressObserver *m_progressObserver;
QString m_buildRoot;
QVariantMap m_buildConfiguration;
- ProjectContext *m_projectContext;
ProductContext *m_productContext;
ModuleContext *m_moduleContext;
- QSet<QString> m_groupPropertyDeclarations;
QProcessEnvironment m_environment;
+ QMap<QString, ResolvedProductPtr> m_productsByName;
+ QHash<ResolvedProductPtr, Item *> m_productItemMap;
+ QMap<QString, QVariantMap> m_exports;
+ const BuiltinDeclarations * const m_builtinDeclarations;
- typedef void (ProjectResolver::*ItemFuncPtr)(Item *item);
+ typedef void (ProjectResolver::*ItemFuncPtr)(Item *item, ProjectContext *projectContext);
typedef QMap<QByteArray, ItemFuncPtr> ItemFuncMap;
- void callItemFunction(const ItemFuncMap &mappings, Item *item);
+ void callItemFunction(const ItemFuncMap &mappings, Item *item, ProjectContext *projectContext);
};
} // namespace Internal
diff --git a/src/lib/language/tst_language.cpp b/src/lib/language/tst_language.cpp
index 23a01d062..49f5ade73 100644
--- a/src/lib/language/tst_language.cpp
+++ b/src/lib/language/tst_language.cpp
@@ -314,7 +314,7 @@ void TestLanguage::exports()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("exports.qbs"));
- ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
QVERIFY(project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.count(), 8);
@@ -431,7 +431,7 @@ void TestLanguage::groupName()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("groupname.qbs"));
- ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
QVERIFY(project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.count(), 2);
@@ -575,7 +575,7 @@ void TestLanguage::idUsage()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("idusage.qbs"));
- ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
QVERIFY(project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.count(), 3);
@@ -686,7 +686,7 @@ void TestLanguage::jsImportUsedInMultipleScopes()
SetupProjectParameters params = defaultParameters;
params.setProjectFilePath(testProject("jsimportsinmultiplescopes.qbs"));
params.setBuildConfiguration(customBuildConfig);
- ResolvedProjectPtr project = loader->loadProject(params);
+ TopLevelProjectPtr project = loader->loadProject(params);
QVERIFY(project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.count(), 1);
@@ -755,7 +755,7 @@ void TestLanguage::moduleScope()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("modulescope.qbs"));
- ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
QVERIFY(project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.count(), 1);
@@ -827,7 +827,7 @@ void TestLanguage::outerInGroup()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("outerInGroup.qbs"));
- ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
QVERIFY(project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.count(), 1);
@@ -892,7 +892,7 @@ void TestLanguage::productConditions()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("productconditions.qbs"));
- ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
QVERIFY(project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.count(), 4);
diff --git a/src/lib/language/tst_language.h b/src/lib/language/tst_language.h
index 3784ccba0..e820c3224 100644
--- a/src/lib/language/tst_language.h
+++ b/src/lib/language/tst_language.h
@@ -52,7 +52,7 @@ private:
Logger m_logger;
ScriptEngine *m_engine;
Loader *loader;
- ResolvedProjectPtr project;
+ TopLevelProjectPtr project;
SetupProjectParameters defaultParameters;
const QString m_wildcardsTestDirPath;
diff --git a/src/lib/tools/persistence.cpp b/src/lib/tools/persistence.cpp
index 4a22e2eaa..1e93e0950 100644
--- a/src/lib/tools/persistence.cpp
+++ b/src/lib/tools/persistence.cpp
@@ -39,7 +39,7 @@
namespace qbs {
namespace Internal {
-static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-40";
+static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-41";
PersistentPool::PersistentPool(const Logger &logger) : m_logger(logger)
{
diff --git a/src/lib/tools/weakpointer.h b/src/lib/tools/weakpointer.h
index 2b6e820e7..a8a82a5dd 100644
--- a/src/lib/tools/weakpointer.h
+++ b/src/lib/tools/weakpointer.h
@@ -39,6 +39,8 @@ template<typename T> class WeakPointer : public QWeakPointer<T>
public:
WeakPointer() : QWeakPointer<T>() {}
WeakPointer(const QSharedPointer<T> &sharedPointer) : QWeakPointer<T>(sharedPointer) {}
+ template <class X> WeakPointer(const QSharedPointer<X> &sp) : QWeakPointer<T>(sp) { }
+
operator T*() const { return checkedData(); }
T *operator->() const { return checkedData(); }