diff options
author | Christian Kandeler <christian.kandeler@digia.com> | 2013-06-06 10:06:30 +0200 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@digia.com> | 2013-06-17 14:23:19 +0200 |
commit | ab60f6e4c489ace089785c002d75018ac6a31e30 (patch) | |
tree | 91053ec752d9aa56df8061ba5e62c13431804a6b /src/lib | |
parent | d3877c67d060d454ac66fde7b6ad41a2bb50d808 (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/lib')
47 files changed, 872 insertions, 427 deletions
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 ¶meters); 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 ¶meters, 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 ¶meters, - 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 ¶met 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 ¶met } } - if (!environmentChanged && !projectFileChanged && !referencedProductRemoved + if (!environmentChanged && !projectFileChanged && !subProjectRemoved && !productRemoved && changedProducts.isEmpty()) { return; } @@ -576,11 +652,13 @@ void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters ¶met 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 ¶met } 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 ¶met 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 ¶meters, 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 ¶meters) +TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters ¶meters) { QBS_CHECK(QFileInfo(parameters.projectFilePath()).isAbsolute()); @@ -92,7 +92,7 @@ ResolvedProjectPtr Loader::loadProject(const SetupProjectParameters ¶meters) // 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 ¶meters); + TopLevelProjectPtr loadProject(const SetupProjectParameters ¶meters); 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(); } |