1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qbs.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "language.h"
41 
42 #include "artifactproperties.h"
43 #include "builtindeclarations.h"
44 #include "propertymapinternal.h"
45 #include "scriptengine.h"
46 
47 #include <buildgraph/artifact.h>
48 #include <buildgraph/buildgraph.h>
49 #include <buildgraph/productbuilddata.h>
50 #include <buildgraph/projectbuilddata.h>
51 #include <buildgraph/rulegraph.h> // TODO: Move to language?
52 #include <buildgraph/transformer.h>
53 #include <jsextensions/jsextensions.h>
54 #include <logging/categories.h>
55 #include <logging/translator.h>
56 #include <tools/buildgraphlocker.h>
57 #include <tools/hostosinfo.h>
58 #include <tools/error.h>
59 #include <tools/fileinfo.h>
60 #include <tools/scripttools.h>
61 #include <tools/qbsassert.h>
62 #include <tools/qttools.h>
63 #include <tools/stringconstants.h>
64 
65 #include <QtCore/qcryptographichash.h>
66 #include <QtCore/qdir.h>
67 #include <QtCore/qdiriterator.h>
68 #include <QtCore/qmap.h>
69 
70 #include <QtScript/qscriptvalue.h>
71 
72 #include <algorithm>
73 #include <memory>
74 #include <mutex>
75 
76 namespace qbs {
77 namespace Internal {
78 
equals(const T * v1,const T * v2)79 template<typename T> bool equals(const T *v1, const T *v2)
80 {
81     if (v1 == v2)
82         return true;
83     if (!v1 != !v2)
84         return false;
85     return *v1 == *v2;
86 }
87 
88 
89 /*!
90  * \class FileTagger
91  * \brief The \c FileTagger class maps 1:1 to the respective item in a qbs source file.
92  */
93 
FileTagger(const QStringList & patterns,FileTags fileTags,int priority)94 FileTagger::FileTagger(const QStringList &patterns, FileTags fileTags, int priority)
95     : m_fileTags(std::move(fileTags)), m_priority(priority)
96 {
97     setPatterns(patterns);
98 }
99 
setPatterns(const QStringList & patterns)100 void FileTagger::setPatterns(const QStringList &patterns)
101 {
102     m_patterns.clear();
103     for (const QString &pattern : patterns) {
104         QBS_CHECK(!pattern.isEmpty());
105         m_patterns << QRegularExpression(QRegularExpression::wildcardToRegularExpression(pattern));
106     }
107 }
108 
109 
needsReconfigure(const FileTime & referenceTime) const110 bool Probe::needsReconfigure(const FileTime &referenceTime) const
111 {
112     const auto criterion = [referenceTime](const QString &filePath) {
113         FileInfo fi(filePath);
114         return !fi.exists() || fi.lastModified() > referenceTime;
115     };
116     return std::any_of(m_importedFilesUsed.cbegin(), m_importedFilesUsed.cend(), criterion);
117 }
118 
119 
120 /*!
121  * \class SourceArtifact
122  * \brief The \c SourceArtifact class represents a source file.
123  * Everything except the file path is inherited from the surrounding \c ResolvedGroup.
124  * (TODO: Not quite true. Artifacts in transformers will be generated by the transformer, but are
125  * still represented as source artifacts. We may or may not want to change this; if we do,
126  * SourceArtifact could simply have a back pointer to the group in addition to the file path.)
127  * \sa ResolvedGroup
128  */
129 
130 
131 /*!
132  * \class ResolvedGroup
133  * \brief The \c ResolvedGroup class corresponds to the Group item in a qbs source file.
134  */
135 
136  /*!
137   * \variable ResolvedGroup::files
138   * \brief The files listed in the group item's "files" binding.
139   * Note that these do not include expanded wildcards.
140   */
141 
142 /*!
143  * \variable ResolvedGroup::wildcards
144  * \brief Represents the wildcard elements in this group's "files" binding.
145  *  If no wildcards are specified there, this variable is null.
146  * \sa SourceWildCards
147  */
148 
149 /*!
150  * \brief Returns all files specified in the group item as source artifacts.
151  * This includes the expanded list of wildcards.
152  */
allFiles() const153 std::vector<SourceArtifactPtr> ResolvedGroup::allFiles() const
154 {
155     std::vector<SourceArtifactPtr> lst = files;
156     if (wildcards)
157         lst << wildcards->files;
158     return lst;
159 }
160 
load(PersistentPool & pool)161 void ResolvedGroup::load(PersistentPool &pool)
162 {
163     serializationOp<PersistentPool::Load>(pool);
164     if (wildcards)
165         wildcards->group = this;
166 }
167 
store(PersistentPool & pool)168 void ResolvedGroup::store(PersistentPool &pool)
169 {
170     serializationOp<PersistentPool::Store>(pool);
171 }
172 
173 /*!
174  * \class RuleArtifact
175  * \brief The \c RuleArtifact class represents an Artifact item encountered in the context
176  *        of a Rule item.
177  * When applying the rule, one \c Artifact object will be constructed from each \c RuleArtifact
178  * object. During that process, the \c RuleArtifact's bindings are evaluated and the results
179  * are inserted into the corresponding \c Artifact's properties.
180  * \sa Rule
181  */
182 
183 
184 /*!
185  * \class ScriptFunction
186  * \brief The \c ScriptFunction class represents the JavaScript code found in the "prepare" binding
187  *        of a \c Rule item in a qbs file.
188  * \sa Rule
189  */
190 
191 ScriptFunction::ScriptFunction() = default;
192 
193 ScriptFunction::~ScriptFunction() = default;
194 
195  /*!
196   * \variable ScriptFunction::script
197   * \brief The actual Javascript code, taken verbatim from the qbs source file.
198   */
199 
200   /*!
201    * \variable ScriptFunction::location
202    * \brief The exact location of the script in the qbs source file.
203    * This is mostly needed for diagnostics.
204    */
205 
isValid() const206 bool ScriptFunction::isValid() const
207 {
208     return location.line() != -1;
209 }
210 
operator ==(const ScriptFunction & a,const ScriptFunction & b)211 bool operator==(const ScriptFunction &a, const ScriptFunction &b)
212 {
213     return a.sourceCode == b.sourceCode
214             && a.location == b.location
215             && equals(a.fileContext.get(), b.fileContext.get());
216 }
217 
argumentNamesForSetupBuildEnv()218 QStringList ResolvedModule::argumentNamesForSetupBuildEnv()
219 {
220     static const QStringList argNames = BuiltinDeclarations::instance()
221             .argumentNamesForScriptFunction(ItemType::Module,
222                                             StringConstants::setupBuildEnvironmentProperty());
223     return argNames;
224 }
225 
argumentNamesForSetupRunEnv()226 QStringList ResolvedModule::argumentNamesForSetupRunEnv()
227 {
228     static const QStringList argNames = BuiltinDeclarations::instance()
229             .argumentNamesForScriptFunction(ItemType::Module,
230                                             StringConstants::setupRunEnvironmentProperty());
231     return argNames;
232 }
233 
operator ==(const ResolvedModule & m1,const ResolvedModule & m2)234 bool operator==(const ResolvedModule &m1, const ResolvedModule &m2)
235 {
236     return m1.name == m2.name
237             && m1.isProduct == m2.isProduct
238             && toSet(m1.moduleDependencies) == toSet(m2.moduleDependencies)
239             && m1.setupBuildEnvironmentScript == m2.setupBuildEnvironmentScript
240             && m1.setupRunEnvironmentScript == m2.setupRunEnvironmentScript;
241 }
242 
clone() const243 RulePtr Rule::clone() const
244 {
245     return std::make_shared<Rule>(*this);
246 }
247 
argumentNamesForOutputArtifacts()248 QStringList Rule::argumentNamesForOutputArtifacts()
249 {
250     static const QStringList argNames = BuiltinDeclarations::instance()
251             .argumentNamesForScriptFunction(ItemType::Rule,
252                                             StringConstants::outputArtifactsProperty());
253     return argNames;
254 }
255 
argumentNamesForPrepare()256 QStringList Rule::argumentNamesForPrepare()
257 {
258     static const QStringList argNames = BuiltinDeclarations::instance()
259             .argumentNamesForScriptFunction(ItemType::Rule, StringConstants::prepareProperty());
260     return argNames;
261 }
262 
toString() const263 QString Rule::toString() const
264 {
265     QStringList outputTagsSorted = collectedOutputFileTags().toStringList();
266     outputTagsSorted.sort();
267     FileTags inputTags = inputs;
268     inputTags.unite(inputsFromDependencies);
269     QStringList inputTagsSorted = inputTags.toStringList();
270     inputTagsSorted.sort();
271     return QLatin1Char('[') + outputTagsSorted.join(QLatin1Char(','))
272             + QLatin1String("][")
273             + inputTagsSorted.join(QLatin1Char(',')) + QLatin1Char(']');
274 }
275 
staticOutputFileTags() const276 FileTags Rule::staticOutputFileTags() const
277 {
278     FileTags result;
279     for (const auto &artifact : artifacts)
280         result.unite(artifact->fileTags);
281     return result;
282 }
283 
collectedOutputFileTags() const284 FileTags Rule::collectedOutputFileTags() const
285 {
286     FileTags result = outputFileTags.empty() ? staticOutputFileTags() : outputFileTags;
287     for (const auto &ap : product->artifactProperties) {
288         if (ap->fileTagsFilter().intersects(result))
289             result += ap->extraFileTags();
290     }
291     return result;
292 }
293 
isDynamic() const294 bool Rule::isDynamic() const
295 {
296     return outputArtifactsScript.isValid();
297 }
298 
declaresInputs() const299 bool Rule::declaresInputs() const
300 {
301     return !inputs.empty() || !inputsFromDependencies.empty();
302 }
303 
ResolvedProduct()304 ResolvedProduct::ResolvedProduct()
305     : enabled(true)
306 {
307 }
308 
309 ResolvedProduct::~ResolvedProduct() = default;
310 
accept(BuildGraphVisitor * visitor) const311 void ResolvedProduct::accept(BuildGraphVisitor *visitor) const
312 {
313     if (!buildData)
314         return;
315     for (BuildGraphNode * const node : qAsConst(buildData->rootNodes()))
316         node->accept(visitor);
317 }
318 
319 /*!
320  * \brief Returns all files of all groups as source artifacts.
321  * This includes the expanded list of wildcards.
322  */
allFiles() const323 std::vector<SourceArtifactPtr> ResolvedProduct::allFiles() const
324 {
325     std::vector<SourceArtifactPtr> lst;
326     for (const auto &group : groups)
327         lst << group->allFiles();
328     return lst;
329 }
330 
331 /*!
332  * \brief Returns all files of all enabled groups as source artifacts.
333  * \sa ResolvedProduct::allFiles()
334  */
allEnabledFiles() const335 std::vector<SourceArtifactPtr> ResolvedProduct::allEnabledFiles() const
336 {
337     std::vector<SourceArtifactPtr> lst;
338     for (const auto &group : groups) {
339         if (group->enabled)
340             lst << group->allFiles();
341     }
342     return lst;
343 }
344 
fileTagsForFileName(const QString & fileName) const345 FileTags ResolvedProduct::fileTagsForFileName(const QString &fileName) const
346 {
347     FileTags result;
348     std::unique_ptr<int> priority;
349     for (const FileTaggerConstPtr &tagger : qAsConst(fileTaggers)) {
350         for (const QRegularExpression &pattern : tagger->patterns()) {
351             if (pattern.match(fileName).hasMatch()) {
352                 if (priority) {
353                     if (*priority != tagger->priority()) {
354                         // The taggers are expected to be sorted by priority.
355                         QBS_ASSERT(*priority > tagger->priority(), return result);
356                         return result;
357                     }
358                 } else {
359                     priority = std::make_unique<int>(tagger->priority());
360                 }
361                 result.unite(tagger->fileTags());
362                 break;
363             }
364         }
365     }
366     return result;
367 }
368 
load(PersistentPool & pool)369 void ResolvedProduct::load(PersistentPool &pool)
370 {
371     serializationOp<PersistentPool::Load>(pool);
372     for (const RulePtr &rule : rules)
373         rule->product = this;
374     for (const ResolvedModulePtr &module : modules)
375         module->product = this;
376 }
377 
store(PersistentPool & pool)378 void ResolvedProduct::store(PersistentPool &pool)
379 {
380     serializationOp<PersistentPool::Store>(pool);
381 }
382 
lookupArtifactsByFileTag(const FileTag & tag) const383 ArtifactSet ResolvedProduct::lookupArtifactsByFileTag(const FileTag &tag) const
384 {
385     QBS_CHECK(buildData);
386     return buildData->artifactsByFileTag().value(tag);
387 }
388 
lookupArtifactsByFileTags(const FileTags & tags) const389 ArtifactSet ResolvedProduct::lookupArtifactsByFileTags(const FileTags &tags) const
390 {
391     QBS_CHECK(buildData);
392     ArtifactSet set;
393     for (const FileTag &tag : tags)
394         set = set.unite(buildData->artifactsByFileTag().value(tag));
395     return set;
396 }
397 
targetArtifacts() const398 ArtifactSet ResolvedProduct::targetArtifacts() const
399 {
400     QBS_CHECK(buildData);
401     ArtifactSet taSet;
402     for (Artifact * const a : buildData->rootArtifacts()) {
403         QBS_CHECK(a->fileTags().intersects(fileTags));
404         taSet << a;
405     }
406     return taSet;
407 }
408 
topLevelProject() const409 TopLevelProject *ResolvedProduct::topLevelProject() const
410 {
411     return project->topLevelProject();
412 }
413 
uniqueName(const QString & name,const QString & multiplexConfigurationId)414 QString ResolvedProduct::uniqueName(const QString &name, const QString &multiplexConfigurationId)
415 {
416     QString result = name;
417     if (!multiplexConfigurationId.isEmpty())
418         result.append(QLatin1Char('.')).append(multiplexConfigurationId);
419     return result;
420 }
421 
uniqueName() const422 QString ResolvedProduct::uniqueName() const
423 {
424     return uniqueName(name, multiplexConfigurationId);
425 }
426 
fullDisplayName(const QString & name,const QString & multiplexConfigurationId)427 QString ResolvedProduct::fullDisplayName(const QString &name,
428                                          const QString &multiplexConfigurationId)
429 {
430     QString result = name;
431     if (!multiplexConfigurationId.isEmpty())
432         result.append(QLatin1Char(' ')).append(multiplexIdToString(multiplexConfigurationId));
433     return result;
434 }
435 
fullDisplayName() const436 QString ResolvedProduct::fullDisplayName() const
437 {
438     return fullDisplayName(name, multiplexConfigurationId);
439 }
440 
profile() const441 QString ResolvedProduct::profile() const
442 {
443     return moduleProperties->qbsPropertyValue(StringConstants::profileProperty()).toString();
444 }
445 
findGeneratedFiles(const Artifact * base,bool recursive,const FileTags & tags)446 static QStringList findGeneratedFiles(const Artifact *base, bool recursive, const FileTags &tags)
447 {
448     QStringList result;
449     for (const Artifact *parent : base->parentArtifacts()) {
450         if (tags.empty() || parent->fileTags().intersects(tags))
451             result << parent->filePath();
452         if (recursive)
453             result << findGeneratedFiles(parent, true, tags);
454     }
455     return result;
456 }
457 
generatedFiles(const QString & baseFile,bool recursive,const FileTags & tags) const458 QStringList ResolvedProduct::generatedFiles(const QString &baseFile, bool recursive,
459                                             const FileTags &tags) const
460 {
461     ProductBuildData *data = buildData.get();
462     if (!data)
463         return {};
464 
465     for (const Artifact *art : filterByType<Artifact>(data->allNodes())) {
466         if (art->filePath() == baseFile)
467             return findGeneratedFiles(art, recursive, tags);
468     }
469     return {};
470 }
471 
deriveBuildDirectoryName(const QString & name,const QString & multiplexConfigurationId)472 QString ResolvedProduct::deriveBuildDirectoryName(const QString &name,
473                                                   const QString &multiplexConfigurationId)
474 {
475     QString dirName = uniqueName(name, multiplexConfigurationId);
476     const QByteArray hash = QCryptographicHash::hash(dirName.toUtf8(), QCryptographicHash::Sha1);
477     return HostOsInfo::rfc1034Identifier(dirName)
478             .append(QLatin1Char('.'))
479             .append(QString::fromLatin1(hash.toHex().left(8)));
480 }
481 
buildDirectory() const482 QString ResolvedProduct::buildDirectory() const
483 {
484     return productProperties.value(StringConstants::buildDirectoryProperty()).toString();
485 }
486 
isInParentProject(const ResolvedProductConstPtr & other) const487 bool ResolvedProduct::isInParentProject(const ResolvedProductConstPtr &other) const
488 {
489     for (const ResolvedProject *otherParent = other->project.get(); otherParent;
490          otherParent = otherParent->parentProject.get()) {
491         if (otherParent == project.get())
492             return true;
493     }
494     return false;
495 }
496 
builtByDefault() const497 bool ResolvedProduct::builtByDefault() const
498 {
499     return productProperties.value(StringConstants::builtByDefaultProperty(), true).toBool();
500 }
501 
cacheExecutablePath(const QString & origFilePath,const QString & fullFilePath)502 void ResolvedProduct::cacheExecutablePath(const QString &origFilePath, const QString &fullFilePath)
503 {
504     std::lock_guard<std::mutex> locker(m_executablePathCacheLock);
505     m_executablePathCache.insert(origFilePath, fullFilePath);
506 }
507 
cachedExecutablePath(const QString & origFilePath) const508 QString ResolvedProduct::cachedExecutablePath(const QString &origFilePath) const
509 {
510     std::lock_guard<std::mutex> locker(m_executablePathCacheLock);
511     return m_executablePathCache.value(origFilePath);
512 }
513 
514 
ResolvedProject()515 ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(nullptr)
516 {
517 }
518 
519 ResolvedProject::~ResolvedProject() = default;
520 
accept(BuildGraphVisitor * visitor) const521 void ResolvedProject::accept(BuildGraphVisitor *visitor) const
522 {
523     for (const ResolvedProductPtr &product : products)
524         product->accept(visitor);
525     for (const ResolvedProjectPtr &subProject : qAsConst(subProjects))
526         subProject->accept(visitor);
527 }
528 
topLevelProject()529 TopLevelProject *ResolvedProject::topLevelProject()
530 {
531     if (m_topLevelProject)
532         return m_topLevelProject;
533     if (parentProject.expired()) {
534         m_topLevelProject = static_cast<TopLevelProject *>(this);
535         return m_topLevelProject;
536     }
537     m_topLevelProject = parentProject->topLevelProject();
538     return m_topLevelProject;
539 }
540 
allSubProjects() const541 std::vector<ResolvedProjectPtr> ResolvedProject::allSubProjects() const
542 {
543     std::vector<ResolvedProjectPtr> projectList = subProjects;
544     for (const auto &subProject : subProjects)
545         projectList << subProject->allSubProjects();
546     return projectList;
547 }
548 
allProducts() const549 std::vector<ResolvedProductPtr> ResolvedProject::allProducts() const
550 {
551     std::vector<ResolvedProductPtr> productList = products;
552     for (const auto &subProject : qAsConst(subProjects))
553         productList << subProject->allProducts();
554     return productList;
555 }
556 
load(PersistentPool & pool)557 void ResolvedProject::load(PersistentPool &pool)
558 {
559     serializationOp<PersistentPool::Load>(pool);
560     std::for_each(products.cbegin(), products.cend(),
561                   [](const ResolvedProductPtr &p) {
562         if (!p->buildData)
563             return;
564         for (BuildGraphNode * const node : qAsConst(p->buildData->allNodes())) {
565             node->product = p;
566 
567             // restore parent links
568             for (BuildGraphNode * const child : qAsConst(node->children))
569                 child->parents.insert(node);
570         }
571     });
572 }
573 
store(PersistentPool & pool)574 void ResolvedProject::store(PersistentPool &pool)
575 {
576     serializationOp<PersistentPool::Store>(pool);
577 }
578 
579 
TopLevelProject()580 TopLevelProject::TopLevelProject()
581     : bgLocker(nullptr), locked(false), lastStartResolveTime(FileTime::oldestTime())
582 {
583 }
584 
~TopLevelProject()585 TopLevelProject::~TopLevelProject()
586 {
587     cleanupModuleProviderOutput();
588     delete bgLocker;
589 }
590 
deriveId(const QVariantMap & config)591 QString TopLevelProject::deriveId(const QVariantMap &config)
592 {
593     const QVariantMap qbsProperties = config.value(StringConstants::qbsModule()).toMap();
594     const QString configurationName = qbsProperties.value(
595                 StringConstants::configurationNameProperty()).toString();
596     return configurationName;
597 }
598 
deriveBuildDirectory(const QString & buildRoot,const QString & id)599 QString TopLevelProject::deriveBuildDirectory(const QString &buildRoot, const QString &id)
600 {
601     return buildRoot + QLatin1Char('/') + id;
602 }
603 
setBuildConfiguration(const QVariantMap & config)604 void TopLevelProject::setBuildConfiguration(const QVariantMap &config)
605 {
606     m_buildConfiguration = config;
607     m_id = deriveId(config);
608 }
609 
profile() const610 QString TopLevelProject::profile() const
611 {
612     return projectProperties().value(StringConstants::profileProperty()).toString();
613 }
614 
makeModuleProvidersNonTransient()615 void TopLevelProject::makeModuleProvidersNonTransient()
616 {
617     for (ModuleProviderInfo &m : moduleProviderInfo.providers)
618         m.transientOutput = false;
619 }
620 
buildGraphFilePath() const621 QString TopLevelProject::buildGraphFilePath() const
622 {
623     return ProjectBuildData::deriveBuildGraphFilePath(buildDirectory, id());
624 }
625 
store(Logger logger)626 void TopLevelProject::store(Logger logger)
627 {
628     // TODO: Use progress observer here.
629 
630     if (!buildData)
631         return;
632     if (!buildData->isDirty()) {
633         qCDebug(lcBuildGraph) << "build graph is unchanged in project" << id();
634         return;
635     }
636 
637     makeModuleProvidersNonTransient();
638 
639     const QString fileName = buildGraphFilePath();
640     qCDebug(lcBuildGraph) << "storing:" << fileName;
641     PersistentPool pool(logger);
642     PersistentPool::HeadData headData;
643     headData.projectConfig = buildConfiguration();
644     pool.setHeadData(headData);
645     pool.setupWriteStream(fileName);
646     store(pool);
647     pool.finalizeWriteStream();
648     buildData->setClean();
649 }
650 
load(PersistentPool & pool)651 void TopLevelProject::load(PersistentPool &pool)
652 {
653     ResolvedProject::load(pool);
654     serializationOp<PersistentPool::Load>(pool);
655     QBS_CHECK(buildData);
656 }
657 
store(PersistentPool & pool)658 void TopLevelProject::store(PersistentPool &pool)
659 {
660     ResolvedProject::store(pool);
661     serializationOp<PersistentPool::Store>(pool);
662 }
663 
cleanupModuleProviderOutput()664 void TopLevelProject::cleanupModuleProviderOutput()
665 {
666     QString error;
667     for (const ModuleProviderInfo &m : moduleProviderInfo.providers) {
668         if (m.transientOutput) {
669             if (!removeDirectoryWithContents(m.outputDirPath(buildDirectory), &error))
670                 qCWarning(lcBuildGraph) << "Error removing module provider output:" << error;
671         }
672     }
673     QDir moduleProviderBaseDir(buildDirectory + QLatin1Char('/')
674                                + ModuleProviderInfo::outputBaseDirName());
675     if (moduleProviderBaseDir.exists() && moduleProviderBaseDir.isEmpty()
676             && !removeDirectoryWithContents(moduleProviderBaseDir.path(), &error)) {
677         qCWarning(lcBuildGraph) << "Error removing module provider output:" << error;
678     }
679 }
680 
681 /*!
682  * \class SourceWildCards
683  * \brief Objects of the \c SourceWildCards class result from giving wildcards in a
684  *        \c ResolvedGroup's "files" binding.
685  * \sa ResolvedGroup
686  */
687 
688 /*!
689   * \variable SourceWildCards::prefix
690   * \brief Inherited from the \c ResolvedGroup
691   * \sa ResolvedGroup
692   */
693 
694 /*!
695  * \variable SourceWildCards::patterns
696  * \brief All elements of the \c ResolvedGroup's "files" binding that contain wildcards.
697  * \sa ResolvedGroup
698  */
699 
700 /*!
701  * \variable SourceWildCards::excludePatterns
702  * \brief Corresponds to the \c ResolvedGroup's "excludeFiles" binding.
703  * \sa ResolvedGroup
704  */
705 
706 /*!
707  * \variable SourceWildCards::files
708  * \brief The \c SourceArtifacts resulting from the expanded list of matching files.
709  */
710 
expandPatterns(const GroupConstPtr & group,const QString & baseDir,const QString & buildDir)711 Set<QString> SourceWildCards::expandPatterns(const GroupConstPtr &group,
712                                               const QString &baseDir, const QString &buildDir)
713 {
714     Set<QString> files = expandPatterns(group, patterns, baseDir, buildDir);
715     files -= expandPatterns(group, excludePatterns, baseDir, buildDir);
716     return files;
717 }
718 
expandPatterns(const GroupConstPtr & group,const QStringList & patterns,const QString & baseDir,const QString & buildDir)719 Set<QString> SourceWildCards::expandPatterns(const GroupConstPtr &group,
720         const QStringList &patterns, const QString &baseDir, const QString &buildDir)
721 {
722     Set<QString> files;
723     QString expandedPrefix = group->prefix;
724     if (expandedPrefix.startsWith(StringConstants::tildeSlash()))
725         expandedPrefix.replace(0, 1, QDir::homePath());
726     for (QString pattern : patterns) {
727         pattern.prepend(expandedPrefix);
728         pattern.replace(QLatin1Char('\\'), QLatin1Char('/'));
729         QStringList parts = pattern.split(QLatin1Char('/'), QBS_SKIP_EMPTY_PARTS);
730         if (FileInfo::isAbsolute(pattern)) {
731             QString rootDir;
732             if (HostOsInfo::isWindowsHost() && pattern.at(0) != QLatin1Char('/')) {
733                 rootDir = parts.takeFirst();
734                 if (!rootDir.endsWith(QLatin1Char('/')))
735                     rootDir.append(QLatin1Char('/'));
736             } else {
737                 rootDir = QLatin1Char('/');
738             }
739             expandPatterns(files, group, parts, rootDir, buildDir);
740         } else {
741             expandPatterns(files, group, parts, baseDir, buildDir);
742         }
743     }
744 
745     return files;
746 }
747 
expandPatterns(Set<QString> & result,const GroupConstPtr & group,const QStringList & parts,const QString & baseDir,const QString & buildDir)748 void SourceWildCards::expandPatterns(Set<QString> &result, const GroupConstPtr &group,
749                                      const QStringList &parts,
750                                      const QString &baseDir, const QString &buildDir)
751 {
752     // People might build directly in the project source directory. This is okay, since
753     // we keep the build data in a "container" directory. However, we must make sure we don't
754     // match any generated files therein as source files.
755     if (baseDir.startsWith(buildDir))
756         return;
757 
758     dirTimeStamps.emplace_back(baseDir, FileInfo(baseDir).lastModified());
759 
760     QStringList changed_parts = parts;
761     bool recursive = false;
762     QString part = changed_parts.takeFirst();
763 
764     while (part == QStringLiteral("**")) {
765         recursive = true;
766 
767         if (changed_parts.empty()) {
768             part = StringConstants::star();
769             break;
770         }
771 
772         part = changed_parts.takeFirst();
773     }
774 
775     const bool isDir = !changed_parts.empty();
776 
777     const QString &filePattern = part;
778     const QDirIterator::IteratorFlags itFlags = recursive
779             ? QDirIterator::Subdirectories
780             : QDirIterator::NoIteratorFlags;
781     QDir::Filters itFilters = isDir
782             ? QDir::Dirs
783             : QDir::Files | QDir::System
784               | QDir::Dirs; // This one is needed to get symbolic links to directories
785 
786     if (isDir && !FileInfo::isPattern(filePattern))
787         itFilters |= QDir::Hidden;
788     if (filePattern != StringConstants::dotDot() && filePattern != StringConstants::dot())
789         itFilters |= QDir::NoDotAndDotDot;
790 
791     QDirIterator it(baseDir, QStringList(filePattern), itFilters, itFlags);
792     while (it.hasNext()) {
793         const QString filePath = it.next();
794         const QString parentDir = it.fileInfo().dir().path();
795         if (parentDir.startsWith(buildDir))
796             continue; // See above.
797         if (!isDir && it.fileInfo().isDir() && !it.fileInfo().isSymLink())
798             continue;
799         if (isDir) {
800             expandPatterns(result, group, changed_parts, filePath, buildDir);
801         } else {
802             if (parentDir != baseDir)
803                 dirTimeStamps.emplace_back(parentDir, FileInfo(baseDir).lastModified());
804             result += QDir::cleanPath(filePath);
805         }
806     }
807 }
808 
809 template<typename L>
listToMap(const L & list)810 QMap<QString, typename L::value_type> listToMap(const L &list)
811 {
812     using V = typename L::value_type;
813     QMap<QString, V> map;
814     for (const V &elem : list)
815         map.insert(keyFromElem(elem), elem);
816     return map;
817 }
818 
819 template<typename L>
listsAreEqual(const L & l1,const L & l2)820 bool listsAreEqual(const L &l1, const L &l2)
821 {
822     if (l1.size() != l2.size())
823         return false;
824     using V = typename L::value_type;
825     const QMap<QString, V> map1 = listToMap(l1);
826     const QMap<QString, V> map2 = listToMap(l2);
827     for (const QString &key : map1.keys()) {
828         const V &value2 = map2.value(key);
829         if (!value2)
830             return false;
831         if (!equals(map1.value(key).get(), value2.get()))
832             return false;
833     }
834     return true;
835 }
836 
keyFromElem(const SourceArtifactPtr & sa)837 QString keyFromElem(const SourceArtifactPtr &sa) { return sa->absoluteFilePath; }
keyFromElem(const RulePtr & r)838 QString keyFromElem(const RulePtr &r) {
839     QString key = r->toString() + r->prepareScript.sourceCode();
840     if (r->outputArtifactsScript.isValid())
841         key += r->outputArtifactsScript.sourceCode();
842     for (const auto &a : r->artifacts)
843         key += a->filePath;
844     return key;
845 }
846 
keyFromElem(const ArtifactPropertiesPtr & ap)847 QString keyFromElem(const ArtifactPropertiesPtr &ap)
848 {
849     QStringList lst = ap->fileTagsFilter().toStringList();
850     lst.sort();
851     return lst.join(QLatin1Char(','));
852 }
853 
operator ==(const SourceArtifactInternal & sa1,const SourceArtifactInternal & sa2)854 bool operator==(const SourceArtifactInternal &sa1, const SourceArtifactInternal &sa2)
855 {
856     return sa1.absoluteFilePath == sa2.absoluteFilePath
857             && sa1.fileTags == sa2.fileTags
858             && sa1.overrideFileTags == sa2.overrideFileTags
859             && sa1.targetOfModule == sa2.targetOfModule
860             && !sa1.properties == !sa2.properties
861             && *sa1.properties == *sa2.properties;
862 }
863 
operator ==(const Rule & r1,const Rule & r2)864 bool operator==(const Rule &r1, const Rule &r2)
865 {
866     if (r1.artifacts.size() != r2.artifacts.size())
867         return false;
868     for (size_t i = 0; i < r1.artifacts.size(); ++i) {
869         if (!equals(r1.artifacts.at(i).get(), r2.artifacts.at(i).get()))
870             return false;
871     }
872 
873     return r1.module->name == r2.module->name
874             && r1.prepareScript == r2.prepareScript
875             && r1.outputArtifactsScript == r2.outputArtifactsScript
876             && r1.inputs == r2.inputs
877             && r1.outputFileTags == r2.outputFileTags
878             && r1.auxiliaryInputs == r2.auxiliaryInputs
879             && r1.excludedInputs == r2.excludedInputs
880             && r1.inputsFromDependencies == r2.inputsFromDependencies
881             && r1.explicitlyDependsOn == r2.explicitlyDependsOn
882             && r1.explicitlyDependsOnFromDependencies == r2.explicitlyDependsOnFromDependencies
883             && r1.multiplex == r2.multiplex
884             && r1.requiresInputs == r2.requiresInputs
885             && r1.alwaysRun == r2.alwaysRun;
886 }
887 
ruleListsAreEqual(const std::vector<RulePtr> & l1,const std::vector<RulePtr> & l2)888 bool ruleListsAreEqual(const std::vector<RulePtr> &l1, const std::vector<RulePtr> &l2)
889 {
890     return listsAreEqual(l1, l2);
891 }
892 
operator ==(const RuleArtifact & a1,const RuleArtifact & a2)893 bool operator==(const RuleArtifact &a1, const RuleArtifact &a2)
894 {
895     return a1.filePath == a2.filePath
896             && a1.fileTags == a2.fileTags
897             && a1.alwaysUpdated == a2.alwaysUpdated
898             && rangeTo<Set<RuleArtifact::Binding>>(a1.bindings) ==
899                rangeTo<Set<RuleArtifact::Binding>>(a2.bindings);
900 }
901 
operator ==(const RuleArtifact::Binding & b1,const RuleArtifact::Binding & b2)902 bool operator==(const RuleArtifact::Binding &b1, const RuleArtifact::Binding &b2)
903 {
904     return b1.code == b2.code && b1.name == b2.name;
905 }
906 
qHash(const RuleArtifact::Binding & b)907 QHashValueType qHash(const RuleArtifact::Binding &b)
908 {
909     return qHash(std::make_pair(b.code, b.name.join(QLatin1Char(','))));
910 }
911 
artifactPropertyListsAreEqual(const std::vector<ArtifactPropertiesPtr> & l1,const std::vector<ArtifactPropertiesPtr> & l2)912 bool artifactPropertyListsAreEqual(const std::vector<ArtifactPropertiesPtr> &l1,
913                                    const std::vector<ArtifactPropertiesPtr> &l2)
914 {
915     return listsAreEqual(l1, l2);
916 }
917 
multiplexIdToString(const QString & id)918 QString multiplexIdToString(const QString &id)
919 {
920     return QString::fromUtf8(QByteArray::fromBase64(id.toUtf8()));
921 }
922 
operator ==(const PrivateScriptFunction & a,const PrivateScriptFunction & b)923 bool operator==(const PrivateScriptFunction &a, const PrivateScriptFunction &b)
924 {
925     return equals(a.m_sharedData.get(), b.m_sharedData.get());
926 }
927 
operator ==(const ExportedProperty & p1,const ExportedProperty & p2)928 bool operator==(const ExportedProperty &p1, const ExportedProperty &p2)
929 {
930     return p1.fullName == p2.fullName
931             && p1.type == p2.type
932             && p1.sourceCode == p2.sourceCode
933             && p1.isBuiltin == p2.isBuiltin;
934 }
935 
operator ==(const ExportedModuleDependency & d1,const ExportedModuleDependency & d2)936 bool operator==(const ExportedModuleDependency &d1, const ExportedModuleDependency &d2)
937 {
938     return d1.name == d2.name && d1.moduleProperties == d2.moduleProperties;
939 }
940 
equals(const std::vector<ExportedItemPtr> & l1,const std::vector<ExportedItemPtr> & l2)941 bool equals(const std::vector<ExportedItemPtr> &l1, const std::vector<ExportedItemPtr> &l2)
942 {
943     static const auto cmp = [](const ExportedItemPtr &p1, const ExportedItemPtr &p2) {
944         return *p1 == *p2;
945     };
946     return l1.size() == l2.size() && std::equal(l1.cbegin(), l1.cend(), l2.cbegin(), cmp);
947 }
948 
operator ==(const ExportedItem & i1,const ExportedItem & i2)949 bool operator==(const ExportedItem &i1, const ExportedItem &i2)
950 {
951     return i1.name == i2.name
952             && i1.properties == i2.properties
953             && equals(i1.children, i2.children);
954 }
955 
operator ==(const ExportedModule & m1,const ExportedModule & m2)956 bool operator==(const ExportedModule &m1, const ExportedModule &m2)
957 {
958     static const auto depMapsEqual = [](const QMap<ResolvedProductConstPtr, QVariantMap> &m1,
959             const QMap<ResolvedProductConstPtr, QVariantMap> &m2) {
960         if (m1.size() != m2.size())
961             return false;
962         for (auto it1 = m1.cbegin(), it2 = m2.cbegin(); it1 != m1.cend(); ++it1, ++it2) {
963             if (it1.key()->name != it2.key()->name)
964                 return false;
965             if (it1.value() != it2.value())
966                 return false;
967         }
968         return true;
969     };
970 
971     return m1.propertyValues == m2.propertyValues
972             && m1.modulePropertyValues == m2.modulePropertyValues
973             && equals(m1.children, m2.children)
974             && m1.m_properties == m2.m_properties
975             && m1.importStatements == m2.importStatements
976             && m1.productDependencies.size() == m2.productDependencies.size()
977             && m1.productDependencies == m2.productDependencies
978             && depMapsEqual(m1.dependencyParameters, m2.dependencyParameters);
979 }
980 
981 } // namespace Internal
982 } // namespace qbs
983