diff options
Diffstat (limited to 'src/lib/corelib/buildgraph')
53 files changed, 1466 insertions, 1503 deletions
diff --git a/src/lib/corelib/buildgraph/artifact.cpp b/src/lib/corelib/buildgraph/artifact.cpp index e82919560..8f94353e6 100644 --- a/src/lib/corelib/buildgraph/artifact.cpp +++ b/src/lib/corelib/buildgraph/artifact.cpp @@ -51,15 +51,21 @@ namespace qbs { namespace Internal { -Artifact::Artifact() +Artifact::Artifact() : + artifactType(ArtifactType::Unknown), + inputsScanned(false), + timestampRetrieved(false), + alwaysUpdated(false), + oldDataPossiblyPresent(true) { - initialize(); } Artifact::~Artifact() { for (Artifact *p : parentArtifacts()) p->childrenAddedByScanner.remove(this); + if (m_deregister) + m_deregister(this); } void Artifact::accept(BuildGraphVisitor *visitor) @@ -120,15 +126,6 @@ RuleNode *Artifact::producer() const return *ruleNodes.begin(); } -void Artifact::initialize() -{ - artifactType = Unknown; - inputsScanned = false; - timestampRetrieved = false; - alwaysUpdated = true; - oldDataPossiblyPresent = true; -} - const TypeFilter<Artifact> Artifact::parentArtifacts() const { return TypeFilter<Artifact>(parents); diff --git a/src/lib/corelib/buildgraph/artifact.h b/src/lib/corelib/buildgraph/artifact.h index ee3acea59..fce34b6ef 100644 --- a/src/lib/corelib/buildgraph/artifact.h +++ b/src/lib/corelib/buildgraph/artifact.h @@ -50,6 +50,7 @@ #include <QtCore/qstring.h> +#include <functional> #include <utility> #include <vector> @@ -114,7 +115,6 @@ public: bool alwaysUpdated : 1; bool oldDataPossiblyPresent : 1; - void initialize(); const TypeFilter<Artifact> parentArtifacts() const; const TypeFilter<Artifact> childArtifacts() const; void onChildDisconnected(BuildGraphNode *child) override; @@ -124,8 +124,12 @@ public: void load(PersistentPool &pool) override; void store(PersistentPool &pool) override; + using Deregister = std::function<void(const Artifact *)>; + void setDeregister(const Deregister &deregister) { m_deregister = deregister; } + private: FileTags m_fileTags; + Deregister m_deregister; }; template<> inline QString Set<Artifact *>::toString(Artifact * const &artifact) const diff --git a/src/lib/corelib/buildgraph/artifactcleaner.cpp b/src/lib/corelib/buildgraph/artifactcleaner.cpp index 000dfda02..6239a138b 100644 --- a/src/lib/corelib/buildgraph/artifactcleaner.cpp +++ b/src/lib/corelib/buildgraph/artifactcleaner.cpp @@ -162,7 +162,7 @@ ArtifactCleaner::ArtifactCleaner(Logger logger, ProgressObserver *observer) } void ArtifactCleaner::cleanup(const TopLevelProjectPtr &project, - const QList<ResolvedProductPtr> &products, const CleanOptions &options) + const QVector<ResolvedProductPtr> &products, const CleanOptions &options) { m_hasError = false; @@ -181,7 +181,7 @@ void ArtifactCleaner::cleanup(const TopLevelProjectPtr &project, // Directories created during the build are not artifacts (TODO: should they be?), // so we have to clean them up manually. - QList<QString> dirList = directories.toList(); + auto dirList = rangeTo<QStringList>(directories); for (int i = 0; i < dirList.size(); ++i) { const QString &dir = dirList.at(i); if (!dir.startsWith(project->buildDirectory)) diff --git a/src/lib/corelib/buildgraph/artifactcleaner.h b/src/lib/corelib/buildgraph/artifactcleaner.h index 5112a75d6..8d0bef275 100644 --- a/src/lib/corelib/buildgraph/artifactcleaner.h +++ b/src/lib/corelib/buildgraph/artifactcleaner.h @@ -54,7 +54,7 @@ class ArtifactCleaner { public: ArtifactCleaner(Logger logger, ProgressObserver *observer); - void cleanup(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products, + void cleanup(const TopLevelProjectPtr &project, const QVector<ResolvedProductPtr> &products, const CleanOptions &options); private: diff --git a/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp b/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp index 2adb77d47..6d0ea738f 100644 --- a/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp +++ b/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp @@ -45,171 +45,178 @@ #include <language/language.h> #include <language/scriptengine.h> -#include <QtScript/qscriptclass.h> -#include <QtScript/qscriptcontext.h> +#include <tools/stlutils.h> +#include <tools/stringconstants.h> namespace qbs { namespace Internal { -enum BuildGraphScriptValueCommonPropertyKeys : quint32 { - CachedValueKey, - FileTagKey, - ProductPtrKey, -}; - -class ArtifactsScriptClass : public QScriptClass +template<class ProductOrModule> +static bool isRelevantArtifact(const ProductOrModule *productOrModule, const Artifact *artifact) { -public: - ArtifactsScriptClass(QScriptEngine *engine) : QScriptClass(engine) { } - -private: - QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, - QueryFlags flags, uint *id) override - { - getProduct(object); - qbsEngine()->setNonExistingArtifactSetRequested(m_product, name.toString()); - return QScriptClass::queryProperty(object, name, flags, id); - } - - QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override - { - getProduct(object); - qbsEngine()->setArtifactsEnumerated(m_product); - return QScriptClass::newIterator(object); + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) { + Q_UNUSED(productOrModule) + return !artifact->isTargetOfModule(); + } else { + return artifact->targetOfModule == productOrModule->name; } - - void getProduct(const QScriptValue &object) - { - if (m_lastObjectId != object.objectId()) { - m_lastObjectId = object.objectId(); - m_product = reinterpret_cast<const ResolvedProduct *>( - object.data().property(ProductPtrKey).toVariant().value<quintptr>()); - } - } - - ScriptEngine *qbsEngine() const { return static_cast<ScriptEngine *>(engine()); } - - qint64 m_lastObjectId = 0; - const ResolvedProduct *m_product = nullptr; -}; - -static bool isRelevantArtifact(const ResolvedProduct *, const Artifact *artifact) -{ - return !artifact->isTargetOfModule(); -} -static bool isRelevantArtifact(const ResolvedModule *module, const Artifact *artifact) -{ - return artifact->targetOfModule == module->name; } -static ArtifactSetByFileTag artifactsMap(const ResolvedProduct *product) +template<class ProductOrModule> +static ArtifactSetByFileTag artifactsMap(const ProductOrModule *productOrModule) { - return product->buildData->artifactsByFileTag(); + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) + return productOrModule->buildData->artifactsByFileTag(); + else + return artifactsMap(productOrModule->product); } -static ArtifactSetByFileTag artifactsMap(const ResolvedModule *module) +template<class ProductOrModule> static int scriptClassIndex() { - return artifactsMap(module->product); + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) + return 0; + return 1; } -static QScriptValue createArtifactsObject(const ResolvedProduct *product, ScriptEngine *engine) +template<class ProductOrModule> +std::unique_lock<std::mutex> getArtifactsMapLock(ProductOrModule *productOrModule) { - QScriptClass *scriptClass = engine->artifactsScriptClass(); - if (!scriptClass) { - scriptClass = new ArtifactsScriptClass(engine); - engine->setArtifactsScriptClass(scriptClass); - } - QScriptValue artifactsObj = engine->newObject(scriptClass); - QScriptValue data = engine->newObject(); - QVariant v; - v.setValue<quintptr>(reinterpret_cast<quintptr>(product)); - data.setProperty(ProductPtrKey, engine->newVariant(v)); - artifactsObj.setData(data); - return artifactsObj; + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) + return productOrModule->buildData->getArtifactsMapLock(); + else + return getArtifactsMapLock(productOrModule->product); } -static QScriptValue createArtifactsObject(const ResolvedModule *, ScriptEngine *engine) +template<class ProductOrModule> +static bool checkAndSetArtifactsMapUpToDateFlag(const ProductOrModule *productOrModule) { - return engine->newObject(); + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) + return productOrModule->buildData->checkAndSetJsArtifactsMapUpToDateFlag(); + else + return checkAndSetArtifactsMapUpToDateFlag(productOrModule->product); } -static bool checkAndSetArtifactsMapUpToDateFlag(const ResolvedProduct *p) +// Must be called with artifacts map lock held! +template<class ProductOrModule> +void registerArtifactsMapAccess(ScriptEngine *engine, ProductOrModule *productOrModule) { - return p->buildData->checkAndSetJsArtifactsMapUpToDateFlag(); + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) { + if (!checkAndSetArtifactsMapUpToDateFlag(productOrModule)) + engine->setArtifactsMapRequested(productOrModule, true); + else + engine->setArtifactsMapRequested(productOrModule, false); + } else { + registerArtifactsMapAccess(engine, productOrModule->product); + } } -static bool checkAndSetArtifactsMapUpToDateFlag(const ResolvedModule *) { return true; } -static void registerArtifactsMapAccess(const ResolvedProduct *p, ScriptEngine *e, bool forceUpdate) -{ - e->setArtifactsMapRequested(p, forceUpdate); -} -static void registerArtifactsMapAccess(const ResolvedModule *, ScriptEngine *, bool) {} -static void registerArtifactsSetAccess(const ResolvedProduct *p, const FileTag &t, ScriptEngine *e) +template<class ProductOrModule> +static int getArtifactsPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, + JSValueConst obj) { - e->setArtifactSetRequestedForTag(p, t); + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const auto productOrModule = attachedPointer<ProductOrModule>( + obj, engine->artifactsScriptClass(scriptClassIndex<ProductOrModule>())); + const std::unique_lock lock = getArtifactsMapLock(productOrModule); + registerArtifactsMapAccess(engine, productOrModule); + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) + engine->setArtifactsEnumerated(productOrModule); + const auto &map = artifactsMap(productOrModule); + const auto filter = [productOrModule](const Artifact *a) { + return isRelevantArtifact(productOrModule, a); + }; + QStringList tags; + for (auto it = map.cbegin(); it != map.cend(); ++it) { + if (any_of(it.value(), filter)) { + tags << it.key().toString(); + } + } + *plen = tags.size(); + if (!tags.isEmpty()) { + *ptab = reinterpret_cast<JSPropertyEnum *>(js_malloc(ctx, *plen * sizeof **ptab)); + JSPropertyEnum *entry = *ptab; + for (const QString &tag : std::as_const(tags)) { + entry->atom = JS_NewAtom(ctx, tag.toUtf8().constData()); + entry->is_enumerable = 1; + ++entry; + } + } else { + *ptab = nullptr; + } + return 0; } -static void registerArtifactsSetAccess(const ResolvedModule *, const FileTag &, ScriptEngine *) {} -template<class ProductOrModule> static QScriptValue js_artifactsForFileTag( - QScriptContext *ctx, ScriptEngine *engine, const ProductOrModule *productOrModule) +template<class ProductOrModule> +static int getArtifactsProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop) { - const FileTag fileTag = FileTag(ctx->callee().property(FileTagKey).toString().toUtf8()); - registerArtifactsSetAccess(productOrModule, fileTag, engine); - QScriptValue result = ctx->callee().property(CachedValueKey); - if (result.isArray()) - return result; - auto artifacts = artifactsMap(productOrModule).value(fileTag); - const auto filter = [productOrModule](const Artifact *a) { + if (!desc) + return 1; + + desc->flags = JS_PROP_ENUMERABLE; + desc->value = desc->getter = desc->setter = JS_UNDEFINED; + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const auto productOrModule = attachedPointer<ProductOrModule>( + obj, engine->artifactsScriptClass(scriptClassIndex<ProductOrModule>())); + const std::unique_lock lock = getArtifactsMapLock(productOrModule); + registerArtifactsMapAccess(engine, productOrModule); + const QString tagString = getJsString(ctx, prop); + const FileTag fileTag(tagString.toUtf8()); + const auto &map = artifactsMap(productOrModule); + const auto it = map.constFind(fileTag); + if (it == map.constEnd()) { + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) + engine->setNonExistingArtifactSetRequested(productOrModule, tagString); + return 1; + } + if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) + engine->setArtifactSetRequestedForTag(productOrModule, fileTag); + ArtifactSet artifacts = it.value(); + removeIf(artifacts, [productOrModule](const Artifact *a) { return !isRelevantArtifact(productOrModule, a); - }; - artifacts.erase(std::remove_if(artifacts.begin(), artifacts.end(), filter), artifacts.end()); - result = engine->newArray(uint(artifacts.size())); - ctx->callee().setProperty(CachedValueKey, result); - int k = 0; - for (const Artifact * const artifact : artifacts) - result.setProperty(k++, Transformer::translateFileConfig(engine, artifact, QString())); - return result; + }); + if (!artifacts.empty()) { + desc->value = JS_NewArray(ctx); // TODO: Also cache this list? + int k = 0; + for (Artifact * const artifact : artifacts) { + JS_SetPropertyUint32(ctx, desc->value, k++, + Transformer::translateFileConfig(engine, artifact, QString())); + } + } + return 1; } -template<class ProductOrModule> static QScriptValue js_artifacts( - QScriptContext *ctx, ScriptEngine *engine, const ProductOrModule *productOrModule) +template<class ProductOrModule> static JSValue js_artifacts(JSContext *ctx, + JSValue jsProductOrModule) { - QScriptValue artifactsObj = ctx->callee().property(CachedValueKey); - if (artifactsObj.isObject() && checkAndSetArtifactsMapUpToDateFlag(productOrModule)) { - registerArtifactsMapAccess(productOrModule, engine, false); + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const auto productOrModule = attachedPointer<ProductOrModule>(jsProductOrModule, + engine->dataWithPtrClass()); + JSValue artifactsObj = engine->artifactsMapScriptValue(productOrModule); + if (!JS_IsUndefined(artifactsObj)) return artifactsObj; + const int classIndex = scriptClassIndex<ProductOrModule>(); + JSClassID scriptClass = engine->artifactsScriptClass(classIndex); + if (scriptClass == 0) { + const QByteArray className = "ArtifactsScriptClass" + QByteArray::number(classIndex); + scriptClass = engine->registerClass(className.constData(), nullptr, nullptr, JS_UNDEFINED, + &getArtifactsPropertyNames<ProductOrModule>, + &getArtifactsProperty<ProductOrModule>); + engine->setArtifactsScriptClass(classIndex, scriptClass); } - registerArtifactsMapAccess(productOrModule, engine, true); - artifactsObj = createArtifactsObject(productOrModule, engine); - ctx->callee().setProperty(CachedValueKey, artifactsObj); - const auto &map = artifactsMap(productOrModule); - for (auto it = map.cbegin(); it != map.cend(); ++it) { - const auto filter = [productOrModule](const Artifact *a) { - return isRelevantArtifact(productOrModule, a); - }; - if (std::none_of(it.value().cbegin(), it.value().cend(), filter)) - continue; - QScriptValue fileTagFunc = engine->newFunction(&js_artifactsForFileTag<ProductOrModule>, - productOrModule); - const QString fileTag = it.key().toString(); - fileTagFunc.setProperty(FileTagKey, fileTag); - artifactsObj.setProperty(fileTag, fileTagFunc, - QScriptValue::ReadOnly | QScriptValue::Undeletable - | QScriptValue::PropertyGetter); - } + artifactsObj = JS_NewObjectClass(engine->context(), scriptClass); + attachPointerTo(artifactsObj, productOrModule); return artifactsObj; } -QScriptValue artifactsScriptValueForProduct(QScriptContext *ctx, ScriptEngine *engine, - const ResolvedProduct *product) +JSValue artifactsScriptValueForProduct(JSContext *ctx, JSValue this_val, int, JSValue *) { - return js_artifacts(ctx, engine, product); + return js_artifacts<ResolvedProduct>(ctx, this_val); } -QScriptValue artifactsScriptValueForModule(QScriptContext *ctx, ScriptEngine *engine, - const ResolvedModule *module) +JSValue artifactsScriptValueForModule(JSContext *ctx, JSValueConst this_val, int, JSValueConst *) { - return js_artifacts(ctx, engine, module); + return js_artifacts<ResolvedModule>(ctx, this_val); } } // namespace Internal diff --git a/src/lib/corelib/buildgraph/artifactsscriptvalue.h b/src/lib/corelib/buildgraph/artifactsscriptvalue.h index c0da8a05e..dce457758 100644 --- a/src/lib/corelib/buildgraph/artifactsscriptvalue.h +++ b/src/lib/corelib/buildgraph/artifactsscriptvalue.h @@ -41,21 +41,14 @@ #include <language/forward_decls.h> -#include <QtScript/qscriptvalue.h> - -QT_BEGIN_NAMESPACE -class QScriptContext; -QT_END_NAMESPACE +#include <quickjs.h> namespace qbs { namespace Internal { class ScriptEngine; -QScriptValue artifactsScriptValueForProduct(QScriptContext *ctx, ScriptEngine *engine, - const ResolvedProduct *product); - -QScriptValue artifactsScriptValueForModule(QScriptContext *ctx, ScriptEngine *engine, - const ResolvedModule *module); +JSValue artifactsScriptValueForProduct(JSContext *ctx, JSValueConst this_val, int, JSValueConst *); +JSValue artifactsScriptValueForModule(JSContext *ctx, JSValueConst this_val, int, JSValueConst *); } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/buildgraph/artifactvisitor.cpp b/src/lib/corelib/buildgraph/artifactvisitor.cpp index c28f07424..885292f06 100644 --- a/src/lib/corelib/buildgraph/artifactvisitor.cpp +++ b/src/lib/corelib/buildgraph/artifactvisitor.cpp @@ -55,13 +55,13 @@ void ArtifactVisitor::visitProduct(const ResolvedProductConstPtr &product) { if (!product->buildData) return; - for (BuildGraphNode *node : qAsConst(product->buildData->allNodes())) + for (BuildGraphNode *node : std::as_const(product->buildData->allNodes())) node->accept(this); } void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project) { - for (const ResolvedProductConstPtr &product : project->allProducts()) + for (const auto &product : project->allProducts()) visitProduct(product); } diff --git a/src/lib/corelib/buildgraph/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp index 3726c654d..10aae5991 100644 --- a/src/lib/corelib/buildgraph/buildgraph.cpp +++ b/src/lib/corelib/buildgraph/buildgraph.cpp @@ -45,7 +45,6 @@ #include "projectbuilddata.h" #include "productbuilddata.h" #include "rulenode.h" -#include "scriptclasspropertyiterator.h" #include "transformer.h" #include <jsextensions/jsextensions.h> @@ -62,14 +61,14 @@ #include <logging/translator.h> #include <tools/error.h> #include <tools/fileinfo.h> -#include <tools/scripttools.h> #include <tools/qbsassert.h> #include <tools/qttools.h> +#include <tools/scripttools.h> +#include <tools/stlutils.h> #include <tools/stringconstants.h> #include <QtCore/qdir.h> #include <QtCore/qfile.h> -#include <QtScript/qscriptclass.h> #include <algorithm> #include <iterator> @@ -82,146 +81,187 @@ static QString childItemsProperty() { return QStringLiteral("childItems"); } static QString exportsProperty() { return QStringLiteral("exports"); } // TODO: Introduce productscriptvalue.{h,cpp}. -static QScriptValue getDataForProductScriptValue(QScriptEngine *engine, - const ResolvedProduct *product) + +static JSValue getDataObject(JSContext *ctx, JSValue obj) { - QScriptValue data = engine->newObject(); - QVariant v; - v.setValue<quintptr>(reinterpret_cast<quintptr>(product)); - data.setProperty(ProductPtrKey, engine->newVariant(v)); - return data; + return getJsProperty(ctx, obj, StringConstants::dataPropertyInternal()); } -class ProductPropertyScriptClass : public QScriptClass +static JSValue getValueFromData(JSContext *ctx, JSValue data, + ModulePropertiesScriptValueCommonPropertyKeys key) { -public: - ProductPropertyScriptClass(QScriptEngine *engine) : QScriptClass(engine) { } - -private: - QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags, - uint *) override - { - if (name == StringConstants::parametersProperty()) { - m_result = object.data().property(DependencyParametersKey); - return HandlesReadAccess; - } - if (name == StringConstants::moduleNameProperty()) { - m_result = object.data().property(ModuleNameKey); - return HandlesReadAccess; - } - if (name == StringConstants::dependenciesProperty() - || name == StringConstants::artifactsProperty() - || name == exportsProperty()) { - // The prototype is not backed by a QScriptClass. - m_result = object.prototype().property(name); - return HandlesReadAccess; - } - - getProduct(object); - QBS_ASSERT(m_product, return {}); - - const auto it = m_product->productProperties.find(name); + return JS_GetPropertyUint32(ctx, data, key); +} - // It is important that we reject unknown property names. Otherwise QtScript will forward - // *everything* to us, including built-in stuff like the hasOwnProperty function. - if (it == m_product->productProperties.cend()) - return {}; +static JSValue getValueFromObject(JSContext *ctx, JSValue obj, + ModulePropertiesScriptValueCommonPropertyKeys key) +{ + const ScopedJsValue data(ctx, getDataObject(ctx, obj)); + return getValueFromData(ctx, data, key); +} - qbsEngine()->addPropertyRequestedInScript(Property(m_product->uniqueName(), QString(), name, - it.value(), Property::PropertyInProduct)); - m_result = qbsEngine()->toScriptValue(it.value()); - return HandlesReadAccess; +void getPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, + const QVariantMap &properties, const QStringList &extraPropertyNames, + JSValue extraObject) +{ + JSPropertyEnum *basePTab = nullptr; + uint32_t basePlen = 0; + JS_GetOwnPropertyNames(ctx, &basePTab, &basePlen, extraObject, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY); + *plen = uint32_t(extraPropertyNames.size() + properties.size() + basePlen); + *ptab = reinterpret_cast<JSPropertyEnum *>(js_malloc(ctx, *plen * sizeof **ptab)); + JSPropertyEnum *entry = *ptab; + for (auto it = properties.begin(); it != properties.end(); ++it, ++entry) { + entry->atom = JS_NewAtom(ctx, it.key().toUtf8().constData()); + entry->is_enumerable = 1; } - - QScriptValue property(const QScriptValue &, const QScriptString &, uint) override - { - return m_result; + for (const QString &prop : extraPropertyNames) { + entry->atom = JS_NewAtom(ctx, prop.toUtf8().constData()); + entry->is_enumerable = 1; + ++entry; + } + for (uint32_t i = 0; i < basePlen; ++i, ++entry) { + entry->atom = basePTab[i].atom; + entry->is_enumerable = 1; + } + js_free(ctx, basePTab); +} + +static int getProductPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, + JSValueConst obj) +{ + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const ScopedJsValue data(ctx, getDataObject(engine->context(), obj)); + const auto product = attachedPointer<ResolvedProduct>( + obj, engine->productPropertyScriptClass()); + QBS_ASSERT(product, return -1); + + // The "moduleName" convenience property is only available for the "main product" in a rule, + // and the "parameters" property exists only for elements of the "dependencies" array for + // which dependency parameters are present. + const auto hasModuleName = [&] { + const ScopedJsValue v(ctx, getValueFromData(ctx, data, ModuleNameKey)); + return JS_IsString(v); + }; + const auto hasDependencyParams = [&] { + const ScopedJsValue v(ctx, getValueFromData(ctx, data, DependencyParametersKey)); + return JS_IsObject(v); + }; + QStringList additionalProperties; + if (hasModuleName()) + additionalProperties.push_back(StringConstants::moduleNameProperty()); + else if (hasDependencyParams()) + additionalProperties.push_back(StringConstants::parametersProperty()); + getPropertyNames(ctx, ptab, plen, product->productProperties, additionalProperties, + engine->baseProductScriptValue(product)); + return 0; +} + +static int getProductProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj, + JSAtom prop) +{ + if (desc) { + desc->getter = desc->setter = desc->value = JS_UNDEFINED; + desc->flags = JS_PROP_ENUMERABLE; + } + const QString name = getJsString(ctx, prop); + if (name == StringConstants::parametersProperty()) { + if (desc) + desc->value = getValueFromObject(ctx, obj, DependencyParametersKey); + return 1; + } + if (name == StringConstants::moduleNameProperty()) { + if (desc) + desc->value = getValueFromObject(ctx, obj, ModuleNameKey); + return 1; } - QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override - { - getProduct(object); - QBS_ASSERT(m_product, return nullptr); - - // These two are in the prototype and are thus common to all product script values. - std::vector<QString> additionalProperties({StringConstants::artifactsProperty(), - StringConstants::dependenciesProperty(), - exportsProperty()}); - - // The "moduleName" convenience property is only available for the "main product" in a rule, - // and the "parameters" property exists only for elements of the "dependencies" array for - // which dependency parameters are present. - if (object.data().property(ModuleNameKey).isValid()) - additionalProperties.push_back(StringConstants::moduleNameProperty()); - else if (object.data().property(DependencyParametersKey).isValid()) - additionalProperties.push_back(StringConstants::parametersProperty()); - return new ScriptClassPropertyIterator(object, m_product->productProperties, - additionalProperties); - } - - void getProduct(const QScriptValue &object) - { - if (m_lastObjectId != object.objectId()) { - m_lastObjectId = object.objectId(); - m_product = reinterpret_cast<const ResolvedProduct *>( - object.data().property(ProductPtrKey).toVariant().value<quintptr>()); - } + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const auto product = attachedPointer<ResolvedProduct>( + obj, engine->productPropertyScriptClass()); + QBS_ASSERT(product, return -1); + + const JSValue baseProductValue = engine->baseProductScriptValue(product); + if (name == exportsProperty()) { + if (desc) + desc->value = getJsProperty(ctx, baseProductValue, name); + engine->addRequestedExport(product); + return 1; } - ScriptEngine *qbsEngine() const { return static_cast<ScriptEngine *>(engine()); } + const auto it = product->productProperties.constFind(name); + if (it == product->productProperties.cend()) { + ScopedJsValue v(ctx, JS_GetProperty(ctx, baseProductValue, prop)); + const int ret = JS_IsUndefined(v) ? 0 : 1; + if (desc) + desc->value = v.release(); + return ret; + } - qint64 m_lastObjectId = 0; - const ResolvedProduct *m_product = nullptr; - QScriptValue m_result; -}; + engine->addPropertyRequestedInScript(Property(product->uniqueName(), QString(), name, + it.value(), Property::PropertyInProduct)); + if (desc) + desc->value = engine->toScriptValue(it.value()); + return 1; +} -static QScriptValue setupProjectScriptValue(ScriptEngine *engine, - const ResolvedProjectConstPtr &project) +static JSValue setupProjectScriptValue(ScriptEngine *engine, const ResolvedProjectConstPtr &project) { - QScriptValue &obj = engine->projectScriptValue(project.get()); - if (obj.isValid()) - return obj; + JSValue &obj = engine->projectScriptValue(project.get()); + if (JS_IsObject(obj)) + return JS_DupValue(engine->context(), obj); obj = engine->newObject(); - obj.setProperty(StringConstants::filePathProperty(), project->location.filePath()); - obj.setProperty(StringConstants::pathProperty(), FileInfo::path(project->location.filePath())); + setJsProperty(engine->context(), obj, StringConstants::filePathProperty(), + project->location.filePath()); + setJsProperty(engine->context(), obj, StringConstants::pathProperty(), + FileInfo::path(project->location.filePath())); const QVariantMap &projectProperties = project->projectProperties(); for (QVariantMap::const_iterator it = projectProperties.begin(); it != projectProperties.end(); ++it) { - engine->setObservedProperty(obj, it.key(), engine->toScriptValue(it.value())); + const ScopedJsValue val(engine->context(), engine->toScriptValue(it.value())); + engine->setObservedProperty(obj, it.key(), val); } - engine->observer()->addProjectObjectId(obj.objectId(), project->name); - return obj; + engine->observer()->addProjectObjectId(jsObjectId(obj), project->name); + return JS_DupValue(engine->context(), obj); } -static QScriptValue setupProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product); +static void setupBaseProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product); class DependenciesFunction { public: - DependenciesFunction(ScriptEngine *engine) - : m_engine(engine) - { - } + DependenciesFunction(ScriptEngine *engine) : m_engine(engine) { } - void init(QScriptValue &productScriptValue, QScriptValue &exportsScriptValue, - const ResolvedProduct *product) + void init(JSValue &productScriptValue, JSValue exportsScriptValue) { - QScriptValue depfunc = m_engine->newFunction(&js_internalProductDependencies, product); - productScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc, - QScriptValue::ReadOnly | QScriptValue::Undeletable - | QScriptValue::PropertyGetter); - depfunc = m_engine->newFunction(&js_exportedProductDependencies, product); - exportsScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc, - QScriptValue::ReadOnly | QScriptValue::Undeletable - | QScriptValue::PropertyGetter); + const QByteArray name = StringConstants::dependenciesProperty().toUtf8(); + const ScopedJsValue depfunc( + m_engine->context(), + JS_NewCFunction(m_engine->context(), &js_internalProductDependencies, + name.constData(), 0)); + const ScopedJsAtom depAtom(m_engine->context(), name); + JS_DefineProperty(m_engine->context(), productScriptValue, depAtom, JS_UNDEFINED, depfunc, + JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE); + const ScopedJsValue exportedDepfunc( + m_engine->context(), + JS_NewCFunction(m_engine->context(), &js_exportedProductDependencies, + name.constData(), 0)); + JS_DefineProperty(m_engine->context(), exportsScriptValue, depAtom, JS_UNDEFINED, + exportedDepfunc, JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE); } private: enum class DependencyType { Internal, Exported }; - static QScriptValue js_productDependencies(QScriptContext *, ScriptEngine *engine, - const ResolvedProduct *product, DependencyType depType) + static JSValue js_productDependencies(const ResolvedProduct *product, + JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv, DependencyType depType) { - QScriptValue result = engine->newArray(); + Q_UNUSED(this_val) + Q_UNUSED(argc) + Q_UNUSED(argv) + + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + JSValue result = JS_NewArray(ctx); quint32 idx = 0; const bool exportCase = depType == DependencyType::Exported; std::vector<ResolvedProductPtr> productDeps; @@ -243,202 +283,234 @@ private: } else { productDeps = product->dependencies; } - for (const ResolvedProductPtr &dependency : qAsConst(productDeps)) { - QScriptValue obj = engine->newObject(engine->productPropertyScriptClass()); - obj.setPrototype(setupProductScriptValue(static_cast<ScriptEngine *>(engine), - dependency.get())); + for (const ResolvedProductPtr &dependency : std::as_const(productDeps)) { + setupBaseProductScriptValue(engine, dependency.get()); + JSValue obj = JS_NewObjectClass(engine->context(), + engine->productPropertyScriptClass()); + attachPointerTo(obj, dependency.get()); const QVariantMap ¶ms = (exportCase ? product->exportedModule.dependencyParameters.value(dependency) : product->dependencyParameters.value(dependency)); - QScriptValue data = getDataForProductScriptValue(engine, dependency.get()); - data.setProperty(DependencyParametersKey, dependencyParametersValue( - product->uniqueName(), dependency->name, params, engine)); - obj.setData(data); - result.setProperty(idx++, obj); + JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass()); + JS_SetPropertyUint32(ctx, data, DependencyParametersKey, dependencyParametersValue( + product->uniqueName(), dependency->name, params, engine)); + defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data); + JS_SetPropertyUint32(ctx, result, idx++, obj); } if (exportCase) { for (const ExportedModuleDependency &m : product->exportedModule.moduleDependencies) { - QScriptValue obj = engine->newObject(); - obj.setProperty(StringConstants::nameProperty(), m.name); - QScriptValue exportsValue = engine->newObject(); - obj.setProperty(exportsProperty(), exportsValue); - exportsValue.setProperty(StringConstants::dependenciesProperty(), - engine->newArray()); + JSValue obj = engine->newObject(); + setJsProperty(ctx, obj, StringConstants::nameProperty(), m.name); + JSValue exportsValue = engine->newObject(); + setJsProperty(ctx, obj, exportsProperty(), exportsValue); + setJsProperty(ctx, exportsValue, StringConstants::dependenciesProperty(), + JS_NewArray(ctx)); for (auto modIt = m.moduleProperties.begin(); modIt != m.moduleProperties.end(); ++modIt) { const QVariantMap entries = modIt.value().toMap(); if (entries.empty()) continue; - QScriptValue moduleObj = engine->newObject(); - ModuleProperties::setModuleScriptValue(exportsValue, moduleObj, modIt.key()); - for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt) - moduleObj.setProperty(valIt.key(), engine->toScriptValue(valIt.value())); + JSValue moduleObj = engine->newObject(); + ModuleProperties::setModuleScriptValue(engine, exportsValue, moduleObj, + modIt.key()); + for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt) { + setJsProperty(ctx, moduleObj, valIt.key(), + engine->toScriptValue(valIt.value())); + } } - result.setProperty(idx++, obj); + JS_SetPropertyUint32(ctx, result, idx++, obj); } return result; } - for (const ResolvedModuleConstPtr &dependency : product->modules) { + for (const auto &dependency : product->modules) { if (dependency->isProduct) continue; - QScriptValue obj = engine->newObject(engine->modulePropertyScriptClass()); - obj.setPrototype(engine->moduleScriptValuePrototype(dependency.get())); - - // The prototype must exist already, because we set it up for all modules - // of the product in ModuleProperties::init(). - QBS_ASSERT(obj.prototype().isValid(), ;); - + JSValue obj = JS_NewObjectClass(ctx, engine->modulePropertyScriptClass()); + attachPointerTo(obj, dependency.get()); const QVariantMap ¶ms = product->moduleParameters.value(dependency); - QScriptValue data = getDataForModuleScriptValue(engine, product, nullptr, - dependency.get()); - data.setProperty(DependencyParametersKey, dependencyParametersValue( - product->uniqueName(), dependency->name, params, engine)); - obj.setData(data); - result.setProperty(idx++, obj); + JSValue data = createDataForModuleScriptValue(engine, nullptr); + JS_SetPropertyUint32(ctx, data, DependencyParametersKey, dependencyParametersValue( + product->uniqueName(), dependency->name, params, engine)); + defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data); + JS_SetPropertyUint32(ctx, result, idx++, obj); } return result; } - static QScriptValue js_internalProductDependencies(QScriptContext *ctx, ScriptEngine *engine, - const ResolvedProduct * const product) + static JSValue js_internalProductDependencies(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const auto product = attachedPointer<ResolvedProduct>(this_val, engine->dataWithPtrClass()); engine->addDependenciesArrayRequested(product); - return js_productDependencies(ctx, engine, product, DependencyType::Internal); + return js_productDependencies(product, ctx, this_val, argc, argv, + DependencyType::Internal); } - static QScriptValue js_exportedProductDependencies(QScriptContext *ctx, ScriptEngine *engine, - const ResolvedProduct * const product) + static JSValue js_exportedProductDependencies(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - return js_productDependencies(ctx, engine, product, DependencyType::Exported); + const auto product = attachedPointer<ResolvedProduct>( + this_val, ScriptEngine::engineForContext(ctx)->dataWithPtrClass()); + return js_productDependencies(product, ctx, this_val, argc, argv, DependencyType::Exported); } ScriptEngine *m_engine; }; -static QScriptValue setupExportedPropertyScriptValue(const ExportedProperty &property, +static JSValue setupExportedPropertyScriptValue(const ExportedProperty &property, ScriptEngine *engine) { - QScriptValue propertyScriptValue = engine->newObject(); - propertyScriptValue.setProperty(StringConstants::nameProperty(), property.fullName); - propertyScriptValue.setProperty(StringConstants::typeProperty(), - PropertyDeclaration::typeString(property.type)); - propertyScriptValue.setProperty(StringConstants::sourceCodeProperty(), property.sourceCode); - propertyScriptValue.setProperty(QStringLiteral("isBuiltin"), property.isBuiltin); + JSValue propertyScriptValue = engine->newObject(); + setJsProperty(engine->context(), propertyScriptValue, StringConstants::nameProperty(), + property.fullName); + setJsProperty(engine->context(), propertyScriptValue, StringConstants::typeProperty(), + PropertyDeclaration::typeString(property.type)); + setJsProperty(engine->context(), propertyScriptValue, StringConstants::sourceCodeProperty(), + property.sourceCode); + JS_SetPropertyStr(engine->context(), propertyScriptValue, "isBuiltin", + JS_NewBool(engine->context(), property.isBuiltin)); return propertyScriptValue; } -static void setupExportedPropertiesScriptValue(QScriptValue &parentObject, +static void setupExportedPropertiesScriptValue(JSValue &parentObject, const std::vector<ExportedProperty> &properties, ScriptEngine *engine) { - QScriptValue propertiesScriptValue = engine->newArray(static_cast<uint>(properties.size())); - parentObject.setProperty(QStringLiteral("properties"), propertiesScriptValue); + JSValue propertiesScriptValue = JS_NewArray(engine->context()); quint32 arrayIndex = 0; for (const ExportedProperty &p : properties) { - propertiesScriptValue.setProperty(arrayIndex++, - setupExportedPropertyScriptValue(p, engine)); + JS_SetPropertyUint32(engine->context(), propertiesScriptValue, arrayIndex++, + setupExportedPropertyScriptValue(p, engine)); } + JS_SetPropertyStr(engine->context(), parentObject, "properties", propertiesScriptValue); } -static QScriptValue setupExportedItemScriptValue(const ExportedItem *item, ScriptEngine *engine) +static JSValue setupExportedItemScriptValue(const ExportedItem *item, ScriptEngine *engine) { - QScriptValue itemScriptValue = engine->newObject(); - itemScriptValue.setProperty(StringConstants::nameProperty(), item->name); + JSValue itemScriptValue = engine->newObject(); + setJsProperty(engine->context(), itemScriptValue, StringConstants::nameProperty(), item->name); setupExportedPropertiesScriptValue(itemScriptValue, item->properties, engine); - QScriptValue childrenScriptValue = engine->newArray(static_cast<uint>(item->children.size())); - itemScriptValue.setProperty(childItemsProperty(), childrenScriptValue); + JSValue childrenScriptValue = JS_NewArray(engine->context()); quint32 arrayIndex = 0; for (const auto &childItem : item->children) { - childrenScriptValue.setProperty(arrayIndex++, - setupExportedItemScriptValue(childItem.get(), engine)); + JS_SetPropertyUint32(engine->context(), childrenScriptValue, arrayIndex++, + setupExportedItemScriptValue(childItem.get(), engine)); } + setJsProperty(engine->context(), itemScriptValue, childItemsProperty(), childrenScriptValue); return itemScriptValue; } -static QScriptValue setupExportsScriptValue(const ExportedModule &module, ScriptEngine *engine) +static JSValue setupExportsScriptValue(const ResolvedProduct *product, ScriptEngine *engine) { - QScriptValue exportsScriptValue = engine->newObject(); - for (auto it = module.propertyValues.cbegin(); it != module.propertyValues.cend(); ++it) - exportsScriptValue.setProperty(it.key(), engine->toScriptValue(it.value())); + const ExportedModule &module = product->exportedModule; + JSValue exportsScriptValue = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass()); + attachPointerTo(exportsScriptValue, product); + for (auto it = module.propertyValues.cbegin(); it != module.propertyValues.cend(); ++it) { + setJsProperty(engine->context(), exportsScriptValue, it.key(), + engine->toScriptValue(it.value())); + } setupExportedPropertiesScriptValue(exportsScriptValue, module.m_properties, engine); - QScriptValue childrenScriptValue = engine->newArray(static_cast<uint>(module.children.size())); - exportsScriptValue.setProperty(childItemsProperty(), childrenScriptValue); + JSValue childrenScriptValue = JS_NewArray(engine->context()); quint32 arrayIndex = 0; for (const auto &exportedItem : module.children) { - childrenScriptValue.setProperty(arrayIndex++, - setupExportedItemScriptValue(exportedItem.get(), engine)); + JS_SetPropertyUint32(engine->context(), childrenScriptValue, arrayIndex++, + setupExportedItemScriptValue(exportedItem.get(), engine)); } - QScriptValue importsScriptValue = engine->newArray(module.importStatements.size()); - exportsScriptValue.setProperty(StringConstants::importsProperty(), importsScriptValue); + setJsProperty(engine->context(), exportsScriptValue, childItemsProperty(), childrenScriptValue); + JSValue importsScriptValue = JS_NewArray(engine->context()); arrayIndex = 0; - for (const QString &importStatement : module.importStatements) - importsScriptValue.setProperty(arrayIndex++, importStatement); + for (const QString &importStatement : module.importStatements) { + JS_SetPropertyUint32(engine->context(), importsScriptValue, arrayIndex++, + makeJsString(engine->context(), importStatement)); + } + setJsProperty(engine->context(), exportsScriptValue, StringConstants::importsProperty(), + importsScriptValue); for (auto it = module.modulePropertyValues.cbegin(); it != module.modulePropertyValues.cend(); ++it) { const QVariantMap entries = it.value().toMap(); if (entries.empty()) continue; - QScriptValue moduleObject = engine->newObject(); - ModuleProperties::setModuleScriptValue(exportsScriptValue, moduleObject, it.key()); - for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt) - moduleObject.setProperty(valIt.key(), engine->toScriptValue(valIt.value())); + JSValue moduleObject = engine->newObject(); + ModuleProperties::setModuleScriptValue(engine, exportsScriptValue, moduleObject, it.key()); + for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt) { + setJsProperty(engine->context(), moduleObject, valIt.key(), + engine->toScriptValue(valIt.value())); + } } return exportsScriptValue; } -static QScriptValue setupProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product) +static void setupBaseProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product) { - QScriptValue &productScriptValue = engine->productScriptValuePrototype(product); - if (productScriptValue.isValid()) - return productScriptValue; - productScriptValue = engine->newObject(); - ModuleProperties::init(productScriptValue, product); - - QScriptValue artifactsFunc = engine->newFunction(&artifactsScriptValueForProduct, product); - productScriptValue.setProperty(StringConstants::artifactsProperty(), artifactsFunc, - QScriptValue::ReadOnly | QScriptValue::Undeletable - | QScriptValue::PropertyGetter); - - QScriptValue exportsScriptValue = setupExportsScriptValue(product->exportedModule, engine); - DependenciesFunction(engine).init(productScriptValue, exportsScriptValue, product); + JSValue &productScriptValue = engine->baseProductScriptValue(product); + if (JS_IsObject(productScriptValue)) + return; + const ScopedJsValue proto(engine->context(), JS_NewObject(engine->context())); + productScriptValue = JS_NewObjectProtoClass(engine->context(), proto, + engine->dataWithPtrClass()); + attachPointerTo(productScriptValue, product); + ModuleProperties::init(engine, productScriptValue, product); + + const QByteArray funcName = StringConstants::artifactsProperty().toUtf8(); + const ScopedJsValue artifactsFunc( + engine->context(), + JS_NewCFunction(engine->context(), &artifactsScriptValueForProduct, + funcName.constData(), 0)); + const ScopedJsAtom artifactsAtom(engine->context(), funcName); + JS_DefineProperty(engine->context(), productScriptValue, artifactsAtom, + JS_UNDEFINED, artifactsFunc, JS_UNDEFINED, + JS_PROP_HAS_GET | JS_PROP_ENUMERABLE); + + // FIXME: Proper un-observe rather than ref count decrease here. + ScopedJsValue exportsScriptValue(engine->context(), setupExportsScriptValue(product, engine)); + DependenciesFunction(engine).init(productScriptValue, exportsScriptValue); + + // TODO: Why are these necessary? We catch accesses to product.exports in getProductProperty(). + // But the exportsQbs() and exportsPkgConfig() tests fail without them. engine->setObservedProperty(productScriptValue, exportsProperty(), exportsScriptValue); - engine->observer()->addExportsObjectId(exportsScriptValue.objectId(), product); - return productScriptValue; + engine->observer()->addExportsObjectId(jsObjectId(exportsScriptValue), product); } void setupScriptEngineForFile(ScriptEngine *engine, const FileContextBaseConstPtr &fileContext, - QScriptValue targetObject, const ObserveMode &observeMode) + JSValue targetObject, const ObserveMode &observeMode) { engine->import(fileContext, targetObject, observeMode); - JsExtensions::setupExtensions(fileContext->jsExtensions(), targetObject); + JsExtensions::setupExtensions(engine, fileContext->jsExtensions(), targetObject); } void setupScriptEngineForProduct(ScriptEngine *engine, ResolvedProduct *product, - const ResolvedModule *module, QScriptValue targetObject, + const ResolvedModule *module, JSValue targetObject, bool setBuildEnvironment) { - QScriptValue projectScriptValue = setupProjectScriptValue(engine, product->project.lock()); - targetObject.setProperty(StringConstants::projectVar(), projectScriptValue); - + JSValue projectScriptValue = setupProjectScriptValue(engine, product->project.lock()); + setJsProperty(engine->context(), targetObject, StringConstants::projectVar(), + projectScriptValue); if (setBuildEnvironment) { QVariant v; v.setValue<void*>(&product->buildEnvironment); engine->setProperty(StringConstants::qbsProcEnvVarInternal(), v); } - QScriptClass *scriptClass = engine->productPropertyScriptClass(); - if (!scriptClass) { - scriptClass = new ProductPropertyScriptClass(engine); - engine->setProductPropertyScriptClass(scriptClass); + JSClassID scriptClass = engine->productPropertyScriptClass(); + if (scriptClass == 0) { + engine->setProductPropertyScriptClass(engine->registerClass("ProductProperties", nullptr, + nullptr, JS_UNDEFINED, &getProductPropertyNames, &getProductProperty)); + scriptClass = engine->productPropertyScriptClass(); } - QScriptValue productScriptValue = engine->newObject(scriptClass); - productScriptValue.setPrototype(setupProductScriptValue(engine, product)); - targetObject.setProperty(StringConstants::productVar(), productScriptValue); + setupBaseProductScriptValue(engine, product); + JSValue productScriptValue = JS_NewObjectClass(engine->context(), scriptClass); + attachPointerTo(productScriptValue, product); + setJsProperty(engine->context(), targetObject, StringConstants::productVar(), + productScriptValue); - QScriptValue data = getDataForProductScriptValue(engine, product); + JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass()); // If the Rule is in a Module, set up the 'moduleName' property - if (!module->name.isEmpty()) - data.setProperty(ModuleNameKey, module->name); - productScriptValue.setData(data); + if (!module->name.isEmpty()) { + JS_SetPropertyUint32(engine->context(), data, ModuleNameKey, + makeJsString(engine->context(), module->name)); + } + defineJsProperty(engine->context(), productScriptValue, + StringConstants::dataPropertyInternal(), data); } bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode *> &path) @@ -448,7 +520,7 @@ bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode *> &pat return true; } - for (BuildGraphNode * const childNode : qAsConst(u->children)) { + for (BuildGraphNode * const childNode : std::as_const(u->children)) { if (findPath(childNode, v, path)) { path.prepend(u); return true; @@ -493,12 +565,9 @@ static bool existsPath_impl(BuildGraphNode *u, BuildGraphNode *v, NodeSet *seen) if (!seen->insert(u).second) return false; - for (BuildGraphNode * const childNode : qAsConst(u->children)) { - if (existsPath_impl(childNode, v, seen)) - return true; - } - - return false; + return Internal::any_of(u->children, [v, seen](const auto &child) { + return existsPath_impl(child, v, seen); + }); } static bool existsPath(BuildGraphNode *u, BuildGraphNode *v) @@ -641,7 +710,7 @@ void updateArtifactFromSourceArtifact(const ResolvedProductPtr &product, const QVariantMap oldModuleProperties = artifact->properties->value(); setArtifactData(artifact, sourceArtifact); if (oldFileTags != artifact->fileTags() - || oldModuleProperties != artifact->properties->value()) { + || !qVariantMapsEqual(oldModuleProperties, artifact->properties->value())) { invalidateArtifactAsRuleInputIfNecessary(artifact); } } @@ -661,7 +730,7 @@ void provideFullFileTagsAndProperties(Artifact *artifact) artifact->properties = artifact->product->moduleProperties; FileTags allTags = artifact->pureFileTags.empty() ? artifact->product->fileTagsForFileName(artifact->fileName()) : artifact->pureFileTags; - for (const ArtifactPropertiesConstPtr &props : artifact->product->artifactProperties) { + for (const auto &props : artifact->product->artifactProperties) { if (allTags.intersects(props->fileTagsFilter())) { artifact->properties = props->propertyMap(); allTags += props->extraFileTags(); @@ -697,7 +766,7 @@ void updateGeneratedArtifacts(ResolvedProduct *product) provideFullFileTagsAndProperties(artifact); applyPerArtifactProperties(artifact); if (oldFileTags != artifact->fileTags() - || oldModuleProperties != artifact->properties->value()) { + || !qVariantMapsEqual(oldModuleProperties, artifact->properties->value())) { invalidateArtifactAsRuleInputIfNecessary(artifact); } } @@ -733,24 +802,24 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, CycleDetector cycleDetector(logger); cycleDetector.visitProduct(product); const ProductBuildData * const buildData = product->buildData.get(); - for (const ResolvedModuleConstPtr &m : product->modules) + for (const auto &m : product->modules) QBS_CHECK(m->product == product.get()); qCDebug(lcBuildGraph) << "enabled:" << product->enabled << "build data:" << buildData; if (product->enabled) QBS_CHECK(buildData); if (!product->buildData) return; - for (BuildGraphNode * const node : qAsConst(buildData->rootNodes())) { + for (BuildGraphNode * const node : std::as_const(buildData->rootNodes())) { qCDebug(lcBuildGraph).noquote() << "Checking root node" << node->toString(); QBS_CHECK(buildData->allNodes().contains(node)); } Set<QString> filePaths; - for (BuildGraphNode * const node : qAsConst(buildData->allNodes())) { + for (BuildGraphNode * const node : std::as_const(buildData->allNodes())) { qCDebug(lcBuildGraph).noquote() << "Sanity checking node" << node->toString(); QBS_CHECK(node->product == product); - for (const BuildGraphNode * const parent : qAsConst(node->parents)) + for (const BuildGraphNode * const parent : std::as_const(node->parents)) QBS_CHECK(parent->children.contains(node)); - for (BuildGraphNode * const child : qAsConst(node->children)) { + for (BuildGraphNode * const child : std::as_const(node->children)) { QBS_CHECK(child->parents.contains(node)); QBS_CHECK(!child->product.expired()); QBS_CHECK(child->product->buildData); @@ -777,7 +846,7 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, !filePaths.contains(artifact->filePath())); filePaths << artifact->filePath(); - for (Artifact * const child : qAsConst(artifact->childrenAddedByScanner)) + for (Artifact * const child : std::as_const(artifact->childrenAddedByScanner)) QBS_CHECK(artifact->children.contains(child)); const TransformerConstPtr transformer = artifact->transformer; if (artifact->artifactType == Artifact::SourceFile) @@ -796,7 +865,7 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, qCDebug(lcBuildGraph) << "The transformer has" << transformer->outputs.size() << "outputs."; ArtifactSet transformerOutputChildren; - for (const Artifact * const output : qAsConst(transformer->outputs)) { + for (const Artifact * const output : std::as_const(transformer->outputs)) { QBS_CHECK(output->transformer == transformer); transformerOutputChildren.unite(ArtifactSet::filtered(output->children)); for (const Artifact *a : filterByType<Artifact>(output->children)) { @@ -814,14 +883,14 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, } if (lcBuildGraph().isDebugEnabled()) { qCDebug(lcBuildGraph) << "The transformer output children are:"; - for (const Artifact * const a : qAsConst(transformerOutputChildren)) + for (const Artifact * const a : std::as_const(transformerOutputChildren)) qCDebug(lcBuildGraph) << "\t" << a->fileName(); qCDebug(lcBuildGraph) << "The transformer inputs are:"; - for (const Artifact * const a : qAsConst(transformer->inputs)) + for (const Artifact * const a : std::as_const(transformer->inputs)) qCDebug(lcBuildGraph) << "\t" << a->fileName(); } QBS_CHECK(transformer->inputs.size() <= transformerOutputChildren.size()); - for (Artifact * const transformerInput : qAsConst(transformer->inputs)) + for (Artifact * const transformerInput : std::as_const(transformer->inputs)) QBS_CHECK(transformerOutputChildren.contains(transformerInput)); transformer->artifactsMapRequestedInPrepareScript.doSanityChecks(); transformer->artifactsMapRequestedInCommands.doSanityChecks(); @@ -833,10 +902,10 @@ static void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger) { logger.qbsDebug() << "Sanity checking project '" << project->name << "'"; - for (const ResolvedProjectPtr &subProject : qAsConst(project->subProjects)) + for (const ResolvedProjectPtr &subProject : std::as_const(project->subProjects)) doSanityChecks(subProject, allProducts, productNames, logger); - for (const ResolvedProductConstPtr &product : project->products) { + for (const auto &product : project->products) { QBS_CHECK(product->project == project); QBS_CHECK(product->topLevelProject() == project->topLevelProject()); doSanityChecksForProduct(product, allProducts, logger); @@ -850,8 +919,7 @@ void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger) if (qEnvironmentVariableIsEmpty("QBS_SANITY_CHECKS")) return; Set<QString> productNames; - const Set<ResolvedProductPtr> allProducts - = Set<ResolvedProductPtr>::fromStdVector(project->allProducts()); + const auto allProducts = rangeTo<Set<ResolvedProductPtr>>(project->allProducts()); doSanityChecks(project, allProducts, productNames, logger); } diff --git a/src/lib/corelib/buildgraph/buildgraph.h b/src/lib/corelib/buildgraph/buildgraph.h index 2909b06bb..1eadb2e20 100644 --- a/src/lib/corelib/buildgraph/buildgraph.h +++ b/src/lib/corelib/buildgraph/buildgraph.h @@ -43,9 +43,10 @@ #include <language/forward_decls.h> #include <tools/qbs_export.h> -#include <QtCore/qstringlist.h> +#include <quickjs.h> -#include <QtScript/qscriptvalue.h> +#include <QtCore/qstringlist.h> +#include <QtCore/QVariantMap> namespace qbs { namespace Internal { @@ -88,14 +89,18 @@ void removeGeneratedArtifactFromDisk(const QString &filePath, const Logger &logg void disconnect(BuildGraphNode *u, BuildGraphNode *v); void setupScriptEngineForFile(ScriptEngine *engine, const FileContextBaseConstPtr &fileContext, - QScriptValue targetObject, const ObserveMode &observeMode); + JSValue targetObject, const ObserveMode &observeMode); void setupScriptEngineForProduct(ScriptEngine *engine, ResolvedProduct *product, - const ResolvedModule *module, QScriptValue targetObject, + const ResolvedModule *module, JSValue targetObject, bool setBuildEnvironment); QString relativeArtifactFileName(const Artifact *artifact); // Debugging helpers void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger); +void getPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, + const QVariantMap &properties, const QStringList &extraPropertyNames, + JSValueConst extraObject); + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/buildgraph/buildgraph.pri b/src/lib/corelib/buildgraph/buildgraph.pri deleted file mode 100644 index 2ed6be4fd..000000000 --- a/src/lib/corelib/buildgraph/buildgraph.pri +++ /dev/null @@ -1,89 +0,0 @@ -include(../../../install_prefix.pri) - -SOURCES += \ - $$PWD/abstractcommandexecutor.cpp \ - $$PWD/artifact.cpp \ - $$PWD/artifactcleaner.cpp \ - $$PWD/artifactsscriptvalue.cpp \ - $$PWD/artifactvisitor.cpp \ - $$PWD/buildgraph.cpp \ - $$PWD/buildgraphloader.cpp \ - $$PWD/buildgraphnode.cpp \ - $$PWD/cycledetector.cpp \ - $$PWD/dependencyparametersscriptvalue.cpp \ - $$PWD/depscanner.cpp \ - $$PWD/emptydirectoriesremover.cpp \ - $$PWD/environmentscriptrunner.cpp \ - $$PWD/executor.cpp \ - $$PWD/executorjob.cpp \ - $$PWD/filedependency.cpp \ - $$PWD/inputartifactscanner.cpp \ - $$PWD/jscommandexecutor.cpp \ - $$PWD/nodeset.cpp \ - $$PWD/nodetreedumper.cpp \ - $$PWD/processcommandexecutor.cpp \ - $$PWD/productbuilddata.cpp \ - $$PWD/productinstaller.cpp \ - $$PWD/projectbuilddata.cpp \ - $$PWD/qtmocscanner.cpp \ - $$PWD/rawscanneddependency.cpp \ - $$PWD/rawscanresults.cpp \ - $$PWD/requestedartifacts.cpp \ - $$PWD/requesteddependencies.cpp \ - $$PWD/rulecommands.cpp \ - $$PWD/rulegraph.cpp \ - $$PWD/rulenode.cpp \ - $$PWD/rulesapplicator.cpp \ - $$PWD/rulesevaluationcontext.cpp \ - $$PWD/timestampsupdater.cpp \ - $$PWD/transformerchangetracking.cpp \ - $$PWD/transformer.cpp - -HEADERS += \ - $$PWD/abstractcommandexecutor.h \ - $$PWD/artifact.h \ - $$PWD/artifactcleaner.h \ - $$PWD/artifactsscriptvalue.h \ - $$PWD/artifactvisitor.h \ - $$PWD/buildgraph.h \ - $$PWD/buildgraphloader.h \ - $$PWD/buildgraphnode.h \ - $$PWD/buildgraphvisitor.h \ - $$PWD/cycledetector.h \ - $$PWD/dependencyparametersscriptvalue.h \ - $$PWD/depscanner.h \ - $$PWD/emptydirectoriesremover.h \ - $$PWD/environmentscriptrunner.h \ - $$PWD/executor.h \ - $$PWD/executorjob.h \ - $$PWD/filedependency.h \ - $$PWD/forward_decls.h \ - $$PWD/inputartifactscanner.h \ - $$PWD/jscommandexecutor.h \ - $$PWD/nodeset.h \ - $$PWD/nodetreedumper.h \ - $$PWD/processcommandexecutor.h \ - $$PWD/productbuilddata.h \ - $$PWD/productinstaller.h \ - $$PWD/projectbuilddata.h \ - $$PWD/qtmocscanner.h \ - $$PWD/rawscanneddependency.h \ - $$PWD/rawscanresults.h \ - $$PWD/requestedartifacts.h \ - $$PWD/requesteddependencies.h \ - $$PWD/rescuableartifactdata.h \ - $$PWD/rulecommands.h \ - $$PWD/rulegraph.h \ - $$PWD/rulenode.h \ - $$PWD/rulesapplicator.h \ - $$PWD/rulesevaluationcontext.h \ - $$PWD/scriptclasspropertyiterator.h \ - $$PWD/timestampsupdater.h \ - $$PWD/transformerchangetracking.h \ - $$PWD/transformer.h - -!qbs_no_dev_install { - buildgraph_headers.files = $$PWD/forward_decls.h - buildgraph_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/buildgraph - INSTALLS += buildgraph_headers -} diff --git a/src/lib/corelib/buildgraph/buildgraphloader.cpp b/src/lib/corelib/buildgraph/buildgraphloader.cpp index cad236db7..ffae52ab2 100644 --- a/src/lib/corelib/buildgraph/buildgraphloader.cpp +++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp @@ -39,31 +39,32 @@ #include "buildgraphloader.h" #include "buildgraph.h" -#include "cycledetector.h" #include "emptydirectoriesremover.h" #include "productbuilddata.h" #include "projectbuilddata.h" #include "rulenode.h" #include "rulecommands.h" -#include "rulesevaluationcontext.h" #include "transformer.h" +#include <buildgraph/rulesevaluationcontext.h> #include <language/artifactproperties.h> #include <language/language.h> -#include <language/loader.h> #include <language/propertymapinternal.h> #include <language/qualifiedid.h> #include <language/resolvedfilecontext.h> +#include <loader/projectresolver.h> #include <logging/categories.h> #include <logging/translator.h> #include <tools/buildgraphlocker.h> #include <tools/fileinfo.h> +#include <tools/jsliterals.h> #include <tools/persistence.h> #include <tools/profile.h> #include <tools/profiling.h> #include <tools/qbsassert.h> #include <tools/qttools.h> #include <tools/settings.h> +#include <tools/stlutils.h> #include <tools/stringconstants.h> #include <QtCore/qdir.h> @@ -93,7 +94,7 @@ static void restoreBackPointers(const ResolvedProjectPtr &project) product->project = project; if (!product->buildData) continue; - for (BuildGraphNode * const n : qAsConst(product->buildData->allNodes())) { + for (BuildGraphNode * const n : std::as_const(product->buildData->allNodes())) { if (n->type() == BuildGraphNode::ArtifactNodeType) { project->topLevelProject()->buildData ->insertIntoLookupTable(static_cast<Artifact *>(n)); @@ -101,7 +102,7 @@ static void restoreBackPointers(const ResolvedProjectPtr &project) } } - for (const ResolvedProjectPtr &subProject : qAsConst(project->subProjects)) { + for (const ResolvedProjectPtr &subProject : std::as_const(project->subProjects)) { subProject->parentProject = project; restoreBackPointers(subProject); } @@ -127,7 +128,7 @@ BuildGraphLoadResult BuildGraphLoader::load(const TopLevelProjectPtr &existingPr if (!m_result.loadedProject) return m_result; if (parameters.restoreBehavior() == SetupProjectParameters::RestoreOnly) { - for (const ErrorInfo &e : qAsConst(m_result.loadedProject->warningsEncountered)) + for (const ErrorInfo &e : std::as_const(m_result.loadedProject->warningsEncountered)) m_logger.printWarning(e); return m_result; } @@ -218,7 +219,7 @@ bool BuildGraphLoader::checkBuildGraphCompatibility(const TopLevelProjectConstPt if (m_parameters.projectFilePath().isEmpty()) m_parameters.setProjectFilePath(project->location.filePath()); else - Loader::setupProjectFilePath(m_parameters); + m_parameters.finalizeProjectFilePath(); if (QFileInfo(project->location.filePath()) == QFileInfo(m_parameters.projectFilePath())) return true; QString message = Tr::tr("Stored build graph at '%1' is for project file '%2', but " @@ -242,7 +243,7 @@ static bool checkProductForChangedDependency(std::vector<ResolvedProductPtr> &ch return false; if (contains(changedProducts, product)) return true; - for (const ResolvedProductPtr &dep : qAsConst(product->dependencies)) { + for (const ResolvedProductPtr &dep : std::as_const(product->dependencies)) { if (checkProductForChangedDependency(changedProducts, seenProducts, dep)) { changedProducts << product; return true; @@ -267,14 +268,14 @@ static void makeChangedProductsListComplete(std::vector<ResolvedProductPtr> &cha static void updateProductAndRulePointers(const ResolvedProductPtr &newProduct) { std::unordered_map<RuleConstPtr, RuleConstPtr> ruleMap; - for (BuildGraphNode *node : qAsConst(newProduct->buildData->allNodes())) { + for (BuildGraphNode *node : std::as_const(newProduct->buildData->allNodes())) { node->product = newProduct; const auto findNewRule = [&ruleMap, &newProduct] (const RuleConstPtr &oldRule) -> RuleConstPtr { const auto it = ruleMap.find(oldRule); if (it != ruleMap.cend()) return it->second; - for (const RuleConstPtr &r : qAsConst(newProduct->rules)) { + for (const auto &r : std::as_const(newProduct->rules)) { if (*r == *oldRule) { ruleMap.insert(std::make_pair(oldRule, r)); return r; @@ -306,8 +307,10 @@ void BuildGraphLoader::trackProjectChanges() std::vector<ResolvedProductPtr> allRestoredProducts = restoredProject->allProducts(); std::vector<ResolvedProductPtr> changedProducts; bool reResolvingNecessary = false; - if (!checkConfigCompatibility()) + if (!checkConfigCompatibility()) { + m_logger.qbsInfo() << Tr::tr("One or more properties have changed."); reResolvingNecessary = true; + } if (hasProductFileChanged(allRestoredProducts, restoredProject->lastStartResolveTime, buildSystemFiles, changedProducts)) { reResolvingNecessary = true; @@ -328,33 +331,41 @@ void BuildGraphLoader::trackProjectChanges() } if (!reResolvingNecessary) { - for (const ErrorInfo &e : qAsConst(restoredProject->warningsEncountered)) + for (const ErrorInfo &e : std::as_const(restoredProject->warningsEncountered)) m_logger.printWarning(e); return; } + for (const QString &file : m_changedProjectFiles) { + m_logger.qbsInfo() << Tr::tr("Project file '%1' has changed.") + .arg(QDir::toNativeSeparators(file)); + } + for (const QString &file : m_removedProjectFiles) { + m_logger.qbsInfo() << Tr::tr("Project file '%1' was removed.") + .arg(QDir::toNativeSeparators(file)); + } + restoredProject->buildData->setDirty(); markTransformersForChangeTracking(allRestoredProducts); if (!m_parameters.overrideBuildGraphData()) m_parameters.setEnvironment(restoredProject->environment); - Loader ldr(m_evalContext->engine(), m_logger); - ldr.setSearchPaths(m_parameters.searchPaths()); - ldr.setProgressObserver(m_evalContext->observer()); - ldr.setOldProjectProbes(restoredProject->probes); + ProjectResolver resolver(m_parameters, m_evalContext->engine(), m_logger); + resolver.setProgressObserver(m_evalContext->observer()); + resolver.setOldProjectProbes(restoredProject->probes); if (!m_parameters.forceProbeExecution()) - ldr.setStoredModuleProviderInfo(restoredProject->moduleProviderInfo); - ldr.setLastResolveTime(restoredProject->lastStartResolveTime); + resolver.setStoredModuleProviderInfo(restoredProject->moduleProviderInfo); + resolver.setLastResolveTime(restoredProject->lastStartResolveTime); QHash<QString, std::vector<ProbeConstPtr>> restoredProbes; - for (const auto &restoredProduct : qAsConst(allRestoredProducts)) + for (const auto &restoredProduct : std::as_const(allRestoredProducts)) restoredProbes.insert(restoredProduct->uniqueName(), restoredProduct->probes); - ldr.setOldProductProbes(restoredProbes); + resolver.setOldProductProbes(restoredProbes); if (!m_parameters.overrideBuildGraphData()) - ldr.setStoredProfiles(restoredProject->profileConfigs); - m_result.newlyResolvedProject = ldr.loadProject(m_parameters); + resolver.setStoredProfiles(restoredProject->profileConfigs); + m_result.newlyResolvedProject = resolver.resolve(); std::vector<ResolvedProductPtr> allNewlyResolvedProducts = m_result.newlyResolvedProject->allProducts(); - for (const ResolvedProductPtr &cp : qAsConst(allNewlyResolvedProducts)) + for (const ResolvedProductPtr &cp : std::as_const(allNewlyResolvedProducts)) m_freshProductsByName.insert(cp->uniqueName(), cp); checkAllProductsForChanges(allRestoredProducts, changedProducts); @@ -363,7 +374,7 @@ void BuildGraphLoader::trackProjectChanges() ChildListHash childLists; if (!changedProducts.empty()) { oldBuildData = std::make_shared<ProjectBuildData>(restoredProject->buildData.get()); - for (const ResolvedProductConstPtr &product : qAsConst(allRestoredProducts)) { + for (const auto &product : std::as_const(allRestoredProducts)) { if (!product->buildData) continue; @@ -382,7 +393,7 @@ void BuildGraphLoader::trackProjectChanges() // mean that artifacts will have to get rebuilt; whether this is necesessary will be decided // an a per-artifact basis by the Executor on the next build. QHash<QString, AllRescuableArtifactData> rescuableArtifactData; - for (const ResolvedProductPtr &product : qAsConst(changedProducts)) { + for (const ResolvedProductPtr &product : std::as_const(changedProducts)) { const QString name = product->uniqueName(); m_changedSourcesByProduct.erase(name); m_productsWhoseArtifactsNeedUpdate.remove(name); @@ -426,7 +437,7 @@ void BuildGraphLoader::trackProjectChanges() } // Products still left in the list do not exist anymore. - for (const ResolvedProductPtr &removedProduct : qAsConst(allRestoredProducts)) { + for (const ResolvedProductPtr &removedProduct : std::as_const(allRestoredProducts)) { removeOne(changedProducts, removedProduct); onProductRemoved(removedProduct, m_result.newlyResolvedProject->buildData.get()); } @@ -452,7 +463,7 @@ void BuildGraphLoader::trackProjectChanges() updateGeneratedArtifacts(product.get()); } - for (const ResolvedProductConstPtr &changedProduct : qAsConst(changedProducts)) { + for (const auto &changedProduct : std::as_const(changedProducts)) { rescueOldBuildData(changedProduct, m_freshProductsByName.value(changedProduct->uniqueName()), childLists, rescuableArtifactData.value(changedProduct->uniqueName())); @@ -461,7 +472,7 @@ void BuildGraphLoader::trackProjectChanges() EmptyDirectoriesRemover(m_result.newlyResolvedProject.get(), m_logger) .removeEmptyParentDirectories(m_artifactsRemovedFromDisk); - for (FileResourceBase * const f : qAsConst(m_objectsToDelete)) { + for (FileResourceBase * const f : std::as_const(m_objectsToDelete)) { if (f->fileType() == FileResourceBase::FileTypeArtifact) static_cast<Artifact *>(f)->product.reset(); // To help with the sanity checks. } @@ -478,12 +489,9 @@ bool BuildGraphLoader::probeExecutionForced( if (!restoredProject->probes.empty()) return true; - for (const auto &p : qAsConst(restoredProducts)) { - if (!p->probes.empty()) - return true; - } - - return false; + return Internal::any_of(restoredProducts, [](const auto &p) { + return !p->probes.empty(); + }); } bool BuildGraphLoader::hasEnvironmentChanged(const TopLevelProjectConstPtr &restoredProject) const @@ -500,9 +508,11 @@ bool BuildGraphLoader::hasEnvironmentChanged(const TopLevelProjectConstPtr &rest newEnv.remove(ldPreloadEnvVar); if (oldEnv != newEnv) { - qCDebug(lcBuildGraph) << "Set of environment variables changed. Must re-resolve project." - << "\nold:" << restoredProject->environment.toStringList() - << "\nnew:" << m_parameters.adjustedEnvironment().toStringList(); + m_logger.qbsInfo() << Tr::tr("Environment changed."); + m_logger.qbsDebug() << Tr::tr("old: %1").arg( + restoredProject->environment.toStringList().join(QLatin1Char('\n'))); + m_logger.qbsDebug() << Tr::tr("new: %2").arg( + m_parameters.adjustedEnvironment().toStringList().join(QLatin1Char('\n'))); return true; } return false; @@ -513,8 +523,8 @@ bool BuildGraphLoader::hasCanonicalFilePathResultChanged(const TopLevelProjectCo for (auto it = restoredProject->canonicalFilePathResults.constBegin(); it != restoredProject->canonicalFilePathResults.constEnd(); ++it) { if (QFileInfo(it.key()).canonicalFilePath() != it.value()) { - qCDebug(lcBuildGraph) << "Canonical file path for file" << it.key() - << "changed, must re-resolve project."; + m_logger.qbsInfo() << Tr::tr("Canonical file path for file '%1' changed.") + .arg(QDir::toNativeSeparators(it.key())); return true; } } @@ -527,8 +537,8 @@ bool BuildGraphLoader::hasFileExistsResultChanged(const TopLevelProjectConstPtr for (QHash<QString, bool>::ConstIterator it = restoredProject->fileExistsResults.constBegin(); it != restoredProject->fileExistsResults.constEnd(); ++it) { if (FileInfo(it.key()).exists() != it.value()) { - qCDebug(lcBuildGraph) << "Existence check for file" << it.key() - << "changed, must re-resolve project."; + m_logger.qbsInfo() << Tr::tr("Existence check for file '%1' changed.") + .arg(QDir::toNativeSeparators(it.key())); return true; } } @@ -542,9 +552,8 @@ bool BuildGraphLoader::hasDirectoryEntriesResultChanged(const TopLevelProjectCon it != restoredProject->directoryEntriesResults.constEnd(); ++it) { if (QDir(it.key().first).entryList(static_cast<QDir::Filters>(it.key().second), QDir::Name) != it.value()) { - qCDebug(lcBuildGraph) << "Entry list for directory" << it.key().first - << static_cast<QDir::Filters>(it.key().second) - << "changed, must re-resolve project."; + m_logger.qbsInfo() << Tr::tr("Entry list for directory '%1' changed.") + .arg(QDir::toNativeSeparators(it.key().first)); return true; } } @@ -558,8 +567,8 @@ bool BuildGraphLoader::hasFileLastModifiedResultChanged(const TopLevelProjectCon = restoredProject->fileLastModifiedResults.constBegin(); it != restoredProject->fileLastModifiedResults.constEnd(); ++it) { if (FileInfo(it.key()).lastModified() != it.value()) { - qCDebug(lcBuildGraph) << "Timestamp for file" << it.key() - << "changed, must re-resolve project."; + m_logger.qbsInfo() << Tr::tr("Timestamp for file '%1' has changed.") + .arg(QDir::toNativeSeparators(it.key())); return true; } } @@ -577,17 +586,18 @@ bool BuildGraphLoader::hasProductFileChanged(const std::vector<ResolvedProductPt const FileInfo pfi(filePath); remainingBuildSystemFiles.remove(filePath); if (!pfi.exists()) { - qCDebug(lcBuildGraph) << "A product was removed, must re-resolve project"; + m_removedProjectFiles << filePath; hasChanged = true; } else if (referenceTime < pfi.lastModified()) { - qCDebug(lcBuildGraph) << "A product was changed, must re-resolve project"; + m_changedProjectFiles << filePath; hasChanged = true; } else if (!contains(changedProducts, product)) { bool foundMissingSourceFile = false; - for (const QString &file : qAsConst(product->missingSourceFiles)) { + for (const QString &file : std::as_const(product->missingSourceFiles)) { if (FileInfo(file).exists()) { - qCDebug(lcBuildGraph) << "Formerly missing file" << file << "in product" - << product->name << "exists now, must re-resolve project"; + m_logger.qbsInfo() + << Tr::tr("Formerly missing file '%1' in product '%2' exists now.") + .arg(QDir::toNativeSeparators(filePath), product->fullDisplayName()); foundMissingSourceFile = true; break; } @@ -601,27 +611,14 @@ bool BuildGraphLoader::hasProductFileChanged(const std::vector<ResolvedProductPt AccumulatingTimer wildcardTimer(m_parameters.logElapsedTime() ? &m_wildcardExpansionEffort : nullptr); for (const GroupPtr &group : product->groups) { - if (!group->wildcards) - continue; - const bool reExpansionRequired = std::any_of( - group->wildcards->dirTimeStamps.cbegin(), - group->wildcards->dirTimeStamps.cend(), - [](const std::pair<QString, FileTime> &pair) { - return FileInfo(pair.first).lastModified() > pair.second; - }); - if (!reExpansionRequired) - continue; - const Set<QString> files = group->wildcards->expandPatterns(group, - FileInfo::path(group->location.filePath()), - product->topLevelProject()->buildDirectory); - Set<QString> wcFiles; - for (const SourceArtifactConstPtr &sourceArtifact : group->wildcards->files) - wcFiles += sourceArtifact->absoluteFilePath; - if (files == wcFiles) - continue; - hasChanged = true; - changedProducts.push_back(product); - break; + if (group->wildcards && group->wildcards->hasChangedSinceExpansion()) { + m_logger.qbsInfo() + << Tr::tr("Must re-expand wildcards for group '%1' in product '%2'.") + .arg(group->name, product->fullDisplayName()); + hasChanged = true; + changedProducts.push_back(product); + break; + } } } } @@ -635,19 +632,18 @@ bool BuildGraphLoader::hasBuildSystemFileChanged(const Set<QString> &buildSystem for (const QString &file : buildSystemFiles) { const FileInfo fi(file); if (!fi.exists()) { - qCDebug(lcBuildGraph) << "Project file" << file - << "no longer exists, must re-resolve project."; + m_removedProjectFiles << file; return true; } const auto generatedChecker = [&file, restoredProject](const ModuleProviderInfo &mpi) { return file.startsWith(mpi.outputDirPath(restoredProject->buildDirectory)); }; - const bool fileWasCreatedByModuleProvider = any_of(restoredProject->moduleProviderInfo, - generatedChecker); + const bool fileWasCreatedByModuleProvider = + any_of(restoredProject->moduleProviderInfo.providers, generatedChecker); const FileTime referenceTime = fileWasCreatedByModuleProvider ? restoredProject->lastEndResolveTime : restoredProject->lastStartResolveTime; if (referenceTime < fi.lastModified()) { - qCDebug(lcBuildGraph) << "Project file" << file << "changed, must re-resolve project."; + m_changedProjectFiles << file; return true; } } @@ -742,9 +738,9 @@ static bool dependenciesAreEqual(const ResolvedProductConstPtr &p1, return false; Set<QString> names1; Set<QString> names2; - for (const ResolvedProductConstPtr &dep : qAsConst(p1->dependencies)) + for (const auto &dep : std::as_const(p1->dependencies)) names1 << dep->uniqueName(); - for (const ResolvedProductConstPtr &dep : qAsConst(p2->dependencies)) + for (const auto &dep : std::as_const(p2->dependencies)) names2 << dep->uniqueName(); return names1 == names2; } @@ -774,10 +770,11 @@ bool BuildGraphLoader::checkProductForInstallInfoChanges(const ResolvedProductPt << StringConstants::installDirProperty() << StringConstants::installPrefixProperty() << StringConstants::installRootProperty(); for (const QString &key : specialProperties) { - if (restoredProduct->moduleProperties->qbsPropertyValue(key) - != newlyResolvedProduct->moduleProperties->qbsPropertyValue(key)) { + if (!qVariantsEqual( + restoredProduct->moduleProperties->qbsPropertyValue(key), + newlyResolvedProduct->moduleProperties->qbsPropertyValue(key))) { qCDebug(lcBuildGraph).noquote().nospace() - << "Product property 'qbs." << key << "' changed."; + << "Product property 'qbs." << key << "' changed."; return true; } } @@ -823,7 +820,7 @@ void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product, removeOne(product->project->products, product); if (product->buildData) { - for (BuildGraphNode * const node : qAsConst(product->buildData->allNodes())) { + for (BuildGraphNode * const node : std::as_const(product->buildData->allNodes())) { if (node->type() == BuildGraphNode::ArtifactNodeType) { const auto artifact = static_cast<Artifact *>(node); projectBuildData->removeArtifact(artifact, m_logger, removeArtifactsFromDisk, @@ -831,10 +828,10 @@ void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product, if (removeArtifactsFromDisk && artifact->artifactType == Artifact::Generated) m_artifactsRemovedFromDisk << artifact->filePath(); } else { - for (BuildGraphNode * const parent : qAsConst(node->parents)) + for (BuildGraphNode * const parent : std::as_const(node->parents)) parent->children.remove(node); node->parents.clear(); - for (BuildGraphNode * const child : qAsConst(node->children)) + for (BuildGraphNode * const child : std::as_const(node->children)) child->parents.remove(node); node->children.clear(); } @@ -863,15 +860,32 @@ void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPt bool BuildGraphLoader::checkConfigCompatibility() { const TopLevelProjectConstPtr restoredProject = m_result.loadedProject; - if (m_parameters.topLevelProfile().isEmpty()) + if (m_parameters.topLevelProfile().isEmpty()) { m_parameters.setTopLevelProfile(restoredProject->profile()); + m_parameters.expandBuildConfiguration(); + } if (!m_parameters.overrideBuildGraphData()) { if (!m_parameters.overriddenValues().empty() - && m_parameters.overriddenValues() != restoredProject->overriddenValues) { + && !qVariantMapsEqual( + m_parameters.overriddenValues(), restoredProject->overriddenValues)) { + const auto toUserOutput = [](const QVariantMap &propMap) { + QString o; + for (auto it = propMap.begin(); it != propMap.end(); ++it) { + if (!o.isEmpty()) + o += QLatin1Char(' '); + o.append(it.key()).append(QLatin1Char(':')).append(toJSLiteral(it.value())); + } + return o; + }; throw ErrorInfo(Tr::tr("Property values set on the command line differ from the " - "ones used for the previous build. Use the 'resolve' command if " - "you really want to rebuild with the new properties.")); - } + "ones used for the previous build.\n" + "Old property values: %1\n" + "New property values: %2\n" + "Use the 'resolve' command if " + "you really want to rebuild with the new properties.") + .arg(toUserOutput(restoredProject->overriddenValues), + toUserOutput(m_parameters.overriddenValues()))); + } m_parameters.setOverriddenValues(restoredProject->overriddenValues); if (m_parameters.topLevelProfile() != restoredProject->profile()) { throw ErrorInfo(Tr::tr("The current profile is '%1', but profile '%2' was used " @@ -886,11 +900,12 @@ bool BuildGraphLoader::checkConfigCompatibility() } if (!m_parameters.overrideBuildGraphData()) return true; - if (m_parameters.finalBuildConfigurationTree() != restoredProject->buildConfiguration()) + if (!qVariantMapsEqual( + m_parameters.finalBuildConfigurationTree(), restoredProject->buildConfiguration())) return false; Settings settings(m_parameters.settingsDirectory()); - for (QVariantMap::ConstIterator it = restoredProject->profileConfigs.constBegin(); - it != restoredProject->profileConfigs.constEnd(); ++it) { + const QVariantMap profileConfigsTree = restoredProject->fullProfileConfigsTree(); + for (auto it = profileConfigsTree.begin(); it != profileConfigsTree.end(); ++it) { const Profile profile(it.key(), &settings); const QVariantMap buildConfig = SetupProjectParameters::expandedBuildConfiguration( profile, m_parameters.configurationName()); @@ -956,14 +971,12 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore rad.lastPrepareScriptExecutionTime = oldArtifact->transformer->lastPrepareScriptExecutionTime; const ChildrenInfo &childrenInfo = childLists.value(oldArtifact); - for (Artifact * const child : qAsConst(childrenInfo.children)) { + for (Artifact * const child : std::as_const(childrenInfo.children)) { rad.children.emplace_back(child->product->name, child->product->multiplexConfigurationId, child->filePath(), childrenInfo.childrenAddedByScanner.contains(child)); - std::transform(oldArtifact->fileDependencies.cbegin(), - oldArtifact->fileDependencies.cend(), - std::back_inserter(rad.fileDependencies), - std::mem_fn(&FileDependency::filePath)); + transform(oldArtifact->fileDependencies, rad.fileDependencies, + std::mem_fn(&FileDependency::filePath)); } newlyResolvedProduct->buildData->addRescuableArtifactData(oldArtifact->filePath(), rad); } diff --git a/src/lib/corelib/buildgraph/buildgraphloader.h b/src/lib/corelib/buildgraph/buildgraphloader.h index a89272958..02b00e29d 100644 --- a/src/lib/corelib/buildgraph/buildgraphloader.h +++ b/src/lib/corelib/buildgraph/buildgraphloader.h @@ -46,6 +46,7 @@ #include <language/forward_decls.h> #include <logging/logger.h> +#include <tools/set.h> #include <tools/setupprojectparameters.h> #include <QtCore/qprocess.h> @@ -122,8 +123,8 @@ private: struct ChildrenInfo { ChildrenInfo() = default; - ChildrenInfo(const ArtifactSet &c1, const ArtifactSet &c2) - : children(c1), childrenAddedByScanner(c2) {} + ChildrenInfo(ArtifactSet c1, ArtifactSet c2) + : children(std::move(c1)), childrenAddedByScanner(std::move(c2)) {} ArtifactSet children; ArtifactSet childrenAddedByScanner; }; @@ -140,6 +141,8 @@ private: Logger m_logger; QStringList m_artifactsRemovedFromDisk; std::unordered_map<QString, std::vector<SourceArtifactConstPtr>> m_changedSourcesByProduct; + Set<QString> m_changedProjectFiles; + Set<QString> m_removedProjectFiles; Set<QString> m_productsWhoseArtifactsNeedUpdate; qint64 m_wildcardExpansionEffort = 0; qint64 m_propertyComparisonEffort = 0; diff --git a/src/lib/corelib/buildgraph/buildgraphnode.cpp b/src/lib/corelib/buildgraph/buildgraphnode.cpp index 7d011d50c..cb19ee95c 100644 --- a/src/lib/corelib/buildgraph/buildgraphnode.cpp +++ b/src/lib/corelib/buildgraph/buildgraphnode.cpp @@ -56,9 +56,9 @@ BuildGraphNode::BuildGraphNode() : buildState(Untouched) BuildGraphNode::~BuildGraphNode() { - for (BuildGraphNode *p : qAsConst(parents)) + for (BuildGraphNode *p : std::as_const(parents)) p->children.remove(this); - for (BuildGraphNode *c : qAsConst(children)) + for (BuildGraphNode *c : std::as_const(children)) c->parents.remove(this); } @@ -69,7 +69,7 @@ void BuildGraphNode::onChildDisconnected(BuildGraphNode *child) void BuildGraphNode::acceptChildren(BuildGraphVisitor *visitor) { - for (BuildGraphNode *child : qAsConst(children)) + for (BuildGraphNode *child : std::as_const(children)) child->accept(visitor); } diff --git a/src/lib/corelib/buildgraph/cycledetector.cpp b/src/lib/corelib/buildgraph/cycledetector.cpp index 5daed55fd..3a1c43cd1 100644 --- a/src/lib/corelib/buildgraph/cycledetector.cpp +++ b/src/lib/corelib/buildgraph/cycledetector.cpp @@ -91,7 +91,7 @@ bool CycleDetector::visitNode(BuildGraphNode *node) m_nodesInCurrentPath += node; m_parent = node; - for (BuildGraphNode * const child : qAsConst(node->children)) + for (BuildGraphNode * const child : std::as_const(node->children)) child->accept(this); m_nodesInCurrentPath -= node; m_allNodes += node; diff --git a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp index f1bf8db13..7b333d32f 100644 --- a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp +++ b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp @@ -46,37 +46,39 @@ namespace qbs { namespace Internal { -static QScriptValue toScriptValue(ScriptEngine *engine, const QString &productName, - const QVariantMap &v, const QString &depName, - const QualifiedId &moduleName) +static JSValue toScriptValue(ScriptEngine *engine, const QString &productName, + const QVariantMap &v, const QString &depName, + const QualifiedId &moduleName) { - QScriptValue obj = engine->newObject(); + JSValue obj = engine->newObject(); bool objIdAddedToObserver = false; for (auto it = v.begin(); it != v.end(); ++it) { - if (it.value().type() == QVariant::Map) { - obj.setProperty(it.key(), toScriptValue(engine, productName, it.value().toMap(), - depName, QualifiedId(moduleName) << it.key())); + if (it.value().userType() == QMetaType::QVariantMap) { + setJsProperty(engine->context(), obj, it.key(), + toScriptValue(engine, productName, it.value().toMap(), + depName, QualifiedId(moduleName) << it.key())); } else { if (!objIdAddedToObserver) { objIdAddedToObserver = true; - engine->observer()->addParameterObjectId(obj.objectId(), productName, depName, + engine->observer()->addParameterObjectId(jsObjectId(obj), productName, depName, moduleName); } - engine->setObservedProperty(obj, it.key(), engine->toScriptValue(it.value())); + const ScopedJsValue val(engine->context(), engine->toScriptValue(it.value())); + engine->setObservedProperty(obj, it.key(), val); } } return obj; } -static QScriptValue toScriptValue(ScriptEngine *scriptEngine, const QString &productName, - const QVariantMap &v, const QString &depName) +static JSValue toScriptValue(ScriptEngine *scriptEngine, const QString &productName, + const QVariantMap &v, const QString &depName) { return toScriptValue(scriptEngine, productName, v, depName, {}); } -QScriptValue dependencyParametersValue(const QString &productName, const QString &dependencyName, - const QVariantMap ¶metersMap, ScriptEngine *engine) +JSValue dependencyParametersValue(const QString &productName, const QString &dependencyName, + const QVariantMap ¶metersMap, ScriptEngine *engine) { return toScriptValue(engine, productName, parametersMap, dependencyName); } diff --git a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h index 7e4287be6..2fb4634e8 100644 --- a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h +++ b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h @@ -39,15 +39,16 @@ #ifndef QBS_DEPENDENCYPARAMETERSSCRIPTVALUE_H #define QBS_DEPENDENCYPARAMETERSSCRIPTVALUE_H +#include <quickjs.h> + #include <QtCore/qvariant.h> -#include <QtScript/qscriptvalue.h> namespace qbs { namespace Internal { class ScriptEngine; -QScriptValue dependencyParametersValue(const QString &productName, const QString &dependencyName, - const QVariantMap ¶metersMap, ScriptEngine *engine); +JSValue dependencyParametersValue(const QString &productName, const QString &dependencyName, + const QVariantMap ¶metersMap, ScriptEngine *engine); } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/buildgraph/depscanner.cpp b/src/lib/corelib/buildgraph/depscanner.cpp index 0bf644286..01e781a54 100644 --- a/src/lib/corelib/buildgraph/depscanner.cpp +++ b/src/lib/corelib/buildgraph/depscanner.cpp @@ -56,7 +56,7 @@ #include <QtCore/qvariant.h> -#include <QtScript/qscriptcontext.h> +#include <vector> namespace qbs { namespace Internal { @@ -110,7 +110,7 @@ QStringList PluginDependencyScanner::collectDependencies(Artifact *artifact, Fil void *scannerHandle = m_plugin->open(filepath.utf16(), fileTags, ScanForDependenciesFlag); if (!scannerHandle) return {}; - forever { + for (;;) { int flags = 0; int length = 0; const char *szOutFilePath = m_plugin->next(scannerHandle, &length, &flags); @@ -127,7 +127,7 @@ QStringList PluginDependencyScanner::collectDependencies(Artifact *artifact, Fil result += outFilePath; } m_plugin->close(scannerHandle); - return result.toList(); + return rangeTo<QStringList>(result); } bool PluginDependencyScanner::recursive() const @@ -158,10 +158,9 @@ UserDependencyScanner::UserDependencyScanner(ResolvedScannerConstPtr scanner, ScriptEngine *engine) : m_scanner(std::move(scanner)), m_engine(engine), + m_global(engine->context(), JS_NewObjectProto(engine->context(), m_engine->globalObject())), m_product(nullptr) { - m_global = m_engine->newObject(); - m_global.setPrototype(m_engine->globalObject()); setupScriptEngineForFile(m_engine, m_scanner->scanScript.fileContext(), m_global, ObserveMode::Disabled); // TODO: QBS-1092 } @@ -218,7 +217,7 @@ public: } }; -QStringList UserDependencyScanner::evaluate(const Artifact *artifact, +QStringList UserDependencyScanner::evaluate(Artifact *artifact, const FileResourceBase *fileToScan, const PrivateScriptFunction &script) { ScriptEngineActiveFlagGuard guard(m_engine); @@ -229,38 +228,37 @@ QStringList UserDependencyScanner::evaluate(const Artifact *artifact, m_scanner->module.get(), m_global, true); } - QScriptValueList args; + JSValueList args; args.reserve(fileToScan ? 4 : 3); - args.push_back(m_global.property(StringConstants::projectVar())); - args.push_back(m_global.property(StringConstants::productVar())); + args.push_back(getJsProperty(m_engine->context(), m_global, StringConstants::projectVar())); + args.push_back(getJsProperty(m_engine->context(), m_global, StringConstants::productVar())); args.push_back(Transformer::translateFileConfig(m_engine, artifact, m_scanner->module->name)); if (fileToScan) - args.push_back(fileToScan->filePath()); + args.push_back(makeJsString(m_engine->context(), fileToScan->filePath())); + const ScopedJsValueList argsMgr(m_engine->context(), args); - m_engine->setGlobalObject(m_global); - QScriptValue &function = script.scriptFunction; - if (!function.isValid() || function.engine() != m_engine) { - function = m_engine->evaluate(script.sourceCode()); - if (Q_UNLIKELY(!function.isFunction())) - throw ErrorInfo(Tr::tr("Invalid scan script."), script.location()); - } - QScriptValue result = function.call(QScriptValue(), args); - m_engine->setGlobalObject(m_global.prototype()); + const TemporaryGlobalObjectSetter gos(m_engine, m_global); + const JSValue function = script.getFunction(m_engine, Tr::tr("Invalid scan script.")); + const ScopedJsValue result( + m_engine->context(), + JS_Call(m_engine->context(), function, m_engine->globalObject(), + int(args.size()), args.data())); m_engine->clearRequestedProperties(); - if (Q_UNLIKELY(m_engine->hasErrorOrException(result))) { - QString msg = Tr::tr("evaluating scan script: ") + m_engine->lastErrorString(result); - const CodeLocation loc = m_engine->lastErrorLocation(result, script.location()); - m_engine->clearExceptions(); - throw ErrorInfo(msg, loc); + if (JsException ex = m_engine->checkAndClearException(script.location())) { + ErrorInfo err = ex.toErrorInfo(); + err.prepend(Tr::tr("Error evaluating scan script")); + throw err; } QStringList list; - if (result.isArray()) { - const int count = result.property(StringConstants::lengthProperty()).toInt32(); + if (JS_IsArray(m_engine->context(), result)) { + const int count = getJsIntProperty(m_engine->context(), result, + StringConstants::lengthProperty()); list.reserve(count); for (qint32 i = 0; i < count; ++i) { - QScriptValue item = result.property(i); - if (item.isValid() && !item.isUndefined()) - list.push_back(item.toString()); + JSValue item = JS_GetPropertyUint32(m_engine->context(), result, i); + if (!JS_IsUninitialized(item) && !JS_IsUndefined(item)) + list.push_back(getJsString(m_engine->context(), item)); + JS_FreeValue(m_engine->context(), item); } } return list; diff --git a/src/lib/corelib/buildgraph/depscanner.h b/src/lib/corelib/buildgraph/depscanner.h index 6b18004f9..51816dbd7 100644 --- a/src/lib/corelib/buildgraph/depscanner.h +++ b/src/lib/corelib/buildgraph/depscanner.h @@ -43,11 +43,10 @@ #include <language/forward_decls.h> #include <language/filetags.h> #include <language/preparescriptobserver.h> +#include <tools/scripttools.h> #include <QtCore/qstringlist.h> -#include <QtScript/qscriptvalue.h> - class ScannerPlugin; namespace qbs { @@ -115,11 +114,11 @@ private: const PropertyMapConstPtr &m2) const override; bool cacheIsPerFile() const override { return true; } - QStringList evaluate(const Artifact *artifact, const FileResourceBase *fileToScan, const PrivateScriptFunction &script); + QStringList evaluate(Artifact *artifact, const FileResourceBase *fileToScan, const PrivateScriptFunction &script); ResolvedScannerConstPtr m_scanner; ScriptEngine *m_engine; - QScriptValue m_global; + ScopedJsValue m_global; ResolvedProduct *m_product; }; diff --git a/src/lib/corelib/buildgraph/environmentscriptrunner.cpp b/src/lib/corelib/buildgraph/environmentscriptrunner.cpp index 7d82efb41..0fbb3ab19 100644 --- a/src/lib/corelib/buildgraph/environmentscriptrunner.cpp +++ b/src/lib/corelib/buildgraph/environmentscriptrunner.cpp @@ -45,11 +45,14 @@ #include <language/propertymapinternal.h> #include <language/resolvedfilecontext.h> #include <language/scriptengine.h> +#include <logging/translator.h> #include <tools/qbsassert.h> #include <tools/qttools.h> -#include <logging/translator.h> +#include <tools/stlutils.h> #include <tools/stringconstants.h> +#include <quickjs.h> + #include <QtCore/qhash.h> #include <QtCore/qvariant.h> @@ -123,19 +126,18 @@ void EnvironmentScriptRunner::setupEnvironment() const auto hasScript = [this](const ResolvedModuleConstPtr &m) { return !getScript(m.get()).sourceCode().isEmpty(); }; - const bool hasAnyScripts = std::any_of(m_product->modules.cbegin(), m_product->modules.cend(), - hasScript); + const bool hasAnyScripts = Internal::any_of(m_product->modules, hasScript); if (!hasAnyScripts) return; QMap<QString, const ResolvedModule *> moduleMap; - for (const ResolvedModuleConstPtr &module : m_product->modules) + for (const auto &module : m_product->modules) moduleMap.insert(module->name, module.get()); QHash<const ResolvedModule*, QList<const ResolvedModule*> > moduleParents; QHash<const ResolvedModule*, QList<const ResolvedModule*> > moduleChildren; - for (const ResolvedModuleConstPtr &module : m_product->modules) { - for (const QString &moduleName : qAsConst(module->moduleDependencies)) { + for (const auto &module : m_product->modules) { + for (const QString &moduleName : std::as_const(module->moduleDependencies)) { const ResolvedModule * const depmod = moduleMap.value(moduleName); QBS_ASSERT(depmod, return); moduleParents[depmod].push_back(module.get()); @@ -144,7 +146,7 @@ void EnvironmentScriptRunner::setupEnvironment() } QList<const ResolvedModule *> rootModules; - for (const ResolvedModuleConstPtr &module : m_product->modules) { + for (const auto &module : m_product->modules) { if (moduleParents.value(module.get()).isEmpty()) { QBS_ASSERT(module, return); rootModules.push_back(module.get()); @@ -165,38 +167,40 @@ void EnvironmentScriptRunner::setupEnvironment() continue; RulesEvaluationContext::Scope s(m_evalContext); - QScriptValue envScriptContext = engine()->newObject(); - envScriptContext.setPrototype(engine()->globalObject()); + JSContext * const ctx = engine()->context(); + ScopedJsValue envScriptContext(ctx, JS_NewObjectProto(ctx, engine()->globalObject())); setupScriptEngineForProduct(engine(), m_product, module, envScriptContext, false); const QString &productKey = StringConstants::productVar(); const QString &projectKey = StringConstants::projectVar(); - m_evalContext->scope().setProperty(productKey, envScriptContext.property(productKey)); - m_evalContext->scope().setProperty(projectKey, envScriptContext.property(projectKey)); + setJsProperty(ctx, m_evalContext->scope(), productKey, + getJsProperty(ctx, envScriptContext, productKey)); + setJsProperty(ctx, m_evalContext->scope(), projectKey, + getJsProperty(ctx, envScriptContext, projectKey)); if (m_envType == RunEnv) { - QScriptValue configArray = engine()->newArray(m_runEnvConfig.size()); + JSValue configArray = JS_NewArray(ctx); for (int i = 0; i < m_runEnvConfig.size(); ++i) - configArray.setProperty(i, QScriptValue(m_runEnvConfig.at(i))); - m_evalContext->scope().setProperty(QStringLiteral("config"), configArray); + JS_SetPropertyUint32(ctx, configArray, i, makeJsString(ctx, m_runEnvConfig.at(i))); + JS_SetPropertyStr(ctx, m_evalContext->scope(), "config", configArray); } setupScriptEngineForFile(engine(), setupScript.fileContext(), m_evalContext->scope(), ObserveMode::Disabled); // TODO: Cache evaluate result - QScriptValue fun = engine()->evaluate(setupScript.sourceCode(), - setupScript.location().filePath(), - setupScript.location().line()); - QBS_CHECK(fun.isFunction()); - const QScriptValueList svArgs = ScriptEngine::argumentList(scriptFunctionArgs, - m_evalContext->scope()); - const QScriptValue res = fun.call(QScriptValue(), svArgs); - engine()->releaseResourcesOfScriptObjects(); - if (Q_UNLIKELY(engine()->hasErrorOrException(res))) { + ScopedJsValue fun(ctx, engine()->evaluate(JsValueOwner::Caller, setupScript.sourceCode(), + setupScript.location().filePath(), + setupScript.location().line())); + QBS_CHECK(JS_IsFunction(ctx, fun)); + const ScopedJsValueList svArgs = engine()->argumentList(scriptFunctionArgs, + m_evalContext->scope()); + JSValueList argsForFun = svArgs; + JS_Call(ctx, fun, engine()->globalObject(), int(argsForFun.size()), argsForFun.data()); + if (const JsException ex = engine()->checkAndClearException(setupScript.location())) { const QString scriptName = m_envType == BuildEnv ? StringConstants::setupBuildEnvironmentProperty() : StringConstants::setupRunEnvironmentProperty(); - throw ErrorInfo(Tr::tr("Error running %1 script for product '%2': %3") - .arg(scriptName, m_product->fullDisplayName(), - engine()->lastErrorString(res)), - engine()->lastErrorLocation(res, setupScript.location())); + ErrorInfo err = ex.toErrorInfo(); + err.prepend(Tr::tr("Error running %1 script for product '%2'") + .arg(scriptName, m_product->fullDisplayName())); + throw err; } } diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp index de81ada20..2b8232a0a 100644 --- a/src/lib/corelib/buildgraph/executor.cpp +++ b/src/lib/corelib/buildgraph/executor.cpp @@ -67,6 +67,7 @@ #include <tools/qbsassert.h> #include <tools/qttools.h> #include <tools/settings.h> +#include <tools/stlutils.h> #include <tools/stringconstants.h> #include <QtCore/qdir.h> @@ -102,12 +103,8 @@ Executor::Executor(Logger logger, QObject *parent) Executor::~Executor() { - // jobs must be destroyed before deleting the shared scan result cache - for (ExecutorJob *job : qAsConst(m_availableJobs)) - delete job; - const auto processingJobs = m_processingJobs.keys(); - for (ExecutorJob *job : processingJobs) - delete job; + // jobs must be destroyed before deleting the m_inputArtifactScanContext + m_allJobs.clear(); delete m_inputArtifactScanContext; delete m_productInstaller; } @@ -138,11 +135,9 @@ void Executor::retrieveSourceFileTimestamp(Artifact *artifact) const { QBS_CHECK(artifact->artifactType == Artifact::SourceFile); - if (m_buildOptions.changedFiles().empty()) - artifact->setTimestamp(recursiveFileTime(artifact->filePath())); - else if (m_buildOptions.changedFiles().contains(artifact->filePath())) + if (m_buildOptions.changedFiles().contains(artifact->filePath())) artifact->setTimestamp(FileTime::currentTime()); - else if (!artifact->timestamp().isValid()) + else if (m_buildOptions.changedFiles().empty() || !artifact->timestamp().isValid()) artifact->setTimestamp(recursiveFileTime(artifact->filePath())); artifact->timestampRetrieved = true; @@ -153,7 +148,7 @@ void Executor::retrieveSourceFileTimestamp(Artifact *artifact) const void Executor::build() { try { - m_partialBuild = m_productsToBuild.size() != m_allProducts.size(); + m_partialBuild = size_t(m_productsToBuild.size()) != m_allProducts.size(); doBuild(); } catch (const ErrorInfo &e) { handleError(e); @@ -170,7 +165,7 @@ void Executor::setProject(const TopLevelProjectPtr &project) m_projectsByName.insert(std::make_pair(p->name, p.get())); } -void Executor::setProducts(const std::vector<ResolvedProductPtr> &productsToBuild) +void Executor::setProducts(const QVector<ResolvedProductPtr> &productsToBuild) { m_productsToBuild = productsToBuild; m_productsByName.clear(); @@ -197,7 +192,7 @@ public: allDependencies += dep; } const Set<ResolvedProductPtr> rootProducts - = Set<ResolvedProductPtr>::fromStdVector(m_allProducts) - allDependencies; + = rangeTo<Set<ResolvedProductPtr>>(m_allProducts) - allDependencies; m_priority = UINT_MAX; m_seenProducts.clear(); for (const ResolvedProductPtr &rootProduct : rootProducts) @@ -209,7 +204,7 @@ private: { if (!m_seenProducts.insert(product).second) return; - for (const ResolvedProductPtr &dependency : qAsConst(product->dependencies)) + for (const ResolvedProductPtr &dependency : std::as_const(product->dependencies)) traverse(dependency); if (!product->buildData) return; @@ -266,9 +261,9 @@ void Executor::doBuild() doSanityChecks(); QBS_CHECK(!m_project->buildData->evaluationContext); - m_project->buildData->evaluationContext - = RulesEvaluationContextPtr(new RulesEvaluationContext(m_logger)); + m_project->buildData->evaluationContext = std::make_shared<RulesEvaluationContext>(m_logger); m_evalContext = m_project->buildData->evaluationContext; + m_progressObserver->addScriptEngine(m_evalContext->engine()); m_elapsedTimeRules = m_elapsedTimeScanners = m_elapsedTimeInstalling = 0; m_evalContext->engine()->enableProfiling(m_buildOptions.logElapsedTime()); @@ -334,7 +329,7 @@ void Executor::updateLeaves(BuildGraphNode *node, NodeSet &seenNodes) } bool isLeaf = true; - for (BuildGraphNode *child : qAsConst(node->children)) { + for (BuildGraphNode *child : std::as_const(node->children)) { if (child->buildState != BuildGraphNode::Built) { isLeaf = false; updateLeaves(child, seenNodes); @@ -402,8 +397,10 @@ bool Executor::schedulingBlockedByJobLimit(const BuildGraphNode *node) if (currentJobCount == 0) continue; const auto jobLimitIsExceeded = [currentJobCount, jobPool, this](const Transformer *t) { - const int maxJobCount = m_jobLimitsPerProduct.at(t->product().get()) - .getLimit(jobPool); + const auto it = m_jobLimitsPerProduct.find(t->product().get()); + if (it == m_jobLimitsPerProduct.cend()) + return false; // See checkNodeProduct() for why this is possible + const int maxJobCount = it->second.getLimit(jobPool); return maxJobCount > 0 && currentJobCount >= maxJobCount; }; @@ -455,7 +452,7 @@ bool Executor::isUpToDate(Artifact *artifact) const return false; } - for (FileDependency *fileDependency : qAsConst(artifact->fileDependencies)) { + for (FileDependency *fileDependency : std::as_const(artifact->fileDependencies)) { if (!fileDependency->timestamp().isValid()) { qCDebug(lcUpToDateCheck) << "file dependency doesn't exist" << fileDependency->filePath(); @@ -482,7 +479,7 @@ bool Executor::mustExecuteTransformer(const TransformerPtr &transformer) const bool hasAlwaysUpdatedArtifacts = false; bool hasUpToDateNotAlwaysUpdatedArtifacts = false; - for (Artifact *artifact : qAsConst(transformer->outputs)) { + for (Artifact *artifact : std::as_const(transformer->outputs)) { if (isUpToDate(artifact)) { if (artifact->alwaysUpdated) hasAlwaysUpdatedArtifacts = true; @@ -571,7 +568,7 @@ void Executor::finishJob(ExecutorJob *job, bool success) updateJobCounts(transformer.get(), -1); if (success) { m_project->buildData->setDirty(); - for (Artifact * const artifact : qAsConst(transformer->outputs)) { + for (Artifact * const artifact : std::as_const(transformer->outputs)) { if (artifact->alwaysUpdated) { artifact->setTimestamp(FileTime::currentTime()); for (Artifact * const parent : artifact->parentArtifacts()) @@ -624,14 +621,13 @@ void Executor::finishJob(ExecutorJob *job, bool success) static bool allChildrenBuilt(BuildGraphNode *node) { - return std::all_of(node->children.cbegin(), node->children.cend(), - std::mem_fn(&BuildGraphNode::isBuilt)); + return Internal::all_of(node->children, std::mem_fn(&BuildGraphNode::isBuilt)); } void Executor::finishNode(BuildGraphNode *leaf) { leaf->buildState = BuildGraphNode::Built; - for (BuildGraphNode * const parent : qAsConst(leaf->parents)) { + for (BuildGraphNode * const parent : std::as_const(leaf->parents)) { if (parent->buildState != BuildGraphNode::Buildable) { qCDebug(lcExec).noquote() << "parent" << parent->toString() << "build state:" << toString(parent->buildState); @@ -666,8 +662,8 @@ bool Executor::transformerHasMatchingOutputTags(const TransformerConstPtr &trans if (m_activeFileTags.empty()) return true; // No filtering requested. - return std::any_of(transformer->outputs.cbegin(), transformer->outputs.cend(), - [this](const Artifact *a) { return artifactHasMatchingOutputTags(a); }); + return Internal::any_of(transformer->outputs, [this](const Artifact *a) { + return artifactHasMatchingOutputTags(a); }); } bool Executor::artifactHasMatchingOutputTags(const Artifact *artifact) const @@ -684,7 +680,7 @@ bool Executor::transformerHasMatchingInputFiles(const TransformerConstPtr &trans return false; if (transformer->inputs.empty()) return true; - for (const Artifact * const input : qAsConst(transformer->inputs)) { + for (const Artifact * const input : std::as_const(transformer->inputs)) { const auto files = m_buildOptions.filesToConsider(); for (const QString &filePath : files) { if (input->filePath() == filePath @@ -700,7 +696,7 @@ bool Executor::transformerHasMatchingInputFiles(const TransformerConstPtr &trans void Executor::setupJobLimits() { Settings settings(m_buildOptions.settingsDirectory()); - for (const ResolvedProductConstPtr &p : m_productsToBuild) { + for (const auto &p : std::as_const(m_productsToBuild)) { const Preferences prefs(&settings, p->profile()); const JobLimits &jobLimitsFromSettings = prefs.jobLimits(); JobLimits effectiveJobLimits; @@ -737,7 +733,7 @@ void Executor::setupProgressObserver() if (!m_progressObserver) return; int totalEffort = 1; // For the effort after the last rule application; - for (const ResolvedProductConstPtr &product : qAsConst(m_productsToBuild)) { + for (const auto &product : std::as_const(m_productsToBuild)) { QBS_CHECK(product->buildData); const auto filtered = filterByType<RuleNode>(product->buildData->allNodes()); totalEffort += std::distance(filtered.begin(), filtered.end()); @@ -749,7 +745,7 @@ void Executor::doSanityChecks() { QBS_CHECK(m_project); QBS_CHECK(!m_productsToBuild.empty()); - for (const ResolvedProductConstPtr &product : qAsConst(m_productsToBuild)) { + for (const auto &product : std::as_const(m_productsToBuild)) { QBS_CHECK(product->buildData); QBS_CHECK(product->topLevelProject() == m_project.get()); } @@ -768,10 +764,13 @@ void Executor::handleError(const ErrorInfo &error) void Executor::addExecutorJobs() { - qCDebug(lcExec) << "preparing executor for" << m_buildOptions.maxJobCount() - << "jobs in parallel"; - for (int i = 1; i <= m_buildOptions.maxJobCount(); i++) { - const auto job = new ExecutorJob(m_logger, this); + const int count = m_buildOptions.maxJobCount(); + qCDebug(lcExec) << "preparing executor for" << count << "jobs in parallel"; + m_allJobs.reserve(count); + m_availableJobs.reserve(count); + for (int i = 1; i <= count; i++) { + m_allJobs.push_back(std::make_unique<ExecutorJob>(m_logger)); + const auto job = m_allJobs.back().get(); job->setMainThreadScriptEngine(m_evalContext->engine()); job->setObjectName(QStringLiteral("J%1").arg(i)); job->setDryRun(m_buildOptions.dryRun()); @@ -917,7 +916,7 @@ bool Executor::checkForUnbuiltDependencies(Artifact *artifact) { bool buildingDependenciesFound = false; NodeSet unbuiltDependencies; - for (BuildGraphNode * const dependency : qAsConst(artifact->children)) { + for (BuildGraphNode * const dependency : std::as_const(artifact->children)) { switch (dependency->buildState) { case BuildGraphNode::Untouched: case BuildGraphNode::Buildable: @@ -948,7 +947,7 @@ bool Executor::checkForUnbuiltDependencies(Artifact *artifact) void Executor::potentiallyRunTransformer(const TransformerPtr &transformer) { - for (Artifact * const output : qAsConst(transformer->outputs)) { + for (Artifact * const output : std::as_const(transformer->outputs)) { // Rescuing build data can introduce new dependencies, potentially delaying execution of // this transformer. bool childrenAddedDueToRescue; @@ -971,7 +970,7 @@ void Executor::potentiallyRunTransformer(const TransformerPtr &transformer) const bool mustExecute = mustExecuteTransformer(transformer); if (mustExecute || m_buildOptions.forceTimestampCheck()) { - for (Artifact * const output : qAsConst(transformer->outputs)) { + for (Artifact * const output : std::as_const(transformer->outputs)) { // Scan all input artifacts. If new dependencies were found during scanning, delay // execution of this transformer. InputArtifactScanner scanner(output, m_inputArtifactScanContext, m_logger); @@ -1002,7 +1001,7 @@ void Executor::runTransformer(const TransformerPtr &transformer) // create the output directories if (!m_buildOptions.dryRun()) { - for (Artifact * const output : qAsConst(transformer->outputs)) { + for (Artifact * const output : std::as_const(transformer->outputs)) { QDir outDir = QFileInfo(output->filePath()).absoluteDir(); if (!outDir.exists() && !outDir.mkpath(StringConstants::dot())) { throw ErrorInfo(tr("Failed to create directory '%1'.") @@ -1013,7 +1012,7 @@ void Executor::runTransformer(const TransformerPtr &transformer) QBS_CHECK(!m_availableJobs.empty()); ExecutorJob *job = m_availableJobs.takeFirst(); - for (Artifact * const artifact : qAsConst(transformer->outputs)) + for (Artifact * const artifact : std::as_const(transformer->outputs)) artifact->buildState = BuildGraphNode::Building; m_processingJobs.insert(job, transformer); updateJobCounts(transformer.get(), 1); @@ -1023,7 +1022,7 @@ void Executor::runTransformer(const TransformerPtr &transformer) void Executor::finishTransformer(const TransformerPtr &transformer) { transformer->markedForRerun = false; - for (Artifact * const artifact : qAsConst(transformer->outputs)) { + for (Artifact * const artifact : std::as_const(transformer->outputs)) { possiblyInstallArtifact(artifact); finishArtifact(artifact); } @@ -1058,7 +1057,7 @@ void Executor::onJobFinished(const qbs::ErrorInfo &err) if (m_evalContext->engine()->isActive()) { qCDebug(lcExec) << "Executor job finished while rule execution is pausing. " "Delaying slot execution."; - QTimer::singleShot(0, job, [job, err] { job->finished(err); }); + QTimer::singleShot(0, job, [job, err] { emit job->finished(err); }); return; } @@ -1084,9 +1083,9 @@ void Executor::checkForUnbuiltProducts() if (m_buildOptions.executeRulesOnly()) return; std::vector<ResolvedProductPtr> unbuiltProducts; - for (const ResolvedProductPtr &product : m_productsToBuild) { + for (const ResolvedProductPtr &product : std::as_const(m_productsToBuild)) { bool productBuilt = true; - for (BuildGraphNode *rootNode : qAsConst(product->buildData->rootNodes())) { + for (BuildGraphNode *rootNode : std::as_const(product->buildData->rootNodes())) { if (rootNode->buildState != BuildGraphNode::Built) { productBuilt = false; unbuiltProducts.push_back(product); @@ -1109,10 +1108,8 @@ void Executor::checkForUnbuiltProducts() m_logger.qbsInfo() << Tr::tr("Build done%1.").arg(configString()); } else { m_error.append(Tr::tr("The following products could not be built%1:").arg(configString())); - QStringList productNames; - std::transform(unbuiltProducts.cbegin(), unbuiltProducts.cend(), - std::back_inserter(productNames), - [](const ResolvedProductConstPtr &p) { return p->fullDisplayName(); }); + auto productNames = transformed<QStringList>(unbuiltProducts, [](const auto &p) { + return p->fullDisplayName(); }); std::sort(productNames.begin(), productNames.end()); m_error.append(productNames.join(QLatin1String(", "))); } @@ -1202,11 +1199,11 @@ void Executor::prepareAllNodes() for (const ResolvedProductPtr &product : m_allProducts) { if (product->enabled) { QBS_CHECK(product->buildData); - for (BuildGraphNode * const node : qAsConst(product->buildData->allNodes())) + for (BuildGraphNode * const node : std::as_const(product->buildData->allNodes())) node->buildState = BuildGraphNode::Untouched; } } - for (const ResolvedProductPtr &product : m_productsToBuild) { + for (const ResolvedProductPtr &product : std::as_const(m_productsToBuild)) { QBS_CHECK(product->buildData); for (Artifact * const artifact : filterByType<Artifact>(product->buildData->allNodes())) prepareArtifact(artifact); @@ -1228,12 +1225,12 @@ void Executor::syncFileDependencies() "removing from lookup table"; m_project->buildData->removeFromLookupTable(dep); bool isReferencedByArtifact = false; - for (const ResolvedProductConstPtr &product : m_allProducts) { + for (const auto &product : m_allProducts) { if (!product->buildData) continue; const auto artifactList = filterByType<Artifact>(product->buildData->allNodes()); - isReferencedByArtifact = std::any_of(artifactList.begin(), artifactList.end(), - [dep](const Artifact *a) { return a->fileDependencies.contains(dep); }); + isReferencedByArtifact = Internal::any_of(artifactList, [dep](const Artifact *a) { + return a->fileDependencies.contains(dep); }); // TODO: Would it be safe to mark the artifact as "not up to date" here and clear // its list of file dependencies, rather than doing the check again in // isUpToDate()? @@ -1290,7 +1287,7 @@ void Executor::setupForBuildingSelectedFiles(const BuildGraphNode *node) */ void Executor::prepareReachableNodes() { - for (BuildGraphNode * const root : qAsConst(m_roots)) + for (BuildGraphNode * const root : std::as_const(m_roots)) prepareReachableNodes_impl(root); } @@ -1302,7 +1299,7 @@ void Executor::prepareReachableNodes_impl(BuildGraphNode *node) return; node->buildState = BuildGraphNode::Buildable; - for (BuildGraphNode *child : qAsConst(node->children)) + for (BuildGraphNode *child : std::as_const(node->children)) prepareReachableNodes_impl(child); } @@ -1310,7 +1307,7 @@ void Executor::prepareProducts() { ProductPrioritySetter prioritySetter(m_allProducts); prioritySetter.apply(); - for (const ResolvedProductPtr &product : m_productsToBuild) { + for (const ResolvedProductPtr &product : std::as_const(m_productsToBuild)) { EnvironmentScriptRunner(product.get(), m_evalContext.get(), m_project->environment) .setupForBuild(); } @@ -1319,7 +1316,7 @@ void Executor::prepareProducts() void Executor::setupRootNodes() { m_roots.clear(); - for (const ResolvedProductPtr &product : m_productsToBuild) + for (const ResolvedProductPtr &product : std::as_const(m_productsToBuild)) m_roots += product->buildData->rootNodes(); } diff --git a/src/lib/corelib/buildgraph/executor.h b/src/lib/corelib/buildgraph/executor.h index 1fd591176..cc879e125 100644 --- a/src/lib/corelib/buildgraph/executor.h +++ b/src/lib/corelib/buildgraph/executor.h @@ -81,7 +81,7 @@ public: ~Executor() override; void setProject(const TopLevelProjectPtr &project); - void setProducts(const std::vector<ResolvedProductPtr> &productsToBuild); + void setProducts(const QVector<ResolvedProductPtr> &productsToBuild); void setBuildOptions(const BuildOptions &buildOptions); void setProgressObserver(ProgressObserver *observer) { m_progressObserver = observer; } @@ -166,10 +166,11 @@ private: BuildOptions m_buildOptions; Logger m_logger; ProgressObserver *m_progressObserver; + std::vector<std::unique_ptr<ExecutorJob>> m_allJobs; QList<ExecutorJob*> m_availableJobs; ExecutorState m_state; TopLevelProjectPtr m_project; - std::vector<ResolvedProductPtr> m_productsToBuild; + QVector<ResolvedProductPtr> m_productsToBuild; std::vector<ResolvedProductPtr> m_allProducts; std::unordered_map<QString, const ResolvedProduct *> m_productsByName; std::unordered_map<QString, const ResolvedProject *> m_projectsByName; diff --git a/src/lib/corelib/buildgraph/executorjob.h b/src/lib/corelib/buildgraph/executorjob.h index bc8954072..1f8f0cd73 100644 --- a/src/lib/corelib/buildgraph/executorjob.h +++ b/src/lib/corelib/buildgraph/executorjob.h @@ -65,7 +65,7 @@ class ExecutorJob : public QObject { Q_OBJECT public: - ExecutorJob(const Logger &logger, QObject *parent); + explicit ExecutorJob(const Logger &logger, QObject *parent = nullptr); ~ExecutorJob() override; void setMainThreadScriptEngine(ScriptEngine *engine); diff --git a/src/lib/corelib/buildgraph/filedependency.h b/src/lib/corelib/buildgraph/filedependency.h index 802654e9f..93bfc5d05 100644 --- a/src/lib/corelib/buildgraph/filedependency.h +++ b/src/lib/corelib/buildgraph/filedependency.h @@ -77,8 +77,8 @@ private: FileTime m_timestamp; QString m_filePath; - QStringRef m_dirPath; - QStringRef m_fileName; + QStringView m_dirPath; + QStringView m_fileName; }; class FileDependency : public FileResourceBase diff --git a/src/lib/corelib/buildgraph/inputartifactscanner.cpp b/src/lib/corelib/buildgraph/inputartifactscanner.cpp index 14f39bb2a..0c73f599f 100644 --- a/src/lib/corelib/buildgraph/inputartifactscanner.cpp +++ b/src/lib/corelib/buildgraph/inputartifactscanner.cpp @@ -49,11 +49,12 @@ #include <language/language.h> #include <logging/categories.h> +#include <tools/error.h> #include <tools/fileinfo.h> -#include <tools/scannerpluginmanager.h> #include <tools/qbsassert.h> -#include <tools/error.h> #include <tools/qttools.h> +#include <tools/scannerpluginmanager.h> +#include <tools/stlutils.h> #include <QtCore/qdir.h> #include <QtCore/qstringlist.h> @@ -77,6 +78,7 @@ static void resolveDepencency(const RawScannedDependency &dependency, FileDependency *fileDependencyArtifact = nullptr; Artifact *dependencyInProduct = nullptr; Artifact *dependencyInOtherProduct = nullptr; + bool productOfDependencyIsDependency = false; const auto files = project->topLevelProject() ->buildData->lookupFiles(absDirPath, dependency.fileName()); for (FileResourceBase *lookupResult : files) { @@ -86,10 +88,13 @@ static void resolveDepencency(const RawScannedDependency &dependency, break; case FileResourceBase::FileTypeArtifact: { auto const foundArtifact = static_cast<Artifact *>(lookupResult); - if (foundArtifact->product == product) + if (foundArtifact->product == product) { dependencyInProduct = foundArtifact; - else + } else if (!productOfDependencyIsDependency) { dependencyInOtherProduct = foundArtifact; + productOfDependencyIsDependency + = contains(product->dependencies, dependencyInOtherProduct->product.lock()); + } break; } } @@ -98,10 +103,23 @@ static void resolveDepencency(const RawScannedDependency &dependency, } // prioritize found artifacts - if ((result->file = dependencyInProduct) - || (result->file = dependencyInOtherProduct) - || (result->file = fileDependencyArtifact)) { + if (dependencyInProduct) + result->file = dependencyInProduct; + else if (dependencyInOtherProduct) + result->file = dependencyInOtherProduct; + else + result->file = fileDependencyArtifact; + + if (result->file) { result->filePath = result->file->filePath(); + + if (result->file == dependencyInOtherProduct && !productOfDependencyIsDependency) { + qCDebug(lcDepScan) << "product" << dependencyInOtherProduct->product->fullDisplayName() + << "of scanned dependency" << result->filePath + << "is not a dependency of product" << product->fullDisplayName() + << ". The file dependency might get lost during change tracking."; + } + return; } @@ -145,7 +163,7 @@ void InputArtifactScanner::scan() for (Artifact * const dependency : childrenAddedByScanner) disconnect(m_artifact, dependency); - for (Artifact * const inputArtifact : qAsConst(m_artifact->transformer->inputs)) + for (Artifact * const inputArtifact : std::as_const(m_artifact->transformer->inputs)) scanForFileDependencies(inputArtifact); } @@ -200,19 +218,18 @@ Set<DependencyScanner *> InputArtifactScanner::scannersForArtifact(const Artifac InputArtifactScannerContext::DependencyScannerCacheItem &cache = scannerCache[fileTag]; if (!cache.valid) { cache.valid = true; - for (ScannerPlugin *scanner : ScannerPluginManager::scannersForFileTag(fileTag)) { - const auto pluginScanner = new PluginDependencyScanner(scanner); - cache.scanners.push_back(DependencyScannerPtr(pluginScanner)); - } + const auto scanners = ScannerPluginManager::scannersForFileTag(fileTag); + transform(scanners, cache.scanners, [](const auto &scanner) { + return std::make_shared<PluginDependencyScanner>(scanner); }); for (const ResolvedScannerConstPtr &scanner : product->scanners) { if (scanner->inputs.contains(fileTag)) { - cache.scanners.push_back(DependencyScannerPtr( - new UserDependencyScanner(scanner, engine))); + cache.scanners.push_back( + std::make_shared<UserDependencyScanner>(scanner, engine)); break; } } } - for (const DependencyScannerPtr &scanner : qAsConst(cache.scanners)) + for (const DependencyScannerPtr &scanner : std::as_const(cache.scanners)) scanners += scanner.get(); } return scanners; @@ -231,21 +248,16 @@ void InputArtifactScanner::scanForScannerFileDependencies(DependencyScanner *sca cache.searchPaths = scanner->collectSearchPaths(inputArtifact); } qCDebug(lcDepScan) << "include paths (cache" << (cacheHit ? "hit)" : "miss)"); - for (const QString &s : qAsConst(cache.searchPaths)) + for (const QString &s : std::as_const(cache.searchPaths)) qCDebug(lcDepScan) << " " << s; const QString &filePathToBeScanned = fileToBeScanned->filePath(); RawScanResults::ScanData &scanData = m_rawScanResults.findScanData(fileToBeScanned, scanner, m_artifact->properties); if (scanData.lastScanTime < fileToBeScanned->timestamp()) { - try { - qCDebug(lcDepScan) << "scanning" << FileInfo::fileName(filePathToBeScanned); - scanWithScannerPlugin(scanner, inputArtifact, fileToBeScanned, &scanData.rawScanResult); - scanData.lastScanTime = FileTime::currentTime(); - } catch (const ErrorInfo &error) { - m_logger.printWarning(error); - return; - } + qCDebug(lcDepScan) << "scanning" << FileInfo::fileName(filePathToBeScanned); + scanWithScannerPlugin(scanner, inputArtifact, fileToBeScanned, &scanData.rawScanResult); + scanData.lastScanTime = FileTime::currentTime(); } resolveScanResultDependencies(inputArtifact, scanData.rawScanResult, filesToScan, cache); @@ -255,38 +267,45 @@ void InputArtifactScanner::resolveScanResultDependencies(const Artifact *inputAr const RawScanResult &scanResult, QList<FileResourceBase *> *artifactsToScan, InputArtifactScannerContext::ScannerResolvedDependenciesCache &cache) { - for (const RawScannedDependency &dependency : scanResult.deps) { + auto getResolvedDependency = [inputArtifact, &cache](const RawScannedDependency &dependency) + -> ResolvedDependency* + { const QString &dependencyFilePath = dependency.filePath(); InputArtifactScannerContext::ResolvedDependencyCacheItem &cachedResolvedDependencyItem = cache.resolvedDependenciesCache[dependency.dirPath()][dependency.fileName()]; ResolvedDependency &resolvedDependency = cachedResolvedDependencyItem.resolvedDependency; if (cachedResolvedDependencyItem.valid) { if (resolvedDependency.filePath.isEmpty()) - goto unresolved; - goto resolved; + return nullptr; + return &resolvedDependency; } cachedResolvedDependencyItem.valid = true; if (FileInfo::isAbsolute(dependencyFilePath)) { resolveDepencency(dependency, inputArtifact->product.get(), &resolvedDependency); if (resolvedDependency.filePath.isEmpty()) - goto unresolved; - goto resolved; + return nullptr; + return &resolvedDependency; } // try include paths - for (const QString &includePath : qAsConst(cache.searchPaths)) { + for (const QString &includePath : std::as_const(cache.searchPaths)) { resolveDepencency(dependency, inputArtifact->product.get(), &resolvedDependency, includePath); if (resolvedDependency.isValid()) - goto resolved; + return &resolvedDependency; } + return nullptr; + }; -unresolved: - qCWarning(lcDepScan) << "unresolved dependency " << dependencyFilePath; - continue; + for (const RawScannedDependency &dependency : scanResult.deps) { + const auto maybeResolvedDependency = getResolvedDependency(dependency); + if (!maybeResolvedDependency) { + qCWarning(lcDepScan) << "unresolved dependency " << dependency.filePath(); + continue; + } + auto &resolvedDependency = *maybeResolvedDependency; -resolved: handleDependency(resolvedDependency); if (artifactsToScan && resolvedDependency.file) { if (resolvedDependency.file->fileType() == FileResourceBase::FileTypeArtifact) { @@ -349,6 +368,8 @@ void InputArtifactScanner::handleDependency(ResolvedDependency &dependency) if (m_artifact == dependency.file) return; + if (artifactDependency && artifactDependency->transformer == m_artifact->transformer) + return; if (fileDependency) { m_artifact->fileDependencies << fileDependency; diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.cpp b/src/lib/corelib/buildgraph/jscommandexecutor.cpp index aa72a7b5c..4d92bf7c3 100644 --- a/src/lib/corelib/buildgraph/jscommandexecutor.cpp +++ b/src/lib/corelib/buildgraph/jscommandexecutor.cpp @@ -40,7 +40,6 @@ #include "jscommandexecutor.h" -#include "artifact.h" #include "buildgraph.h" #include "rulecommands.h" #include "transformer.h" @@ -53,8 +52,12 @@ #include <tools/codelocation.h> #include <tools/error.h> #include <tools/qbsassert.h> +#include <tools/qttools.h> + +#include <quickjs.h> #include <QtCore/qeventloop.h> +#include <QtCore/qmutex.h> #include <QtCore/qpointer.h> #include <QtCore/qthread.h> #include <QtCore/qtimer.h> @@ -86,10 +89,11 @@ public: void cancel(const qbs::ErrorInfo &reason) { + QMutexLocker locker(&m_resultMutex); m_result.success = !reason.hasError(); m_result.errorMessage = reason.toString(); if (m_scriptEngine) - m_scriptEngine->abortEvaluation(); + m_scriptEngine->cancel(); m_cancelled = true; } @@ -121,40 +125,34 @@ private: m_result.success = true; m_result.errorMessage.clear(); ScriptEngine * const scriptEngine = provideScriptEngine(); - QScriptValue scope = scriptEngine->newObject(); - scope.setPrototype(scriptEngine->globalObject()); - m_scriptEngine->clearRequestedProperties(); + JSContext * const ctx = scriptEngine->context(); + const ScopedJsValue scope(ctx, JS_NewObject(scriptEngine->context())); setupScriptEngineForFile(scriptEngine, transformer->rule->prepareScript.fileContext(), scope, ObserveMode::Enabled); - - QScriptValue importScopeForSourceCode; + ScopedJsValue importScopeForSourceCode(ctx, JS_UNDEFINED); if (!cmd->scopeName().isEmpty()) - importScopeForSourceCode = scope.property(cmd->scopeName()); + importScopeForSourceCode.setValue(getJsProperty(ctx, scope, cmd->scopeName())); setupScriptEngineForProduct(scriptEngine, transformer->product().get(), transformer->rule->module.get(), scope, true); - transformer->setupInputs(scope); - transformer->setupOutputs(scope); - transformer->setupExplicitlyDependsOn(scope); - - for (QVariantMap::const_iterator it = cmd->properties().constBegin(); - it != cmd->properties().constEnd(); ++it) { - scope.setProperty(it.key(), scriptEngine->toScriptValue(it.value())); - } - - scriptEngine->setGlobalObject(scope); - if (importScopeForSourceCode.isObject()) - scriptEngine->currentContext()->pushScope(importScopeForSourceCode); - scriptEngine->evaluate(cmd->sourceCode()); - scriptEngine->releaseResourcesOfScriptObjects(); - if (importScopeForSourceCode.isObject()) - scriptEngine->currentContext()->popScope(); - scriptEngine->setGlobalObject(scope.prototype()); + transformer->setupInputs(scriptEngine, scope); + transformer->setupOutputs(scriptEngine, scope); + transformer->setupExplicitlyDependsOn(scriptEngine, scope); + + for (auto it = cmd->properties().constBegin(); it != cmd->properties().constEnd(); ++it) + setJsProperty(ctx, scope, it.key(), scriptEngine->toScriptValue(it.value())); + + const TemporaryGlobalObjectSetter gos(scriptEngine, scope); + JSValueList scopeChain; + if (JS_IsObject(importScopeForSourceCode)) + scopeChain << importScopeForSourceCode; + const ScopedJsValue res(ctx, scriptEngine->evaluate(JsValueOwner::Caller, cmd->sourceCode(), + {}, 1, scopeChain)); transformer->propertiesRequestedInCommands += scriptEngine->propertiesRequestedInScript(); - transformer->propertiesRequestedFromArtifactInCommands - .unite(scriptEngine->propertiesRequestedFromArtifact()); + unite(transformer->propertiesRequestedFromArtifactInCommands, + scriptEngine->propertiesRequestedFromArtifact()); const std::vector<QString> &importFilesUsedInCommand = scriptEngine->importedFilesUsedInScript(); transformer->importedFilesUsedInCommands.insert( @@ -167,14 +165,17 @@ private: std::make_pair(p->uniqueName(), p->exportedModule)); } scriptEngine->clearRequestedProperties(); - if (scriptEngine->hasUncaughtException()) { + if (const JsException exception = scriptEngine->checkAndClearException(cmd->codeLocation())) { // ### We don't know the line number of the command's sourceCode property assignment. - setError(scriptEngine->uncaughtException().toString(), cmd->codeLocation()); + setError(exception.message(), cmd->codeLocation()); } } void setError(const QString &errorMessage, const CodeLocation &codeLocation) { + QMutexLocker locker(&m_resultMutex); + if (m_cancelled) + return; m_result.success = false; m_result.errorMessage = errorMessage; m_result.errorLocation = codeLocation; @@ -183,13 +184,16 @@ private: ScriptEngine *provideScriptEngine() { if (!m_scriptEngine) - m_scriptEngine = ScriptEngine::create(m_logger, EvalContext::JsCommand, this); - return m_scriptEngine; + m_scriptEngine = ScriptEngine::create(m_logger, EvalContext::JsCommand); + else + m_scriptEngine->reset(); + return m_scriptEngine.get(); } Logger m_logger; - ScriptEngine *m_scriptEngine; + std::unique_ptr<ScriptEngine> m_scriptEngine; JavaScriptCommandResult m_result; + QMutex m_resultMutex; bool m_running = false; bool m_cancelled = false; }; @@ -201,6 +205,9 @@ JsCommandExecutor::JsCommandExecutor(const Logger &logger, QObject *parent) , m_objectInThread(new JsCommandExecutorThreadObject(logger)) , m_running(false) { + qRegisterMetaType<Transformer *>("Transformer *"); + qRegisterMetaType<const JavaScriptCommand *>("const JavaScriptCommand *"); + m_objectInThread->moveToThread(m_thread); connect(m_objectInThread, &JsCommandExecutorThreadObject::finished, this, &JsCommandExecutor::onJavaScriptCommandFinished); @@ -254,11 +261,8 @@ bool JsCommandExecutor::doStart() void JsCommandExecutor::cancel(const qbs::ErrorInfo &reason) { - if (m_running && (!dryRun() || command()->ignoreDryRun())) - QTimer::singleShot(0, m_objectInThread, [objectInThread = QPointer<JsCommandExecutorThreadObject>{m_objectInThread}, reason] { - if (objectInThread) - objectInThread->cancel(reason); - }); + if (m_running && (!dryRun() || command()->ignoreDryRun()) && m_objectInThread) + m_objectInThread->cancel(reason); } void JsCommandExecutor::onJavaScriptCommandFinished() diff --git a/src/lib/corelib/buildgraph/nodeset.h b/src/lib/corelib/buildgraph/nodeset.h index 961a4bcc3..469988b19 100644 --- a/src/lib/corelib/buildgraph/nodeset.h +++ b/src/lib/corelib/buildgraph/nodeset.h @@ -71,11 +71,17 @@ public: { } - class const_iterator : public std::iterator<std::forward_iterator_tag, T *> + class const_iterator { const NodeSet &m_nodes; NodeSet::const_iterator m_it; public: + using value_type = T*; + using difference_type = ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; + const_iterator(const NodeSet &nodes, const NodeSet::const_iterator &it) : m_nodes(nodes), m_it(it) { diff --git a/src/lib/corelib/buildgraph/nodetreedumper.cpp b/src/lib/corelib/buildgraph/nodetreedumper.cpp index 89975baf2..6ad597c70 100644 --- a/src/lib/corelib/buildgraph/nodetreedumper.cpp +++ b/src/lib/corelib/buildgraph/nodetreedumper.cpp @@ -51,13 +51,13 @@ namespace qbs { namespace Internal { -static unsigned int indentWidth() { return 4; } +static int indentWidth() { return 4; } NodeTreeDumper::NodeTreeDumper(QIODevice &outDevice) : m_outDevice(outDevice) { } -void NodeTreeDumper::start(const QList<ResolvedProductPtr> &products) +void NodeTreeDumper::start(const QVector<ResolvedProductPtr> &products) { m_indentation = 0; for (const ResolvedProductPtr &p : products) { @@ -120,7 +120,7 @@ bool NodeTreeDumper::doVisit(BuildGraphNode *node, const QString &nodeRepr) QByteArray NodeTreeDumper::indentation() const { - return QByteArray(m_indentation, ' '); + return {m_indentation, ' '}; } } // namespace Internal diff --git a/src/lib/corelib/buildgraph/nodetreedumper.h b/src/lib/corelib/buildgraph/nodetreedumper.h index 4175ce727..38ccd6dae 100644 --- a/src/lib/corelib/buildgraph/nodetreedumper.h +++ b/src/lib/corelib/buildgraph/nodetreedumper.h @@ -57,7 +57,7 @@ class NodeTreeDumper : public BuildGraphVisitor public: NodeTreeDumper(QIODevice &outDevice); - void start(const QList<ResolvedProductPtr> &products); + void start(const QVector<ResolvedProductPtr> &products); private: bool visit(Artifact *artifact) override; @@ -74,7 +74,7 @@ private: QIODevice &m_outDevice; ResolvedProductPtr m_currentProduct; NodeSet m_visited; - unsigned int m_indentation = 0; + int m_indentation = 0; }; } // namespace Internal diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp index 79edda320..52a8fe75d 100644 --- a/src/lib/corelib/buildgraph/processcommandexecutor.cpp +++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp @@ -61,19 +61,19 @@ #include <tools/shellutils.h> #include <tools/stringconstants.h> +#include <quickjs.h> + #include <QtCore/qdir.h> #include <QtCore/qtemporaryfile.h> #include <QtCore/qtimer.h> -#include <QtScript/qscriptvalue.h> - namespace qbs { namespace Internal { ProcessCommandExecutor::ProcessCommandExecutor(const Logger &logger, QObject *parent) : AbstractCommandExecutor(logger, parent) { - connect(&m_process, static_cast<void (QbsProcess::*)(QProcess::ProcessError)>(&QbsProcess::error), + connect(&m_process, &QbsProcess::errorOccurred, this, &ProcessCommandExecutor::onProcessError); connect(&m_process, static_cast<void (QbsProcess::*)(int)>(&QbsProcess::finished), this, &ProcessCommandExecutor::onProcessFinished); @@ -166,6 +166,7 @@ bool ProcessCommandExecutor::doStart() .arg(responseFile.fileName()))); return false; } + const auto separator = cmd->responseFileSeparator().toUtf8(); for (int i = cmd->responseFileArgumentIndex(); i < cmd->arguments().size(); ++i) { const QString arg = cmd->arguments().at(i); if (arg.startsWith(cmd->responseFileUsagePrefix())) { @@ -179,12 +180,12 @@ bool ProcessCommandExecutor::doStart() } else { responseFile.write(qbs::Internal::shellQuote(arg).toLocal8Bit()); } - responseFile.write("\n"); + responseFile.write(separator); } responseFile.close(); m_responseFileName = responseFile.fileName(); - arguments = arguments.mid(0, - std::min(cmd->responseFileArgumentIndex(), arguments.size())); + arguments = arguments.mid(0, std::min<int>(cmd->responseFileArgumentIndex(), + arguments.size())); arguments += QDir::toNativeSeparators(cmd->responseFileUsagePrefix() + responseFile.fileName()); } @@ -210,38 +211,43 @@ void ProcessCommandExecutor::cancel(const qbs::ErrorInfo &reason) QString ProcessCommandExecutor::filterProcessOutput(const QByteArray &_output, const QString &filterFunctionSource) { - const QString output = QString::fromLocal8Bit(_output); + QString output = QString::fromLocal8Bit(_output); if (filterFunctionSource.isEmpty()) return output; - QScriptValue scope = scriptEngine()->newObject(); - scope.setPrototype(scriptEngine()->globalObject()); - for (QVariantMap::const_iterator it = command()->properties().constBegin(); - it != command()->properties().constEnd(); ++it) { - scope.setProperty(it.key(), scriptEngine()->toScriptValue(it.value())); + JSContext * const ctx = scriptEngine()->context(); + const ScopedJsValue scope(ctx, JS_NewObjectProto(ctx, scriptEngine()->globalObject())); + for (auto it = command()->properties().constBegin(); + it != command()->properties().constEnd(); ++it) { + setJsProperty(ctx, scope, it.key(), scriptEngine()->toScriptValue(it.value())); } - TemporaryGlobalObjectSetter tgos(scope); - QScriptValue filterFunction = scriptEngine()->evaluate(QLatin1String("var f = ") - + filterFunctionSource - + QLatin1String("; f")); - if (!filterFunction.isFunction()) { + TemporaryGlobalObjectSetter tgos(scriptEngine(), scope); + const ScopedJsValue filterFunction( + ctx, + scriptEngine()->evaluate(JsValueOwner::Caller, + QLatin1String("var f = ") + + filterFunctionSource + + QLatin1String("; f"))); + if (!JS_IsFunction(scriptEngine()->context(), filterFunction)) { logger().printWarning(ErrorInfo(Tr::tr("Error in filter function: %1.\n%2") - .arg(filterFunctionSource, filterFunction.toString()))); + .arg(filterFunctionSource, + getJsString(scriptEngine()->context(), filterFunction)))); return output; } - QScriptValue outputArg = scriptEngine()->newArray(1); - outputArg.setProperty(0, scriptEngine()->toScriptValue(output)); - QScriptValue filteredOutput = filterFunction.call(scriptEngine()->undefinedValue(), outputArg); - if (scriptEngine()->hasErrorOrException(filteredOutput)) { - logger().printWarning(ErrorInfo(Tr::tr("Error when calling output filter function: %1") - .arg(scriptEngine()->lastErrorString(filteredOutput)), - scriptEngine()->lastErrorLocation(filteredOutput))); + const ScopedJsValue outputArg(ctx, scriptEngine()->toScriptValue(output)); + JSValue outputArgForCall = outputArg; + const ScopedJsValue filteredOutput( + ctx, JS_Call(ctx, filterFunction, JS_UNDEFINED, 1, &outputArgForCall)); + if (const JsException ex = scriptEngine()->checkAndClearException({})) { + ErrorInfo err = ex.toErrorInfo(); + err.prepend(Tr::tr("Error when calling output filter function")); + logger().printWarning(err); return output; } - return filteredOutput.toString(); + return getJsString(ctx, filteredOutput); } static QProcess::ProcessError saveToFile(const QString &filePath, const QByteArray &content) @@ -285,7 +291,7 @@ void ProcessCommandExecutor::getProcessOutput(bool stdOut, ProcessResult &result } else { if (!contentString.isEmpty() && contentString.endsWith(QLatin1Char('\n'))) contentString.chop(1); - *target = contentString.split(QLatin1Char('\n'), QString::SkipEmptyParts); + *target = contentString.split(QLatin1Char('\n'), Qt::SkipEmptyParts); } } @@ -392,7 +398,7 @@ void ProcessCommandExecutor::doReportCommandDescription(const QString &productNa if (m_echoMode == CommandEchoModeCommandLineWithEnvironment) { QStringList keys = m_commandEnvironment.keys(); keys.sort(); - for (const QString &key : keys) + for (const QString &key : std::as_const(keys)) fullInvocation += environmentVariableString(key, m_commandEnvironment.value(key)); } fullInvocation += m_shellInvocation; diff --git a/src/lib/corelib/buildgraph/productbuilddata.cpp b/src/lib/corelib/buildgraph/productbuilddata.cpp index db51b2b9f..6ec01a1ce 100644 --- a/src/lib/corelib/buildgraph/productbuilddata.cpp +++ b/src/lib/corelib/buildgraph/productbuilddata.cpp @@ -102,7 +102,6 @@ void ProductBuildData::addFileTagToArtifact(Artifact *artifact, const FileTag &t ArtifactSetByFileTag ProductBuildData::artifactsByFileTag() const { - std::lock_guard<std::mutex> l(m_artifactsMapMutex); return m_artifactsByFileTag; } @@ -124,7 +123,6 @@ void ProductBuildData::addRescuableArtifactData(const QString &filePath, bool ProductBuildData::checkAndSetJsArtifactsMapUpToDateFlag() { - std::lock_guard<std::mutex> l(m_artifactsMapMutex); if (!m_jsArtifactsMapUpToDate) { m_jsArtifactsMapUpToDate = true; return false; diff --git a/src/lib/corelib/buildgraph/productbuilddata.h b/src/lib/corelib/buildgraph/productbuilddata.h index a7660af27..f6c531713 100644 --- a/src/lib/corelib/buildgraph/productbuilddata.h +++ b/src/lib/corelib/buildgraph/productbuilddata.h @@ -86,6 +86,9 @@ public: void setBuildPriority(unsigned int prio) { m_buildPriority = prio; } bool checkAndSetJsArtifactsMapUpToDateFlag(); + std::unique_lock<std::mutex> getArtifactsMapLock() { + return std::unique_lock(m_artifactsMapMutex); + } template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool) { diff --git a/src/lib/corelib/buildgraph/productinstaller.cpp b/src/lib/corelib/buildgraph/productinstaller.cpp index d4acc9ace..f757c8a85 100644 --- a/src/lib/corelib/buildgraph/productinstaller.cpp +++ b/src/lib/corelib/buildgraph/productinstaller.cpp @@ -60,7 +60,7 @@ namespace qbs { namespace Internal { ProductInstaller::ProductInstaller(TopLevelProjectPtr project, - std::vector<ResolvedProductPtr> products, InstallOptions options, + QVector<ResolvedProductPtr> products, InstallOptions options, ProgressObserver *observer, Logger logger) : m_project(std::move(project)), m_products(std::move(products)), @@ -96,7 +96,7 @@ void ProductInstaller::install() removeInstallRoot(); QList<const Artifact *> artifactsToInstall; - for (const ResolvedProductConstPtr &product : qAsConst(m_products)) { + for (const auto &product : std::as_const(m_products)) { QBS_CHECK(product->buildData); for (const Artifact *artifact : filterByType<Artifact>(product->buildData->allNodes())) { if (artifact->properties->qbsPropertyValue(StringConstants::installProperty()).toBool()) @@ -105,7 +105,7 @@ void ProductInstaller::install() } m_observer->initialize(Tr::tr("Installing"), artifactsToInstall.size()); - for (const Artifact * const a : qAsConst(artifactsToInstall)) { + for (const Artifact * const a : std::as_const(artifactsToInstall)) { copyFile(a); m_observer->incrementProgressValue(); } @@ -250,5 +250,5 @@ void ProductInstaller::handleError(const QString &message) m_logger.qbsWarning() << message; } -} // namespace Intern +} // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/buildgraph/productinstaller.h b/src/lib/corelib/buildgraph/productinstaller.h index 09828cfe9..c07e0b7cf 100644 --- a/src/lib/corelib/buildgraph/productinstaller.h +++ b/src/lib/corelib/buildgraph/productinstaller.h @@ -56,7 +56,7 @@ class ProductInstaller { public: ProductInstaller(TopLevelProjectPtr project, - std::vector<ResolvedProductPtr> products, + QVector<ResolvedProductPtr> products, InstallOptions options, ProgressObserver *observer, Logger logger); void install(); @@ -72,7 +72,7 @@ private: void handleError(const QString &message); const TopLevelProjectConstPtr m_project; - const std::vector<ResolvedProductPtr> m_products; + const QVector<ResolvedProductPtr> m_products; InstallOptions m_options; ProgressObserver * const m_observer; Logger m_logger; diff --git a/src/lib/corelib/buildgraph/projectbuilddata.cpp b/src/lib/corelib/buildgraph/projectbuilddata.cpp index 0c7f7bfdc..36ac75331 100644 --- a/src/lib/corelib/buildgraph/projectbuilddata.cpp +++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp @@ -157,7 +157,7 @@ void ProjectBuildData::insertFileDependency(FileDependency *dependency) static void disconnectArtifactChildren(Artifact *artifact) { qCDebug(lcBuildGraph) << "disconnect children of" << relativeArtifactFileName(artifact); - for (BuildGraphNode * const child : qAsConst(artifact->children)) + for (BuildGraphNode * const child : std::as_const(artifact->children)) child->parents.remove(artifact); artifact->children.clear(); artifact->childrenAddedByScanner.clear(); @@ -166,7 +166,7 @@ static void disconnectArtifactChildren(Artifact *artifact) static void disconnectArtifactParents(Artifact *artifact) { qCDebug(lcBuildGraph) << "disconnect parents of" << relativeArtifactFileName(artifact); - for (BuildGraphNode * const parent : qAsConst(artifact->parents)) { + for (BuildGraphNode * const parent : std::as_const(artifact->parents)) { parent->children.remove(artifact); if (parent->type() != BuildGraphNode::ArtifactNodeType) continue; @@ -240,7 +240,6 @@ void ProjectBuildData::removeArtifact(Artifact *artifact, artifact->transformer->outputs.remove(artifact); if (removeFromProduct) artifact->product->buildData->removeArtifact(artifact); - m_isDirty = false; } void ProjectBuildData::setDirty() @@ -258,7 +257,7 @@ void ProjectBuildData::setClean() void ProjectBuildData::load(PersistentPool &pool) { serializationOp<PersistentPool::Load>(pool); - for (FileDependency * const dep : qAsConst(fileDependencies)) + for (FileDependency * const dep : std::as_const(fileDependencies)) insertIntoLookupTable(dep); m_isDirty = false; } @@ -338,7 +337,7 @@ private: { if (!m_rulesOnPath.insert(rule.get()).second) { QString pathstr; - for (const Rule *r : qAsConst(m_rulePath)) { + for (const Rule *r : std::as_const(m_rulePath)) { pathstr += QLatin1Char('\n') + r->toString() + QLatin1Char('\t') + r->prepareScript.location().toString(); } @@ -395,7 +394,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc product->buildData = std::make_unique<ProductBuildData>(); ArtifactSetByFileTag artifactsPerFileTag; - for (const auto &dependency : qAsConst(product->dependencies)) { + for (const auto &dependency : std::as_const(product->dependencies)) { QBS_CHECK(dependency->enabled); resolveProductBuildData(dependency); } @@ -413,7 +412,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc artifactsPerFileTag["qbs"].insert(qbsFileArtifact); // read sources - for (const SourceArtifactConstPtr &sourceArtifact : product->allEnabledFiles()) { + for (const auto &sourceArtifact : product->allEnabledFiles()) { QString filePath = sourceArtifact->absoluteFilePath; if (lookupArtifact(product, filePath)) continue; // ignore duplicate artifacts @@ -445,7 +444,7 @@ void BuildDataResolver::connectRulesToDependencies(const ResolvedProductPtr &pro std::vector<RuleNode *> ruleNodes; for (RuleNode *ruleNode : filterByType<RuleNode>(product->buildData->allNodes())) ruleNodes.push_back(ruleNode); - for (const ResolvedProductConstPtr &dep : dependencies) { + for (const auto &dep : dependencies) { if (!dep->buildData) continue; for (RuleNode *depRuleNode : filterByType<RuleNode>(dep->buildData->allNodes())) { @@ -474,7 +473,7 @@ ScriptEngine *BuildDataResolver::engine() const return evalContext()->engine(); } -QScriptValue BuildDataResolver::scope() const +JSValue BuildDataResolver::scope() const { return evalContext()->scope(); } diff --git a/src/lib/corelib/buildgraph/projectbuilddata.h b/src/lib/corelib/buildgraph/projectbuilddata.h index 930344435..416651912 100644 --- a/src/lib/corelib/buildgraph/projectbuilddata.h +++ b/src/lib/corelib/buildgraph/projectbuilddata.h @@ -47,11 +47,11 @@ #include <tools/set.h> #include <tools/qttools.h> +#include <quickjs.h> + #include <QtCore/qlist.h> #include <QtCore/qstring.h> -#include <QtScript/qscriptvalue.h> - #include <unordered_map> namespace qbs { @@ -126,7 +126,7 @@ private: RulesEvaluationContextPtr evalContext() const; ScriptEngine *engine() const; - QScriptValue scope() const; + JSValue scope() const; TopLevelProjectPtr m_project; Logger m_logger; diff --git a/src/lib/corelib/buildgraph/qtmocscanner.cpp b/src/lib/corelib/buildgraph/qtmocscanner.cpp index 4e054a636..7df84e52c 100644 --- a/src/lib/corelib/buildgraph/qtmocscanner.cpp +++ b/src/lib/corelib/buildgraph/qtmocscanner.cpp @@ -54,9 +54,6 @@ #include <QtCore/qdebug.h> -#include <QtScript/qscriptcontext.h> -#include <QtScript/qscriptengine.h> - namespace qbs { namespace Internal { @@ -102,21 +99,23 @@ private: static QString qtMocScannerJsName() { return QStringLiteral("QtMocScanner"); } -QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue) - : m_tags(*commonFileTags()) +QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, ScriptEngine *engine, JSValue targetScriptValue) + : m_engine(engine) + , m_tags(*commonFileTags()) , m_product(product) - , m_targetScriptValue(targetScriptValue) + , m_targetScriptValue(JS_DupValue(engine->context(), targetScriptValue)) { - const auto engine = static_cast<ScriptEngine *>(targetScriptValue.engine()); - QScriptValue scannerObj = engine->newObject(); - targetScriptValue.setProperty(qtMocScannerJsName(), scannerObj); - QScriptValue applyFunction = engine->newFunction(&js_apply, this); - scannerObj.setProperty(QStringLiteral("apply"), applyFunction); + JSValue scannerObj = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass()); + attachPointerTo(scannerObj, this); + setJsProperty(engine->context(), targetScriptValue, qtMocScannerJsName(), scannerObj); + JSValue applyFunction = JS_NewCFunction(engine->context(), &js_apply, "QtMocScanner", 1); + setJsProperty(engine->context(), scannerObj, QStringLiteral("apply"), applyFunction); } QtMocScanner::~QtMocScanner() { - m_targetScriptValue.setProperty(qtMocScannerJsName(), QScriptValue()); + setJsProperty(m_engine->context(), m_targetScriptValue, qtMocScannerJsName(), JS_UNDEFINED); + JS_FreeValue(m_engine->context(), m_targetScriptValue); } static RawScanResult runScanner(ScannerPlugin *scanner, const Artifact *artifact) @@ -153,7 +152,7 @@ static RawScanResult runScanner(ScannerPlugin *scanner, const Artifact *artifact } QString baseDirOfInFilePath = artifact->dirPath(); - forever { + for (;;) { int flags = 0; const char *szOutFilePath = scanner->next(opaq, &length, &flags); if (szOutFilePath == nullptr) @@ -199,22 +198,24 @@ void QtMocScanner::findIncludedMocCppFiles() } } -QScriptValue QtMocScanner::js_apply(QScriptContext *ctx, QScriptEngine *engine, - QtMocScanner *that) +JSValue QtMocScanner::js_apply(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - QScriptValue input = ctx->argument(0); - return that->apply(engine, attachedPointer<Artifact>(input)); + if (argc < 1) + return throwError(ctx, Tr::tr("QtMocScanner.apply() requires an argument.")); + ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const auto scanner = attachedPointer<QtMocScanner>(this_val, engine->dataWithPtrClass()); + return scanner->apply(engine, attachedPointer<Artifact>(argv[0], engine->dataWithPtrClass())); } -static QScriptValue scannerCountError(QScriptEngine *engine, size_t scannerCount, - const QString &fileTag) +static JSValue scannerCountError(ScriptEngine *engine, size_t scannerCount, + const QString &fileTag) { - return engine->currentContext()->throwError( - Tr::tr("There are %1 scanners for the file tag %2. " - "Expected is exactly one.").arg(scannerCount).arg(fileTag)); + return throwError(engine->context(), + Tr::tr("There are %1 scanners for the file tag %2. " + "Expected is exactly one.").arg(scannerCount).arg(fileTag)); } -QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact) +JSValue QtMocScanner::apply(ScriptEngine *engine, const Artifact *artifact) { if (!m_cppScanner) { auto scanners = ScannerPluginManager::scannersForFileTag(m_tags.cpp); @@ -266,11 +267,13 @@ QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact << "mustCompile:" << mustCompile << "hasPluginMetaDataMacro:" << hasPluginMetaDataMacro; - QScriptValue obj = engine->newObject(); - obj.setProperty(QStringLiteral("hasQObjectMacro"), hasQObjectMacro); - obj.setProperty(QStringLiteral("mustCompile"), mustCompile); - obj.setProperty(QStringLiteral("hasPluginMetaDataMacro"), hasPluginMetaDataMacro); - static_cast<ScriptEngine *>(engine)->setUsesIo(); + JSValue obj = engine->newObject(); + JSContext * const ctx = m_engine->context(); + setJsProperty(ctx, obj, QStringLiteral("hasQObjectMacro"), JS_NewBool(ctx, hasQObjectMacro)); + setJsProperty(ctx, obj, QStringLiteral("mustCompile"), JS_NewBool(ctx, mustCompile)); + setJsProperty(ctx, obj, QStringLiteral("hasPluginMetaDataMacro"), + JS_NewBool(ctx, hasPluginMetaDataMacro)); + engine->setUsesIo(); return obj; } diff --git a/src/lib/corelib/buildgraph/qtmocscanner.h b/src/lib/corelib/buildgraph/qtmocscanner.h index 6455383f3..2e2a00007 100644 --- a/src/lib/corelib/buildgraph/qtmocscanner.h +++ b/src/lib/corelib/buildgraph/qtmocscanner.h @@ -41,12 +41,11 @@ #define QBS_QTMOCSCANNER_H #include <language/language.h> +#include <quickjs.h> #include <QtCore/qhash.h> #include <QtCore/qstring.h> -#include <QtScript/qscriptvalue.h> - QT_BEGIN_NAMESPACE class QScriptContext; QT_END_NAMESPACE @@ -55,6 +54,7 @@ class ScannerPlugin; namespace qbs { namespace Internal { +class ScriptEngine; class Artifact; struct CommonFileTags; @@ -62,17 +62,19 @@ struct CommonFileTags; class QtMocScanner { public: - explicit QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue); + explicit QtMocScanner(const ResolvedProductPtr &product, ScriptEngine *engine, + JSValue targetScriptValue); ~QtMocScanner(); private: void findIncludedMocCppFiles(); - static QScriptValue js_apply(QScriptContext *ctx, QScriptEngine *engine, QtMocScanner *that); - QScriptValue apply(QScriptEngine *engine, const Artifact *artifact); + static JSValue js_apply(JSContext *ctx, JSValue this_val, int argc, JSValue *argv); + JSValue apply(ScriptEngine *engine, const Artifact *artifact); + ScriptEngine * const m_engine; const CommonFileTags &m_tags; const ResolvedProductPtr &m_product; - QScriptValue m_targetScriptValue; + JSValue m_targetScriptValue; QHash<QString, QString> m_includedMocCppFiles; ScannerPlugin *m_cppScanner = nullptr; }; diff --git a/src/lib/corelib/buildgraph/requesteddependencies.cpp b/src/lib/corelib/buildgraph/requesteddependencies.cpp index f993b2518..b95c8db94 100644 --- a/src/lib/corelib/buildgraph/requesteddependencies.cpp +++ b/src/lib/corelib/buildgraph/requesteddependencies.cpp @@ -48,9 +48,9 @@ namespace Internal { static Set<QString> depNamesForProduct(const ResolvedProduct *p) { Set<QString> names; - for (const ResolvedProductConstPtr &dep : p->dependencies) + for (const auto &dep : p->dependencies) names.insert(dep->uniqueName()); - for (const ResolvedModuleConstPtr &m : p->modules) { + for (const auto &m : p->modules) { if (!m->isProduct) names.insert(m->name); } @@ -73,7 +73,7 @@ bool RequestedDependencies::isUpToDate(const TopLevelProject *project) const { if (m_depsPerProduct.empty()) return true; - for (const ResolvedProductConstPtr &product : project->allProducts()) { + for (const auto &product : project->allProducts()) { const auto it = m_depsPerProduct.find(product->uniqueName()); if (it == m_depsPerProduct.cend()) continue; diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.h b/src/lib/corelib/buildgraph/rescuableartifactdata.h index 3e4d6e25f..6dd10f76c 100644 --- a/src/lib/corelib/buildgraph/rescuableartifactdata.h +++ b/src/lib/corelib/buildgraph/rescuableartifactdata.h @@ -84,9 +84,12 @@ public: struct ChildData { - ChildData(const QString &n = QString(), const QString &m = QString(), - const QString &c = QString(), bool byScanner = false) - : productName(n), productMultiplexId(m), childFilePath(c), addedByScanner(byScanner) + ChildData(QString n = QString(), QString m = QString(), + QString c = QString(), bool byScanner = false) + : productName(std::move(n)) + , productMultiplexId(std::move(m)) + , childFilePath(std::move(c)) + , addedByScanner(byScanner) {} template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool) diff --git a/src/lib/corelib/buildgraph/rulecommands.cpp b/src/lib/corelib/buildgraph/rulecommands.cpp index c09c31c2c..73d05eaca 100644 --- a/src/lib/corelib/buildgraph/rulecommands.cpp +++ b/src/lib/corelib/buildgraph/rulecommands.cpp @@ -39,6 +39,8 @@ ****************************************************************************/ #include "rulecommands.h" + +#include <language/scriptengine.h> #include <logging/translator.h> #include <tools/error.h> #include <tools/fileinfo.h> @@ -48,9 +50,6 @@ #include <QtCore/qfile.h> -#include <QtScript/qscriptengine.h> -#include <QtScript/qscriptvalueiterator.h> - namespace qbs { namespace Internal { @@ -70,6 +69,7 @@ static QString responseFileUsagePrefixProperty() { return QStringLiteral("responseFileUsagePrefix"); } +static QString responseFileSeparatorProperty() { return QStringLiteral("responseFileSeparator"); } static QString silentProperty() { return QStringLiteral("silent"); } static QString stderrFilePathProperty() { return QStringLiteral("stderrFilePath"); } static QString stderrFilterFunctionProperty() { return QStringLiteral("stderrFilterFunction"); } @@ -78,10 +78,11 @@ static QString stdoutFilterFunctionProperty() { return QStringLiteral("stdoutFil static QString timeoutProperty() { return QStringLiteral("timeout"); } static QString workingDirProperty() { return QStringLiteral("workingDirectory"); } -static QString invokedSourceCode(const QScriptValue codeOrFunction) +static QString invokedSourceCode(JSContext *ctx, const JSValue &codeOrFunction) { - const QString &code = codeOrFunction.toString(); - return codeOrFunction.isFunction() ? QStringLiteral("(") + code + QStringLiteral(")()") : code; + const QString &code = getJsString(ctx, codeOrFunction); + return JS_IsFunction(ctx, codeOrFunction) + ? QStringLiteral("(") + code + QStringLiteral(")()") : code; } AbstractCommand::AbstractCommand() @@ -98,28 +99,25 @@ AbstractCommand::~AbstractCommand() = default; bool AbstractCommand::equals(const AbstractCommand *other) const { - return type() == other->type() - && m_description == other->m_description - && m_extendedDescription == other->m_extendedDescription - && m_highlight == other->m_highlight - && m_ignoreDryRun == other->m_ignoreDryRun - && m_silent == other->m_silent - && m_jobPool == other->m_jobPool - && m_timeout == other->m_timeout - && m_properties == other->m_properties; -} - -void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation) -{ - m_description = scriptValue->property(StringConstants::descriptionProperty()).toString(); - m_extendedDescription = scriptValue->property(extendedDescriptionProperty()).toString(); - m_highlight = scriptValue->property(highlightProperty()).toString(); - m_ignoreDryRun = scriptValue->property(ignoreDryRunProperty()).toBool(); - m_silent = scriptValue->property(silentProperty()).toBool(); - m_jobPool = scriptValue->property(StringConstants::jobPoolProperty()).toString(); - const auto timeoutScriptValue = scriptValue->property(timeoutProperty()); - if (!timeoutScriptValue.isUndefined() && !timeoutScriptValue.isNull()) - m_timeout = timeoutScriptValue.toInt32(); + return type() == other->type() && m_description == other->m_description + && m_extendedDescription == other->m_extendedDescription + && m_highlight == other->m_highlight && m_ignoreDryRun == other->m_ignoreDryRun + && m_silent == other->m_silent && m_jobPool == other->m_jobPool + && m_timeout == other->m_timeout && qVariantMapsEqual(m_properties, other->m_properties); +} + +void AbstractCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, + const CodeLocation &codeLocation) +{ + m_description = getJsStringProperty(ctx, *scriptValue, StringConstants::descriptionProperty()); + m_extendedDescription = getJsStringProperty(ctx, *scriptValue, extendedDescriptionProperty()); + m_highlight = getJsStringProperty(ctx, *scriptValue, highlightProperty()); + m_ignoreDryRun = getJsBoolProperty(ctx, *scriptValue, ignoreDryRunProperty()); + m_silent = getJsBoolProperty(ctx, *scriptValue, silentProperty()); + m_jobPool = getJsStringProperty(ctx, *scriptValue, StringConstants::jobPoolProperty()); + const auto timeoutScriptValue = getJsProperty(ctx, *scriptValue, timeoutProperty()); + if (!JS_IsUndefined(timeoutScriptValue) && !JS_IsNull(timeoutScriptValue)) + m_timeout = JS_VALUE_GET_INT(timeoutScriptValue); m_codeLocation = codeLocation; m_predefinedProperties @@ -147,98 +145,102 @@ void AbstractCommand::store(PersistentPool &pool) serializationOp<PersistentPool::Store>(pool); } -void AbstractCommand::applyCommandProperties(const QScriptValue *scriptValue) +void AbstractCommand::applyCommandProperties(JSContext *ctx, const JSValue *scriptValue) { - QScriptValueIterator it(*scriptValue); - while (it.hasNext()) { - it.next(); - if (m_predefinedProperties.contains(it.name())) - continue; - const QVariant value = it.value().toVariant(); - if (QMetaType::Type(value.type()) == QMetaType::QObjectStar - || it.value().scriptClass() - || it.value().data().isValid()) { + handleJsProperties(ctx, *scriptValue, [this, ctx](const JSAtom &prop, const JSPropertyDescriptor &desc) { + const QString name = getJsString(ctx, prop); + if (m_predefinedProperties.contains(name)) + return; + // TODO: Use script class for command objects, don't allow setting random properties + if (!isSimpleValue(desc.value)) { throw ErrorInfo(Tr::tr("Property '%1' has a type unsuitable for storing in a command " - "object.").arg(it.name()), m_codeLocation); + "object.").arg(name), m_codeLocation); } - m_properties.insert(it.name(), value); - } -} - -static QScriptValue js_CommandBase(QScriptContext *context, QScriptEngine *engine) -{ - QScriptValue cmd = context->thisObject(); - QBS_ASSERT(context->isCalledAsConstructor(), cmd = engine->newObject()); - cmd.setProperty(StringConstants::descriptionProperty(), - engine->toScriptValue(AbstractCommand::defaultDescription())); - cmd.setProperty(extendedDescriptionProperty(), - engine->toScriptValue(AbstractCommand::defaultExtendedDescription())); - cmd.setProperty(highlightProperty(), - engine->toScriptValue(AbstractCommand::defaultHighLight())); - cmd.setProperty(ignoreDryRunProperty(), - engine->toScriptValue(AbstractCommand::defaultIgnoreDryRun())); - cmd.setProperty(silentProperty(), - engine->toScriptValue(AbstractCommand::defaultIsSilent())); - cmd.setProperty(timeoutProperty(), - engine->toScriptValue(AbstractCommand::defaultTimeout())); + m_properties.insert(name, getJsVariant(ctx, desc.value)); + }); +} + +static JSValue js_CommandBase(JSContext *ctx) +{ + const JSValue cmd = JS_NewObject(ctx); + setJsProperty(ctx, cmd, StringConstants::descriptionProperty(), + makeJsString(ctx, AbstractCommand::defaultDescription())); + setJsProperty(ctx, cmd, extendedDescriptionProperty(), + makeJsString(ctx, AbstractCommand::defaultExtendedDescription())); + setJsProperty(ctx, cmd, highlightProperty(), + makeJsString(ctx, AbstractCommand::defaultHighLight())); + setJsProperty(ctx, cmd, ignoreDryRunProperty(), + JS_NewBool(ctx, AbstractCommand::defaultIgnoreDryRun())); + setJsProperty(ctx, cmd, silentProperty(), + JS_NewBool(ctx, AbstractCommand::defaultIsSilent())); + setJsProperty(ctx, cmd, timeoutProperty(), + JS_NewInt32(ctx, AbstractCommand::defaultTimeout())); return cmd; } -static QScriptValue js_Command(QScriptContext *context, QScriptEngine *engine) +static JSValue js_Command(JSContext *ctx, JSValueConst, JSValueConst, + int argc, JSValueConst *argv, int) { - if (Q_UNLIKELY(!context->isCalledAsConstructor())) - return context->throwError(Tr::tr("Command constructor called without new.")); - static ProcessCommandPtr commandPrototype = ProcessCommand::create(); - - QScriptValue program = context->argument(0); - if (program.isUndefined()) - program = engine->toScriptValue(commandPrototype->program()); - QScriptValue arguments = context->argument(1); - if (arguments.isUndefined()) - arguments = engine->toScriptValue(commandPrototype->arguments()); - QScriptValue cmd = js_CommandBase(context, engine); - cmd.setProperty(StringConstants::classNameProperty(), - engine->toScriptValue(StringConstants::commandType())); - cmd.setProperty(programProperty(), program); - cmd.setProperty(argumentsProperty(), arguments); - cmd.setProperty(workingDirProperty(), - engine->toScriptValue(commandPrototype->workingDir())); - cmd.setProperty(maxExitCodeProperty(), - engine->toScriptValue(commandPrototype->maxExitCode())); - cmd.setProperty(stdoutFilterFunctionProperty(), - engine->toScriptValue(commandPrototype->stdoutFilterFunction())); - cmd.setProperty(stderrFilterFunctionProperty(), - engine->toScriptValue(commandPrototype->stderrFilterFunction())); - cmd.setProperty(responseFileThresholdProperty(), - engine->toScriptValue(commandPrototype->responseFileThreshold())); - cmd.setProperty(responseFileArgumentIndexProperty(), - engine->toScriptValue(commandPrototype->responseFileArgumentIndex())); - cmd.setProperty(responseFileUsagePrefixProperty(), - engine->toScriptValue(commandPrototype->responseFileUsagePrefix())); - cmd.setProperty(stdoutFilePathProperty(), - engine->toScriptValue(commandPrototype->stdoutFilePath())); - cmd.setProperty(stderrFilePathProperty(), - engine->toScriptValue(commandPrototype->stderrFilePath())); - cmd.setProperty(environmentProperty(), - engine->toScriptValue(commandPrototype->environment().toStringList())); - cmd.setProperty(ignoreDryRunProperty(), - engine->toScriptValue(commandPrototype->ignoreDryRun())); + JSValue program = JS_UNDEFINED; + if (argc > 0) + program = JS_DupValue(ctx, argv[0]); + if (JS_IsUndefined(program)) + program = makeJsString(ctx, commandPrototype->program()); + JSValue arguments = JS_UNDEFINED; + if (argc > 1) + arguments = JS_DupValue(ctx, argv[1]); + if (JS_IsUndefined(arguments)) + arguments = makeJsStringList(ctx, commandPrototype->arguments()); + if (JS_IsString(arguments)) { + JSValue args = JS_NewArray(ctx); + JS_SetPropertyUint32(ctx, args, 0, arguments); + arguments = args; + } + JSValue cmd = js_CommandBase(ctx); + setJsProperty(ctx, cmd, StringConstants::classNameProperty(), + makeJsString(ctx, StringConstants::commandType())); + setJsProperty(ctx, cmd, programProperty(), program); + setJsProperty(ctx, cmd, argumentsProperty(), arguments); + setJsProperty(ctx, cmd, workingDirProperty(), + makeJsString(ctx, commandPrototype->workingDir())); + setJsProperty(ctx, cmd, maxExitCodeProperty(), + JS_NewInt32(ctx, commandPrototype->maxExitCode())); + setJsProperty(ctx, cmd, stdoutFilterFunctionProperty(), + makeJsString(ctx, commandPrototype->stdoutFilterFunction())); + setJsProperty(ctx, cmd, stderrFilterFunctionProperty(), + makeJsString(ctx, commandPrototype->stderrFilterFunction())); + setJsProperty(ctx, cmd, responseFileThresholdProperty(), + JS_NewInt32(ctx, commandPrototype->responseFileThreshold())); + setJsProperty(ctx, cmd, responseFileArgumentIndexProperty(), + JS_NewInt32(ctx, commandPrototype->responseFileArgumentIndex())); + setJsProperty(ctx, cmd, responseFileUsagePrefixProperty(), + makeJsString(ctx, commandPrototype->responseFileUsagePrefix())); + setJsProperty(ctx, cmd, responseFileSeparatorProperty(), + makeJsString(ctx, commandPrototype->responseFileSeparator())); + setJsProperty(ctx, cmd, stdoutFilePathProperty(), + makeJsString(ctx, commandPrototype->stdoutFilePath())); + setJsProperty(ctx, cmd, stderrFilePathProperty(), + makeJsString(ctx, commandPrototype->stderrFilePath())); + setJsProperty(ctx, cmd, environmentProperty(), + makeJsStringList(ctx, commandPrototype->environment().toStringList())); + setJsProperty(ctx, cmd, ignoreDryRunProperty(), + JS_NewBool(ctx, commandPrototype->ignoreDryRun())); return cmd; } -void ProcessCommand::setupForJavaScript(QScriptValue targetObject) +void ProcessCommand::setupForJavaScript(ScriptEngine *engine, JSValue targetObject) { - QBS_CHECK(targetObject.isObject()); - QScriptValue ctor = targetObject.engine()->newFunction(js_Command, 2); - targetObject.setProperty(StringConstants::commandType(), ctor); + engine->registerClass(StringConstants::commandType().toUtf8().constData(), js_Command, nullptr, + targetObject); } ProcessCommand::ProcessCommand() : m_maxExitCode(0) , m_responseFileThreshold(defaultResponseFileThreshold()) , m_responseFileArgumentIndex(0) + , m_responseFileSeparator(QStringLiteral("\n")) { } @@ -277,6 +279,7 @@ bool ProcessCommand::equals(const AbstractCommand *otherAbstractCommand) const && m_responseFileThreshold == other->m_responseFileThreshold && m_responseFileArgumentIndex == other->m_responseFileArgumentIndex && m_responseFileUsagePrefix == other->m_responseFileUsagePrefix + && m_responseFileSeparator == other->m_responseFileSeparator && m_stdoutFilePath == other->m_stdoutFilePath && m_stderrFilePath == other->m_stderrFilePath && m_relevantEnvVars == other->m_relevantEnvVars @@ -284,38 +287,37 @@ bool ProcessCommand::equals(const AbstractCommand *otherAbstractCommand) const && m_environment == other->m_environment; } -void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation) -{ - AbstractCommand::fillFromScriptValue(scriptValue, codeLocation); - m_program = scriptValue->property(programProperty()).toString(); - m_arguments = scriptValue->property(argumentsProperty()).toVariant().toStringList(); - m_workingDir = scriptValue->property(workingDirProperty()).toString(); - m_maxExitCode = scriptValue->property(maxExitCodeProperty()).toInt32(); - - // toString() is required, presumably due to QtScript bug that manifests itself on Windows - const QScriptValue stdoutFilterFunction - = scriptValue->property(stdoutFilterFunctionProperty()).toString(); - - m_stdoutFilterFunction = invokedSourceCode(stdoutFilterFunction); - - // toString() is required, presumably due to QtScript bug that manifests itself on Windows - const QScriptValue stderrFilterFunction - = scriptValue->property(stderrFilterFunctionProperty()).toString(); - - m_stderrFilterFunction = invokedSourceCode(stderrFilterFunction); - m_relevantEnvVars = scriptValue->property(QStringLiteral("relevantEnvironmentVariables")) - .toVariant().toStringList(); - m_responseFileThreshold = scriptValue->property(responseFileThresholdProperty()) - .toInt32(); - m_responseFileArgumentIndex = scriptValue->property(responseFileArgumentIndexProperty()) - .toInt32(); - m_responseFileUsagePrefix = scriptValue->property(responseFileUsagePrefixProperty()) - .toString(); - QStringList envList = scriptValue->property(environmentProperty()).toVariant() - .toStringList(); +void ProcessCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, const CodeLocation &codeLocation) +{ + AbstractCommand::fillFromScriptValue(ctx, scriptValue, codeLocation); + m_program = getJsStringProperty(ctx, *scriptValue, programProperty()); + m_arguments = getJsStringListProperty(ctx, *scriptValue, argumentsProperty()); + m_workingDir = getJsStringProperty(ctx, *scriptValue, workingDirProperty()); + m_maxExitCode = getJsIntProperty(ctx, *scriptValue, maxExitCodeProperty()); + + const ScopedJsValue stdoutFilterFunction(ctx, getJsProperty(ctx, *scriptValue, + stdoutFilterFunctionProperty())); + if (JS_IsFunction(ctx, stdoutFilterFunction)) + m_stdoutFilterFunction = QLatin1Char('(') + getJsString(ctx, stdoutFilterFunction) + QLatin1Char(')'); + + const ScopedJsValue stderrFilterFunction(ctx, getJsProperty(ctx, *scriptValue, + stderrFilterFunctionProperty())); + if (JS_IsFunction(ctx, stderrFilterFunction)) + m_stderrFilterFunction = QLatin1Char('(') + getJsString(ctx, stderrFilterFunction) + QLatin1Char(')'); + + m_relevantEnvVars = getJsStringListProperty(ctx, *scriptValue, + QStringLiteral("relevantEnvironmentVariables")); + m_responseFileThreshold = getJsIntProperty(ctx, *scriptValue, responseFileThresholdProperty()); + m_responseFileArgumentIndex = getJsIntProperty(ctx, *scriptValue, + responseFileArgumentIndexProperty()); + m_responseFileUsagePrefix = getJsStringProperty(ctx, *scriptValue, + responseFileUsagePrefixProperty()); + m_responseFileSeparator = getJsStringProperty(ctx, *scriptValue, + responseFileSeparatorProperty()); + QStringList envList = getJsStringListProperty(ctx, *scriptValue, environmentProperty()); getEnvironmentFromList(envList); - m_stdoutFilePath = scriptValue->property(stdoutFilePathProperty()).toString(); - m_stderrFilePath = scriptValue->property(stderrFilePathProperty()).toString(); + m_stdoutFilePath = getJsStringProperty(ctx, *scriptValue, stdoutFilePathProperty()); + m_stderrFilePath = getJsStringProperty(ctx, *scriptValue, stderrFilePathProperty()); m_predefinedProperties << programProperty() @@ -330,7 +332,7 @@ void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue, const << environmentProperty() << stdoutFilePathProperty() << stderrFilePathProperty(); - applyCommandProperties(scriptValue); + applyCommandProperties(ctx, scriptValue); } QStringList ProcessCommand::relevantEnvVars() const @@ -358,43 +360,42 @@ void ProcessCommand::store(PersistentPool &pool) serializationOp<PersistentPool::Store>(pool); } -static QString currentImportScopeName(QScriptContext *context) +static QString currentImportScopeName(JSContext *ctx) { - for (; context; context = context->parentContext()) { - QScriptValue v = context->thisObject() - .property(StringConstants::importScopeNamePropertyInternal()); - if (v.isString()) - return v.toString(); + const ScriptEngine * const engine = ScriptEngine::engineForContext(ctx); + const JSValueList &contextStack = engine->contextStack(); + for (auto it = contextStack.rbegin(); it != contextStack.rend(); ++it) { + if (!JS_IsObject(*it)) + continue; + const ScopedJsValue v(ctx, getJsProperty(ctx, *it, + StringConstants::importScopeNamePropertyInternal())); + if (JS_IsString(v)) + return getJsString(ctx, v); } return {}; } -static QScriptValue js_JavaScriptCommand(QScriptContext *context, QScriptEngine *engine) +static JSValue js_JavaScriptCommand(JSContext *ctx, JSValueConst, JSValueConst, + int argc, JSValueConst *, int) { - if (Q_UNLIKELY(!context->isCalledAsConstructor())) - return context->throwError(Tr::tr("JavaScriptCommand constructor called without new.")); - if (Q_UNLIKELY(context->argumentCount() != 0)) { - return context->throwError(QScriptContext::SyntaxError, - Tr::tr("JavaScriptCommand c'tor doesn't take arguments.")); - } + if (argc > 0) + return throwError(ctx, Tr::tr("JavaScriptCommand c'tor doesn't take arguments.")); static JavaScriptCommandPtr commandPrototype = JavaScriptCommand::create(); - QScriptValue cmd = js_CommandBase(context, engine); - cmd.setProperty(StringConstants::classNameProperty(), - engine->toScriptValue(StringConstants::javaScriptCommandType())); - cmd.setProperty(StringConstants::sourceCodeProperty(), - engine->toScriptValue(commandPrototype->sourceCode())); - cmd.setProperty(StringConstants::importScopeNamePropertyInternal(), - engine->toScriptValue(currentImportScopeName(context))); - + JSValue cmd = js_CommandBase(ctx); + setJsProperty(ctx, cmd, StringConstants::classNameProperty(), + makeJsString(ctx, StringConstants::javaScriptCommandType())); + setJsProperty(ctx, cmd, StringConstants::sourceCodeProperty(), + makeJsString(ctx, commandPrototype->sourceCode())); + setJsProperty(ctx, cmd, StringConstants::importScopeNamePropertyInternal(), + makeJsString(ctx, currentImportScopeName(ctx))); return cmd; } -void JavaScriptCommand::setupForJavaScript(QScriptValue targetObject) +void JavaScriptCommand::setupForJavaScript(ScriptEngine *engine, JSValue targetObject) { - QBS_CHECK(targetObject.isObject()); - QScriptValue ctor = targetObject.engine()->newFunction(js_JavaScriptCommand, 0); - targetObject.setProperty(StringConstants::javaScriptCommandType(), ctor); + engine->registerClass(StringConstants::javaScriptCommandType().toUtf8().constData(), + js_JavaScriptCommand, nullptr, targetObject); } JavaScriptCommand::JavaScriptCommand() = default; @@ -407,22 +408,24 @@ bool JavaScriptCommand::equals(const AbstractCommand *otherAbstractCommand) cons return m_sourceCode == other->m_sourceCode; } -void JavaScriptCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation) +void JavaScriptCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, + const CodeLocation &codeLocation) { - AbstractCommand::fillFromScriptValue(scriptValue, codeLocation); + AbstractCommand::fillFromScriptValue(ctx, scriptValue, codeLocation); - const QScriptValue importScope = scriptValue->property( - StringConstants::importScopeNamePropertyInternal()); - if (importScope.isString()) - m_scopeName = importScope.toString(); + const ScopedJsValue importScope(ctx, getJsProperty(ctx, *scriptValue, + StringConstants::importScopeNamePropertyInternal())); + if (JS_IsString(importScope)) + m_scopeName = getJsString(ctx, importScope); - const QScriptValue sourceCode = scriptValue->property(StringConstants::sourceCodeProperty()); - m_sourceCode = invokedSourceCode(sourceCode); + const ScopedJsValue sourceCode(ctx, getJsProperty(ctx, *scriptValue, + StringConstants::sourceCodeProperty())); + m_sourceCode = invokedSourceCode(ctx, sourceCode); m_predefinedProperties << StringConstants::classNameProperty() << StringConstants::sourceCodeProperty() << StringConstants::importScopeNamePropertyInternal(); - applyCommandProperties(scriptValue); + applyCommandProperties(ctx, scriptValue); } void JavaScriptCommand::load(PersistentPool &pool) @@ -461,7 +464,7 @@ void CommandList::load(PersistentPool &pool) void CommandList::store(PersistentPool &pool) const { - pool.store(m_commands.size()); + pool.store(int(m_commands.size())); for (const AbstractCommandPtr &cmd : m_commands) { pool.store(static_cast<quint8>(cmd->type())); pool.store(cmd); diff --git a/src/lib/corelib/buildgraph/rulecommands.h b/src/lib/corelib/buildgraph/rulecommands.h index d4d70d591..7b08d1015 100644 --- a/src/lib/corelib/buildgraph/rulecommands.h +++ b/src/lib/corelib/buildgraph/rulecommands.h @@ -47,14 +47,15 @@ #include <tools/persistence.h> #include <tools/set.h> +#include <quickjs.h> + #include <QtCore/qprocess.h> #include <QtCore/qstringlist.h> #include <QtCore/qvariant.h> -#include <QtScript/qscriptvalue.h> - namespace qbs { namespace Internal { +class ScriptEngine; class AbstractCommand { @@ -75,7 +76,8 @@ public: virtual CommandType type() const = 0; virtual bool equals(const AbstractCommand *other) const; - virtual void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation); + virtual void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, + const CodeLocation &codeLocation); QString fullDescription(const QString &productName) const; const QString description() const { return m_description; } @@ -94,7 +96,7 @@ public: protected: AbstractCommand(); - void applyCommandProperties(const QScriptValue *scriptValue); + void applyCommandProperties(JSContext *ctx, const JSValue *scriptValue); Set<QString> m_predefinedProperties; @@ -121,11 +123,11 @@ class ProcessCommand : public AbstractCommand { public: static ProcessCommandPtr create() { return ProcessCommandPtr(new ProcessCommand); } - static void setupForJavaScript(QScriptValue targetObject); + static void setupForJavaScript(ScriptEngine *engine, JSValue targetObject); CommandType type() const override { return ProcessCommandType; } bool equals(const AbstractCommand *otherAbstractCommand) const override; - void fillFromScriptValue(const QScriptValue *scriptValue, + void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, const CodeLocation &codeLocation) override; const QString program() const { return m_program; } const QStringList arguments() const { return m_arguments; } @@ -136,6 +138,7 @@ public: int responseFileThreshold() const { return m_responseFileThreshold; } int responseFileArgumentIndex() const { return m_responseFileArgumentIndex; } QString responseFileUsagePrefix() const { return m_responseFileUsagePrefix; } + QString responseFileSeparator() const { return m_responseFileSeparator; } QProcessEnvironment environment() const { return m_environment; } QStringList relevantEnvVars() const; void clearRelevantEnvValues() { m_relevantEnvValues.clear(); } @@ -158,10 +161,10 @@ private: { pool.serializationOp<opType>(m_program, m_arguments, m_environment, m_workingDir, m_stdoutFilterFunction, m_stderrFilterFunction, - m_responseFileUsagePrefix, m_maxExitCode, - m_responseFileThreshold, m_responseFileArgumentIndex, - m_relevantEnvVars, m_relevantEnvValues, m_stdoutFilePath, - m_stderrFilePath); + m_responseFileUsagePrefix, m_responseFileSeparator, + m_maxExitCode, m_responseFileThreshold, + m_responseFileArgumentIndex, m_relevantEnvVars, + m_relevantEnvValues, m_stdoutFilePath, m_stderrFilePath); } QString m_program; @@ -173,6 +176,7 @@ private: int m_responseFileThreshold; // When to use response files? In bytes of (program name + arguments). int m_responseFileArgumentIndex; QString m_responseFileUsagePrefix; + QString m_responseFileSeparator; QProcessEnvironment m_environment; QStringList m_relevantEnvVars; QProcessEnvironment m_relevantEnvValues; @@ -184,16 +188,15 @@ class JavaScriptCommand : public AbstractCommand { public: static JavaScriptCommandPtr create() { return JavaScriptCommandPtr(new JavaScriptCommand); } - static void setupForJavaScript(QScriptValue targetObject); + static void setupForJavaScript(ScriptEngine *engine, JSValue targetObject); CommandType type() const override { return JavaScriptCommandType; } bool equals(const AbstractCommand *otherAbstractCommand) const override; - void fillFromScriptValue(const QScriptValue *scriptValue, + void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, const CodeLocation &codeLocation) override; const QString &scopeName() const { return m_scopeName; } const QString &sourceCode() const { return m_sourceCode; } - void setSourceCode(const QString &str) { m_sourceCode = str; } void load(PersistentPool &pool) override; void store(PersistentPool &pool) override; diff --git a/src/lib/corelib/buildgraph/rulegraph.cpp b/src/lib/corelib/buildgraph/rulegraph.cpp index e01a8bda8..2acc2a97e 100644 --- a/src/lib/corelib/buildgraph/rulegraph.cpp +++ b/src/lib/corelib/buildgraph/rulegraph.cpp @@ -61,11 +61,11 @@ void RuleGraph::build(const std::vector<RulePtr> &rules, const FileTags &product m_parents.resize(rules.size()); m_children.resize(rules.size()); - for (const RuleConstPtr &rule : qAsConst(m_rules)) { + for (const auto &rule : std::as_const(m_rules)) { FileTags inFileTags = rule->inputs; inFileTags += rule->auxiliaryInputs; inFileTags += rule->explicitlyDependsOn; - for (const FileTag &fileTag : qAsConst(inFileTags)) { + for (const FileTag &fileTag : std::as_const(inFileTags)) { inputFileTagToRule[fileTag].push_back(rule.get()); for (const Rule * const producingRule : m_outputFileTagToRule.value(fileTag)) { if (!producingRule->collectedOutputFileTags().intersects( @@ -82,38 +82,38 @@ void RuleGraph::build(const std::vector<RulePtr> &rules, const FileTags &product productRules << rules; //### check: the rule graph must be a in valid shape! } - for (const Rule *r : qAsConst(productRules)) + for (const Rule *r : std::as_const(productRules)) m_rootRules += r->ruleGraphId; } void RuleGraph::accept(RuleGraphVisitor *visitor) const { const RuleConstPtr nullParent; - for (int rootIndex : qAsConst(m_rootRules)) + for (int rootIndex : std::as_const(m_rootRules)) traverse(visitor, nullParent, m_rules.at(rootIndex)); } void RuleGraph::dump() const { QByteArray indent; - printf("---rule graph dump:\n"); + std::printf("---rule graph dump:\n"); Set<int> rootRules; - for (const RuleConstPtr &rule : qAsConst(m_rules)) + for (const auto &rule : std::as_const(m_rules)) if (m_parents[rule->ruleGraphId].empty()) rootRules += rule->ruleGraphId; - for (int idx : qAsConst(rootRules)) + for (int idx : std::as_const(rootRules)) dump_impl(indent, idx); } void RuleGraph::dump_impl(QByteArray &indent, int rootIndex) const { const RuleConstPtr r = m_rules[rootIndex]; - printf("%s", indent.constData()); - printf("%s", qPrintable(r->toString())); - printf("\n"); + std::printf("%s", indent.constData()); + std::printf("%s", qPrintable(r->toString())); + std::printf("\n"); indent.append(" "); - for (int childIndex : qAsConst(m_children[rootIndex])) + for (int childIndex : std::as_const(m_children[rootIndex])) dump_impl(indent, childIndex); indent.chop(2); } diff --git a/src/lib/corelib/buildgraph/rulenode.cpp b/src/lib/corelib/buildgraph/rulenode.cpp index 48d17934f..8568e4098 100644 --- a/src/lib/corelib/buildgraph/rulenode.cpp +++ b/src/lib/corelib/buildgraph/rulenode.cpp @@ -229,7 +229,7 @@ int RuleNode::transformerCount() const ArtifactSet RuleNode::currentInputArtifacts() const { ArtifactSet s; - for (const FileTag &t : qAsConst(m_rule->inputs)) { + for (const FileTag &t : std::as_const(m_rule->inputs)) { for (Artifact *artifact : product->lookupArtifactsByFileTag(t)) { if (artifact->isTargetOfModule()) continue; @@ -246,7 +246,7 @@ ArtifactSet RuleNode::currentInputArtifacts() const if (m_rule->inputsFromDependencies.empty()) return s; - for (const FileTag &t : qAsConst(m_rule->inputsFromDependencies)) { + for (const FileTag &t : std::as_const(m_rule->inputsFromDependencies)) { for (Artifact *artifact : product->lookupArtifactsByFileTag(t)) { if (!artifact->isTargetOfModule()) continue; @@ -258,7 +258,7 @@ ArtifactSet RuleNode::currentInputArtifacts() const } } - for (const ResolvedProductConstPtr &dep : qAsConst(product->dependencies)) { + for (const auto &dep : std::as_const(product->dependencies)) { if (!dep->buildData) continue; for (Artifact * const a : filterByType<Artifact>(dep->buildData->allNodes())) { diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp index 437e3f4da..94cee0c62 100644 --- a/src/lib/corelib/buildgraph/rulesapplicator.cpp +++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp @@ -1,5 +1,3 @@ -#include <utility> - /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. @@ -41,7 +39,6 @@ #include "rulesapplicator.h" #include "buildgraph.h" -#include "productbuilddata.h" #include "projectbuilddata.h" #include "qtmocscanner.h" #include "rulecommands.h" @@ -57,7 +54,6 @@ #include <language/preparescriptobserver.h> #include <language/propertymapinternal.h> #include <language/resolvedfilecontext.h> -#include <language/scriptengine.h> #include <logging/categories.h> #include <logging/translator.h> #include <tools/error.h> @@ -69,7 +65,6 @@ #include <QtCore/qcryptographichash.h> #include <QtCore/qdir.h> -#include <QtScript/qscriptvalueiterator.h> #include <memory> #include <vector> @@ -79,12 +74,13 @@ namespace Internal { RulesApplicator::RulesApplicator( ResolvedProductPtr product, - std::unordered_map<QString, const ResolvedProduct *> productsByName, - std::unordered_map<QString, const ResolvedProject *> projectsByName, + const std::unordered_map<QString, const ResolvedProduct *> &productsByName, + const std::unordered_map<QString, const ResolvedProject *> &projectsByName, Logger logger) : m_product(std::move(product)) - , m_productsByName(std::move(productsByName)) - , m_projectsByName(std::move(projectsByName)) + // m_productsByName and m_projectsByName are references, cannot move-construct + , m_productsByName(productsByName) + , m_projectsByName(projectsByName) , m_mocScanner(nullptr) , m_logger(std::move(logger)) { @@ -112,10 +108,10 @@ void RulesApplicator::applyRule(RuleNode *ruleNode, const ArtifactSet &inputArti m_completeInputSet = inputArtifacts; if (m_rule->name.startsWith(QLatin1String("QtCoreMocRule"))) { delete m_mocScanner; - m_mocScanner = new QtMocScanner(m_product, scope()); + m_mocScanner = new QtMocScanner(m_product, engine(), scope()); } - QScriptValue prepareScriptContext = engine()->newObject(); - prepareScriptContext.setPrototype(engine()->globalObject()); + ScopedJsValue prepareScriptContext(jsContext(), engine()->newObject()); + JS_SetPrototype(jsContext(), prepareScriptContext, engine()->globalObject()); setupScriptEngineForFile(engine(), m_rule->prepareScript.fileContext(), scope(), ObserveMode::Enabled); setupScriptEngineForProduct(engine(), m_product.get(), m_rule->module.get(), @@ -133,6 +129,7 @@ void RulesApplicator::applyRule(RuleNode *ruleNode, const ArtifactSet &inputArti } if (engine()->usesIo()) m_ruleUsesIo = true; + engine()->releaseInputArtifactScriptValues(ruleNode); } void RulesApplicator::handleRemovedRuleOutputs(const ArtifactSet &inputArtifacts, @@ -149,7 +146,7 @@ void RulesApplicator::handleRemovedRuleOutputs(const ArtifactSet &inputArtifacts project->buildData->removeArtifactAndExclusiveDependents(removedArtifact, logger, true, &artifactsToRemove); } - for (Artifact * const artifact : qAsConst(artifactsToRemove)) { + for (Artifact * const artifact : std::as_const(artifactsToRemove)) { QBS_CHECK(!inputArtifacts.contains(artifact)); removedArtifacts << artifact->filePath(); delete artifact; @@ -163,9 +160,9 @@ ArtifactSet RulesApplicator::collectAuxiliaryInputs(const Rule *rule, CurrentProduct | Dependencies); } -static void copyProperty(const QString &name, const QScriptValue &src, QScriptValue dst) +static void copyProperty(JSContext *ctx, const QString &name, const JSValue &src, JSValue dst) { - dst.setProperty(name, src.property(name)); + setJsProperty(ctx, dst, name, getJsProperty(ctx, src, name)); } static QStringList toStringList(const ArtifactSet &artifacts) @@ -179,7 +176,7 @@ static QStringList toStringList(const ArtifactSet &artifacts) return lst; } -void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext) +void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, JSValue prepareScriptContext) { evalContext()->checkForCancelation(); for (const Artifact *inputArtifact : inputArtifacts) @@ -201,20 +198,22 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p engine()->clearRequestedProperties(); // create the output artifacts from the set of input artifacts - m_transformer->setupInputs(prepareScriptContext); - m_transformer->setupExplicitlyDependsOn(prepareScriptContext); - copyProperty(StringConstants::inputsVar(), prepareScriptContext, scope()); - copyProperty(StringConstants::inputVar(), prepareScriptContext, scope()); - copyProperty(StringConstants::explicitlyDependsOnVar(), prepareScriptContext, scope()); - copyProperty(StringConstants::productVar(), prepareScriptContext, scope()); - copyProperty(StringConstants::projectVar(), prepareScriptContext, scope()); + m_transformer->setupInputs(engine(), prepareScriptContext); + m_transformer->setupExplicitlyDependsOn(engine(), prepareScriptContext); + copyProperty(jsContext(), StringConstants::inputsVar(), prepareScriptContext, scope()); + copyProperty(jsContext(), StringConstants::inputVar(), prepareScriptContext, scope()); + copyProperty(jsContext(), StringConstants::explicitlyDependsOnVar(), + prepareScriptContext, scope()); + copyProperty(jsContext(), StringConstants::productVar(), prepareScriptContext, scope()); + copyProperty(jsContext(), StringConstants::projectVar(), prepareScriptContext, scope()); if (m_rule->isDynamic()) { - outputArtifacts = runOutputArtifactsScript(inputArtifacts, - ScriptEngine::argumentList(Rule::argumentNamesForOutputArtifacts(), scope())); + const ScopedJsValueList argList + = engine()->argumentList(Rule::argumentNamesForOutputArtifacts(), scope()); + outputArtifacts = runOutputArtifactsScript(inputArtifacts, argList); } else { Set<QString> outputFilePaths; - for (const RuleArtifactConstPtr &ruleArtifact : m_rule->artifacts) { - const OutputArtifactInfo outputInfo = createOutputArtifactFromRuleArtifact( + for (const auto &ruleArtifact : m_rule->artifacts) { + const OutputArtifactInfo &outputInfo = createOutputArtifactFromRuleArtifact( ruleArtifact, inputArtifacts, &outputFilePaths); if (!outputInfo.artifact) continue; @@ -227,7 +226,7 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p } } - ArtifactSet newOutputs = ArtifactSet::fromList(outputArtifacts); + const auto newOutputs = rangeTo<ArtifactSet>(outputArtifacts); const ArtifactSet oldOutputs = collectOldOutputArtifacts(inputArtifacts); handleRemovedRuleOutputs(m_completeInputSet, oldOutputs - newOutputs, m_removedArtifacts, m_logger); @@ -248,59 +247,64 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p if (outputArtifacts.empty()) return; - for (Artifact * const outputArtifact : qAsConst(outputArtifacts)) { - for (Artifact * const dependency : qAsConst(m_transformer->explicitlyDependsOn)) + for (Artifact * const outputArtifact : std::as_const(outputArtifacts)) { + for (Artifact * const dependency : std::as_const(m_transformer->explicitlyDependsOn)) connect(outputArtifact, dependency); } if (inputArtifacts != m_transformer->inputs) - m_transformer->setupInputs(prepareScriptContext); + m_transformer->setupInputs(engine(), prepareScriptContext); // change the transformer outputs according to the bindings in Artifact - QScriptValue scriptValue; - if (!ruleArtifactArtifactMap.empty()) - engine()->setGlobalObject(prepareScriptContext); - for (auto it = ruleArtifactArtifactMap.crbegin(), end = ruleArtifactArtifactMap.crend(); - it != end; ++it) { - const RuleArtifact *ra = it->first; - if (ra->bindings.empty()) - continue; - - // expose attributes of this artifact - const OutputArtifactInfo outputInfo = it->second; - Artifact *outputArtifact = outputInfo.artifact; - outputArtifact->properties = outputArtifact->properties->clone(); + if (!ruleArtifactArtifactMap.empty()) { + const TemporaryGlobalObjectSetter gos(engine(), prepareScriptContext); + for (auto it = ruleArtifactArtifactMap.crbegin(), end = ruleArtifactArtifactMap.crend(); + it != end; ++it) { + const RuleArtifact *ra = it->first; + if (ra->bindings.empty()) + continue; - scope().setProperty(StringConstants::fileNameProperty(), - engine()->toScriptValue(outputArtifact->filePath())); - scope().setProperty(StringConstants::fileTagsProperty(), - toScriptValue(engine(), outputArtifact->fileTags().toStringList())); - - QVariantMap artifactModulesCfg = outputArtifact->properties->value(); - for (const auto &binding : ra->bindings) { - scriptValue = engine()->evaluate(binding.code); - if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue))) { - QString msg = QStringLiteral("evaluating rule binding '%1': %2"); - throw ErrorInfo(msg.arg(binding.name.join(QLatin1Char('.')), - engine()->lastErrorString(scriptValue)), - engine()->lastErrorLocation(scriptValue, binding.location)); + // expose attributes of this artifact + const OutputArtifactInfo &outputInfo = it->second; + Artifact *outputArtifact = outputInfo.artifact; + outputArtifact->properties = outputArtifact->properties->clone(); + + setJsProperty(jsContext(), scope(), StringConstants::fileNameProperty(), + engine()->toScriptValue(outputArtifact->filePath())); + setJsProperty(jsContext(), scope(), StringConstants::fileTagsProperty(), + makeJsStringList(engine()->context(), + outputArtifact->fileTags().toStringList())); + + QVariantMap artifactModulesCfg = outputArtifact->properties->value(); + for (const auto &binding : ra->bindings) { + const ScopedJsValue scriptValue(jsContext(), engine()->evaluate( + JsValueOwner::Caller, binding.code, + binding.location.filePath(), + binding.location.line())); + if (JsException ex = engine()->checkAndClearException(binding.location)) { + ErrorInfo err = ex.toErrorInfo(); + err.prepend(QStringLiteral("evaluating rule binding '%1'") + .arg(binding.name.join(QLatin1Char('.')))); + throw err; + } + const QVariant value = getJsVariant(jsContext(), scriptValue); + setConfigProperty(artifactModulesCfg, binding.name, value); + outputArtifact->pureProperties.emplace_back(binding.name, value); + } + outputArtifact->properties->setValue(artifactModulesCfg); + if (!outputInfo.newlyCreated + && (outputArtifact->fileTags() != outputInfo.oldFileTags + || !qVariantMapsEqual( + outputArtifact->properties->value(), outputInfo.oldProperties))) { + invalidateArtifactAsRuleInputIfNecessary(outputArtifact); } - const QVariant value = scriptValue.toVariant(); - setConfigProperty(artifactModulesCfg, binding.name, value); - outputArtifact->pureProperties.emplace_back(binding.name, value); - } - outputArtifact->properties->setValue(artifactModulesCfg); - if (!outputInfo.newlyCreated && (outputArtifact->fileTags() != outputInfo.oldFileTags - || outputArtifact->properties->value() != outputInfo.oldProperties)) { - invalidateArtifactAsRuleInputIfNecessary(outputArtifact); } } - if (!ruleArtifactArtifactMap.empty()) - engine()->setGlobalObject(prepareScriptContext.prototype()); - m_transformer->setupOutputs(prepareScriptContext); - m_transformer->createCommands(engine(), m_rule->prepareScript, - ScriptEngine::argumentList(Rule::argumentNamesForPrepare(), prepareScriptContext)); + m_transformer->setupOutputs(engine(), prepareScriptContext); + const ScopedJsValueList argList = engine()->argumentList(Rule::argumentNamesForPrepare(), + prepareScriptContext); + m_transformer->createCommands(engine(), m_rule->prepareScript, argList); if (Q_UNLIKELY(m_transformer->commands.empty())) throw ErrorInfo(Tr::tr("There is a rule without commands: %1.") .arg(m_rule->toString()), m_rule->prepareScript.location()); @@ -310,7 +314,7 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p || m_oldTransformer->commands != m_transformer->commands || commandsNeedRerun(m_transformer.get(), m_product.get(), m_productsByName, m_projectsByName)) { - for (Artifact * const output : qAsConst(outputArtifacts)) { + for (Artifact * const output : std::as_const(outputArtifacts)) { output->clearTimestamp(); m_invalidatedArtifacts += output; } @@ -354,7 +358,7 @@ ArtifactSet RulesApplicator::collectAdditionalInputs(const FileTags &tags, const } if (inputsSources.testFlag(Dependencies)) { - for (const ResolvedProductConstPtr &depProduct : product->dependencies) { + for (const auto &depProduct : product->dependencies) { for (Artifact * const ta : depProduct->targetArtifacts()) { if (ta->fileTags().contains(fileTag) && !ta->fileTags().intersects(rule->excludedInputs)) { @@ -385,12 +389,14 @@ RulesApplicator::OutputArtifactInfo RulesApplicator::createOutputArtifactFromRul FileTags fileTags; bool alwaysUpdated; if (ruleArtifact) { - QScriptValue scriptValue = engine()->evaluate(ruleArtifact->filePath, - ruleArtifact->filePathLocation.filePath(), - ruleArtifact->filePathLocation.line()); - if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue))) - throw engine()->lastError(scriptValue, ruleArtifact->filePathLocation); - outputPath = scriptValue.toString(); + const ScopedJsValue scriptValue( + jsContext(), + engine()->evaluate(JsValueOwner::Caller, ruleArtifact->filePath, + ruleArtifact->filePathLocation.filePath(), + ruleArtifact->filePathLocation.line())); + if (JsException ex = engine()->checkAndClearException(ruleArtifact->filePathLocation)) + throw ex.toErrorInfo(); + outputPath = getJsString(jsContext(), scriptValue); fileTags = ruleArtifact->fileTags; alwaysUpdated = ruleArtifact->alwaysUpdated; } else { @@ -414,11 +420,7 @@ RulesApplicator::OutputArtifactInfo RulesApplicator::createOutputArtifactFromRul RulesApplicator::OutputArtifactInfo RulesApplicator::createOutputArtifact(const QString &filePath, const FileTags &fileTags, bool alwaysUpdated, const ArtifactSet &inputArtifacts) { - QString outputPath = filePath; - // don't let the output artifact "escape" its build dir - outputPath.replace(StringConstants::dotDot(), QStringLiteral("dotdot")); - outputPath = resolveOutPath(outputPath); - + const QString outputPath = resolveOutPath(filePath); if (m_rule->isDynamic()) { const Set<FileTag> undeclaredTags = fileTags - m_rule->collectedOutputFileTags(); if (!undeclaredTags.empty()) { @@ -513,26 +515,31 @@ public: }; QList<Artifact *> RulesApplicator::runOutputArtifactsScript(const ArtifactSet &inputArtifacts, - const QScriptValueList &args) + const JSValueList &args) { QList<Artifact *> lst; - QScriptValue fun = engine()->evaluate(m_rule->outputArtifactsScript.sourceCode(), - m_rule->outputArtifactsScript.location().filePath(), - m_rule->outputArtifactsScript.location().line()); - if (!fun.isFunction()) + const ScopedJsValue fun(jsContext(), + engine()->evaluate(JsValueOwner::Caller, + m_rule->outputArtifactsScript.sourceCode(), + m_rule->outputArtifactsScript.location().filePath(), + m_rule->outputArtifactsScript.location().line())); + if (!JS_IsFunction(jsContext(), fun)) throw ErrorInfo(QStringLiteral("Function expected."), m_rule->outputArtifactsScript.location()); - QScriptValue res = fun.call(QScriptValue(), args); - engine()->releaseResourcesOfScriptObjects(); - if (engine()->hasErrorOrException(res)) - throw engine()->lastError(res, m_rule->outputArtifactsScript.location()); - if (!res.isArray()) + JSValueList argv(args.begin(), args.end()); + const ScopedJsValue res( + jsContext(), + JS_Call(jsContext(), fun, engine()->globalObject(), int(args.size()), argv.data())); + if (JsException ex = engine()->checkAndClearException(m_rule->outputArtifactsScript.location())) + throw ex.toErrorInfo(); + if (!JS_IsArray(jsContext(), res)) throw ErrorInfo(Tr::tr("Rule.outputArtifacts must return an array of objects."), m_rule->outputArtifactsScript.location()); - const quint32 c = res.property(StringConstants::lengthProperty()).toUInt32(); + const quint32 c = getJsIntProperty(jsContext(), res, StringConstants::lengthProperty()); for (quint32 i = 0; i < c; ++i) { try { - lst.push_back(createOutputArtifactFromScriptValue(res.property(i), inputArtifacts)); + ScopedJsValue val(engine()->context(), JS_GetPropertyUint32(jsContext(), res, i)); + lst.push_back(createOutputArtifactFromScriptValue(val, inputArtifacts)); } catch (const RuleOutputArtifactsException &roae) { ErrorInfo ei = roae; ei.prepend(Tr::tr("Error in Rule.outputArtifacts[%1]").arg(i), @@ -555,6 +562,8 @@ class ArtifactBindingsExtractor QString name; QVariant value; }; + ScriptEngine *m_engine = nullptr; + JSContext *m_ctx = nullptr; std::vector<Entry> m_propertyValues; static Set<QString> getArtifactItemPropertyNames() @@ -569,35 +578,36 @@ class ArtifactBindingsExtractor return s; } - void extractPropertyValues(const QScriptValue &obj, const QString &moduleName = QString()) + void extractPropertyValues(const JSValue &obj, const QString &moduleName = QString()) { - QScriptValueIterator svit(obj); - while (svit.hasNext()) { - svit.next(); - const QString name = svit.name(); + handleJsProperties(m_ctx, obj, [&](const JSAtom &prop, const JSPropertyDescriptor &desc) { + const QString name = getJsString(m_ctx, prop); if (moduleName.isEmpty()) { // Ignore property names that are part of the Artifact item. static const Set<QString> artifactItemPropertyNames = getArtifactItemPropertyNames(); if (artifactItemPropertyNames.contains(name)) - continue; + return; } - const QScriptValue value = svit.value(); - if (value.isObject() && !value.isArray() && !value.isError() && !value.isRegExp()) { + const JSValue value = desc.value; + if (JS_IsObject(value) && !JS_IsArray(m_ctx, value) && !JS_IsError(m_ctx, value) + && !JS_IsRegExp(m_ctx, value)) { QString newModuleName; if (!moduleName.isEmpty()) newModuleName.append(moduleName + QLatin1Char('.')); newModuleName.append(name); extractPropertyValues(value, newModuleName); } else { - m_propertyValues.emplace_back(moduleName, name, value.toVariant()); + m_propertyValues.emplace_back(moduleName, name, getJsVariant(m_ctx, value)); } - } + }); } public: - void apply(Artifact *outputArtifact, const QScriptValue &obj) + void apply(ScriptEngine *engine, Artifact *outputArtifact, const JSValue &obj) { + m_engine = engine; + m_ctx = m_engine->context(); extractPropertyValues(obj); if (m_propertyValues.empty()) return; @@ -613,24 +623,27 @@ public: } }; -Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValue &obj, +Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const JSValue &obj, const ArtifactSet &inputArtifacts) { - if (!obj.isObject()) { + if (!JS_IsObject(obj)) { throw ErrorInfo(Tr::tr("Elements of the Rule.outputArtifacts array must be " "of Object type."), m_rule->outputArtifactsScript.location()); } - const QString unresolvedFilePath - = obj.property(StringConstants::filePathProperty()).toVariant().toString(); + QString unresolvedFilePath; + const ScopedJsValue jsFilePath(jsContext(), getJsProperty(jsContext(), obj, + StringConstants::filePathProperty())); + if (JS_IsString(jsFilePath)) + unresolvedFilePath = getJsString(jsContext(), jsFilePath); if (unresolvedFilePath.isEmpty()) { throw RuleOutputArtifactsException( Tr::tr("Property filePath must be a non-empty string.")); } const QString filePath = FileInfo::resolvePath(m_product->buildDirectory(), unresolvedFilePath); const FileTags fileTags = FileTags::fromStringList( - obj.property(StringConstants::fileTagsProperty()).toVariant().toStringList()); - const QVariant alwaysUpdatedVar - = obj.property(StringConstants::alwaysUpdatedProperty()).toVariant(); + getJsStringListProperty(jsContext(), obj, StringConstants::fileTagsProperty())); + const QVariant alwaysUpdatedVar = getJsVariantProperty(jsContext(), obj, + StringConstants::alwaysUpdatedProperty()); const bool alwaysUpdated = alwaysUpdatedVar.isValid() ? alwaysUpdatedVar.toBool() : true; OutputArtifactInfo outputInfo = createOutputArtifact(filePath, fileTags, alwaysUpdated, inputArtifacts); @@ -641,16 +654,17 @@ Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValu "Alternatively, a FileTagger can be provided.") .arg(unresolvedFilePath)); } - const FileTags explicitlyDependsOn = FileTags::fromStringList( - obj.property(StringConstants::explicitlyDependsOnProperty()) - .toVariant().toStringList()); + const FileTags explicitlyDependsOn = FileTags::fromStringList(getJsStringListProperty( + jsContext(), obj, StringConstants::explicitlyDependsOnProperty())); for (const FileTag &tag : explicitlyDependsOn) { for (Artifact * const dependency : m_product->lookupArtifactsByFileTag(tag)) connect(outputInfo.artifact, dependency); } - ArtifactBindingsExtractor().apply(outputInfo.artifact, obj); - if (!outputInfo.newlyCreated && (outputInfo.artifact->fileTags() != outputInfo.oldFileTags - || outputInfo.artifact->properties->value() != outputInfo.oldProperties)) { + ArtifactBindingsExtractor().apply(engine(), outputInfo.artifact, obj); + if (!outputInfo.newlyCreated + && (outputInfo.artifact->fileTags() != outputInfo.oldFileTags + || !qVariantMapsEqual( + outputInfo.artifact->properties->value(), outputInfo.oldProperties))) { invalidateArtifactAsRuleInputIfNecessary(outputInfo.artifact); } return outputInfo.artifact; @@ -658,9 +672,14 @@ Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValu QString RulesApplicator::resolveOutPath(const QString &path) const { - QString buildDir = m_product->topLevelProject()->buildDirectory; - QString result = FileInfo::resolvePath(buildDir, path); - result = QDir::cleanPath(result); + const QString buildDir = m_product->topLevelProject()->buildDirectory; + QString result = QDir::cleanPath(FileInfo::resolvePath(buildDir, path)); + if (!result.startsWith(buildDir + QLatin1Char('/'))) { + throw ErrorInfo( + Tr::tr("Refusing to create artifact '%1' outside of build directory '%2'.") + .arg(QDir::toNativeSeparators(result), QDir::toNativeSeparators(buildDir)), + m_rule->prepareScript.location()); + } return result; } @@ -669,15 +688,9 @@ const RulesEvaluationContextPtr &RulesApplicator::evalContext() const return m_product->topLevelProject()->buildData->evaluationContext; } -ScriptEngine *RulesApplicator::engine() const -{ - return evalContext()->engine(); -} - -QScriptValue RulesApplicator::scope() const -{ - return evalContext()->scope(); -} +ScriptEngine *RulesApplicator::engine() const { return evalContext()->engine(); } +JSContext *RulesApplicator::jsContext() const { return engine()->context(); } +JSValue RulesApplicator::scope() const { return evalContext()->scope(); } } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/buildgraph/rulesapplicator.h b/src/lib/corelib/buildgraph/rulesapplicator.h index 1160f3d09..72f9aadcc 100644 --- a/src/lib/corelib/buildgraph/rulesapplicator.h +++ b/src/lib/corelib/buildgraph/rulesapplicator.h @@ -44,12 +44,13 @@ #include "nodeset.h" #include <language/filetags.h> #include <language/forward_decls.h> +#include <language/scriptengine.h> #include <logging/logger.h> +#include <quickjs.h> #include <QtCore/qflags.h> #include <QtCore/qhash.h> #include <QtCore/qstring.h> -#include <QtScript/qscriptvalue.h> #include <unordered_map> @@ -63,8 +64,8 @@ class RulesApplicator { public: RulesApplicator(ResolvedProductPtr product, - std::unordered_map<QString, const ResolvedProduct *> productsByName, - std::unordered_map<QString, const ResolvedProject *> projectsByName, + const std::unordered_map<QString, const ResolvedProduct *> &productsByName, + const std::unordered_map<QString, const ResolvedProject *> &projectsByName, Logger logger); ~RulesApplicator(); @@ -85,7 +86,7 @@ public: Q_DECLARE_FLAGS(InputsSources, InputsSourceFlag) private: - void doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext); + void doApply(const ArtifactSet &inputArtifacts, JSValue prepareScriptContext); ArtifactSet collectOldOutputArtifacts(const ArtifactSet &inputArtifacts) const; struct OutputArtifactInfo { @@ -100,13 +101,14 @@ private: OutputArtifactInfo createOutputArtifact(const QString &filePath, const FileTags &fileTags, bool alwaysUpdated, const ArtifactSet &inputArtifacts); QList<Artifact *> runOutputArtifactsScript(const ArtifactSet &inputArtifacts, - const QScriptValueList &args); - Artifact *createOutputArtifactFromScriptValue(const QScriptValue &obj, + const JSValueList &args); + Artifact *createOutputArtifactFromScriptValue(const JSValue &obj, const ArtifactSet &inputArtifacts); QString resolveOutPath(const QString &path) const; const RulesEvaluationContextPtr &evalContext() const; ScriptEngine *engine() const; - QScriptValue scope() const; + JSContext *jsContext() const; + JSValue scope() const; static ArtifactSet collectAdditionalInputs(const FileTags &tags, const Rule *rule, const ResolvedProduct *product, diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp index 6ae230329..2d0f5d44d 100644 --- a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp +++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp @@ -38,9 +38,7 @@ ****************************************************************************/ #include "rulesevaluationcontext.h" -#include "artifact.h" #include "rulecommands.h" -#include "transformer.h" #include <language/language.h> #include <language/scriptengine.h> #include <logging/translator.h> @@ -57,17 +55,18 @@ RulesEvaluationContext::RulesEvaluationContext(Logger logger) : m_logger(std::move(logger)), m_engine(ScriptEngine::create(m_logger, EvalContext::RuleExecution)), m_observer(nullptr), - m_initScopeCalls(0) + m_initScopeCalls(0), + m_prepareScriptScope(m_engine->newObject()) { - m_prepareScriptScope = m_engine->newObject(); - m_prepareScriptScope.setPrototype(m_engine->globalObject()); - ProcessCommand::setupForJavaScript(m_prepareScriptScope); - JavaScriptCommand::setupForJavaScript(m_prepareScriptScope); + + JS_SetPrototype(m_engine->context(), m_prepareScriptScope, m_engine->globalObject()); + ProcessCommand::setupForJavaScript(m_engine.get(), m_prepareScriptScope); + JavaScriptCommand::setupForJavaScript(m_engine.get(), m_prepareScriptScope); } RulesEvaluationContext::~RulesEvaluationContext() { - delete m_engine; + JS_FreeValue(m_engine->context(), m_prepareScriptScope); } void RulesEvaluationContext::initializeObserver(const QString &description, int maximumProgress) @@ -95,7 +94,7 @@ void RulesEvaluationContext::initScope() m_engine->setActive(true); m_scope = m_engine->newObject(); - m_scope.setPrototype(m_prepareScriptScope); + JS_SetPrototype(m_engine->context(), m_scope, m_prepareScriptScope); m_engine->setGlobalObject(m_scope); } @@ -105,8 +104,11 @@ void RulesEvaluationContext::cleanupScope() if (--m_initScopeCalls > 0) return; - m_scope = QScriptValue(); - m_engine->setGlobalObject(m_prepareScriptScope.prototype()); + JS_FreeValue(m_engine->context(), m_scope); + m_scope = JS_UNDEFINED; + const ScopedJsValue proto(engine()->context(), + JS_GetPrototype(m_engine->context(), m_prepareScriptScope)); + m_engine->setGlobalObject(proto); m_engine->setActive(false); } diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.h b/src/lib/corelib/buildgraph/rulesevaluationcontext.h index a5d81ce61..7ff75b51f 100644 --- a/src/lib/corelib/buildgraph/rulesevaluationcontext.h +++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.h @@ -42,12 +42,11 @@ #include <language/forward_decls.h> #include <logging/logger.h> +#include <quickjs.h> + #include <QtCore/qhash.h> #include <QtCore/qstring.h> -#include <QtScript/qscriptprogram.h> -#include <QtScript/qscriptvalue.h> - namespace qbs { namespace Internal { class ProgressObserver; @@ -69,8 +68,8 @@ public: RulesEvaluationContext * const m_evalContext; }; - ScriptEngine *engine() const { return m_engine; } - QScriptValue scope() const { return m_scope; } + ScriptEngine *engine() const { return m_engine.get(); } + JSValue scope() const { return m_scope; } void setObserver(ProgressObserver *observer) { m_observer = observer; } ProgressObserver *observer() const { return m_observer; } @@ -79,17 +78,15 @@ public: void checkForCancelation(); private: - friend class Scope; - void initScope(); void cleanupScope(); Logger m_logger; - ScriptEngine * const m_engine; + const std::unique_ptr<ScriptEngine> m_engine; ProgressObserver *m_observer; unsigned int m_initScopeCalls; - QScriptValue m_scope; - QScriptValue m_prepareScriptScope; + JSValue m_scope = JS_UNDEFINED; + const JSValue m_prepareScriptScope; }; } // namespace Internal diff --git a/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h b/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h deleted file mode 100644 index f6154f993..000000000 --- a/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qbs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QBS_SCRIPTCLASSPROPERTYITERATOR_H -#define QBS_SCRIPTCLASSPROPERTYITERATOR_H - -#include <tools/qbsassert.h> - -#include <QtCore/qmap.h> -#include <QtCore/qstring.h> -#include <QtScript/qscriptclasspropertyiterator.h> -#include <QtScript/qscriptengine.h> -#include <QtScript/qscriptstring.h> - -#include <vector> - -namespace qbs { -namespace Internal { - -class ScriptClassPropertyIterator : public QScriptClassPropertyIterator -{ -public: - ScriptClassPropertyIterator(const QScriptValue &object, const QVariantMap &properties, - const std::vector<QString> &additionalProperties) - : QScriptClassPropertyIterator(object), - m_it(properties), - m_additionalProperties(additionalProperties) - { - } - -private: - bool hasNext() const override - { - return m_it.hasNext() || m_index < int(m_additionalProperties.size()) - 1; - } - bool hasPrevious() const override { return m_index > -1 || m_it.hasPrevious(); } - void toFront() override { m_it.toFront(); m_index = -1; } - void toBack() override { m_it.toBack(); m_index = int(m_additionalProperties.size()) - 1; } - - void next() override - { - QBS_ASSERT(hasNext(), return); - if (m_it.hasNext()) - m_it.next(); - else - ++m_index; - } - - void previous() override - { - QBS_ASSERT(hasPrevious(), return); - if (m_index >= 0) - --m_index; - if (m_index == -1) - m_it.previous(); - } - - QScriptString name() const override - { - const QString theName = m_index >= 0 && m_index < int(m_additionalProperties.size()) - ? m_additionalProperties.at(m_index) - : m_it.key(); - return object().engine()->toStringHandle(theName); - } - - QMapIterator<QString, QVariant> m_it; - const std::vector<QString> m_additionalProperties; - int m_index = -1; -}; - -} // namespace Internal -} // namespace qbs - -#endif // QBS_SCRIPTCLASSPROPERTYITERATOR_H diff --git a/src/lib/corelib/buildgraph/timestampsupdater.cpp b/src/lib/corelib/buildgraph/timestampsupdater.cpp index d31f57445..3f5279dd2 100644 --- a/src/lib/corelib/buildgraph/timestampsupdater.cpp +++ b/src/lib/corelib/buildgraph/timestampsupdater.cpp @@ -83,7 +83,7 @@ private: }; void TimestampsUpdater::updateTimestamps(const TopLevelProjectPtr &project, - const QList<ResolvedProductPtr> &products, const Logger &logger) + const QVector<ResolvedProductPtr> &products, const Logger &logger) { TimestampsUpdateVisitor v; for (const ResolvedProductPtr &product : products) diff --git a/src/lib/corelib/buildgraph/timestampsupdater.h b/src/lib/corelib/buildgraph/timestampsupdater.h index cfe20df12..8184ca708 100644 --- a/src/lib/corelib/buildgraph/timestampsupdater.h +++ b/src/lib/corelib/buildgraph/timestampsupdater.h @@ -51,7 +51,7 @@ class TimestampsUpdater { public: void updateTimestamps(const TopLevelProjectPtr &project, - const QList<ResolvedProductPtr> &products, const Logger &logger); + const QVector<ResolvedProductPtr> &products, const Logger &logger); }; } // namespace Internal diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp index 5e27c3e74..2346ad5c9 100644 --- a/src/lib/corelib/buildgraph/transformer.cpp +++ b/src/lib/corelib/buildgraph/transformer.cpp @@ -39,6 +39,7 @@ #include "transformer.h" #include "artifact.h" +#include "productbuilddata.h" #include <jsextensions/moduleproperties.h> #include <language/language.h> #include <language/preparescriptobserver.h> @@ -54,6 +55,7 @@ #include <QtCore/qdir.h> #include <algorithm> +#include <vector> namespace qbs { namespace Internal { @@ -64,78 +66,82 @@ Transformer::Transformer() : alwaysRun(false) Transformer::~Transformer() = default; -static QScriptValue js_baseName(QScriptContext *ctx, QScriptEngine *engine, - const Artifact *artifact) +static JSValue js_baseName(JSContext *ctx, JSValueConst this_val, int, JSValueConst *) { - Q_UNUSED(ctx); - Q_UNUSED(engine); - return {FileInfo::baseName(artifact->filePath())}; + return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val, + [ctx](const Artifact *a) { + return makeJsString(ctx, FileInfo::baseName(a->filePath())); + }); } -static QScriptValue js_completeBaseName(QScriptContext *ctx, QScriptEngine *engine, - const Artifact *artifact) +static JSValue js_completeBaseName(JSContext *ctx, JSValueConst this_val, int, JSValueConst *) { - Q_UNUSED(ctx); - Q_UNUSED(engine); - return {FileInfo::completeBaseName(artifact->filePath())}; + return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val, + [ctx](const Artifact *a) { + return makeJsString(ctx, FileInfo::completeBaseName(a->filePath())); + }); } -static QScriptValue js_baseDir(QScriptContext *ctx, QScriptEngine *engine, - const Artifact *artifact) +static JSValue js_baseDir(JSContext *ctx, JSValueConst this_val, int, JSValueConst *) { - Q_UNUSED(ctx); - Q_UNUSED(engine); - QString basedir; - if (artifact->artifactType == Artifact::SourceFile) { - QDir sourceDir(artifact->product->sourceDirectory); - basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath())); - } else { - QDir buildDir(artifact->product->buildDirectory()); - basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath())); - } - return basedir; + return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val, + [ctx](const Artifact *artifact) { + QString basedir; + if (artifact->artifactType == Artifact::SourceFile) { + QDir sourceDir(artifact->product->sourceDirectory); + basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath())); + } else { + QDir buildDir(artifact->product->buildDirectory()); + basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath())); + } + return makeJsString(ctx, basedir); + }); } -static QScriptValue js_children(QScriptContext *ctx, QScriptEngine *engine, const Artifact *artifact) +static JSValue js_children(JSContext *ctx, JSValueConst this_val, int, JSValueConst *) { - Q_UNUSED(ctx); - QScriptValue sv = engine->newArray(); - uint idx = 0; - for (const Artifact *child : artifact->childArtifacts()) { - sv.setProperty(idx++, Transformer::translateFileConfig(static_cast<ScriptEngine *>(engine), - child, QString())); - } - return sv; + return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val, + [ctx](const Artifact *artifact) { + JSValue sv = JS_NewArray(ctx); + uint idx = 0; + + // FIXME: childArtifacts() is not guarded by any mutex ... + for (Artifact *child : artifact->childArtifacts()) { + JS_SetPropertyUint32(ctx, sv, idx++, Transformer::translateFileConfig( + ScriptEngine::engineForContext(ctx), child, QString())); + } + return sv; + }); } -static void setArtifactProperty(QScriptValue &obj, const QString &name, - QScriptValue (*func)(QScriptContext *, QScriptEngine *, const Artifact *), - const Artifact *artifact) +static void setArtifactProperty(JSContext *ctx, JSValue &obj, const QString &name, + JSCFunction *func) { - obj.setProperty(name, static_cast<ScriptEngine *>(obj.engine())->newFunction(func, artifact), - QScriptValue::PropertyGetter); + const QByteArray nameBa = name.toUtf8(); + const JSValue jsFunc = JS_NewCFunction(ctx, func, nameBa.constData(), 0); + const ScopedJsAtom nameAtom(ctx, nameBa); + JS_DefinePropertyGetSet(ctx, obj, nameAtom, jsFunc, JS_UNDEFINED, JS_PROP_HAS_GET); } -QScriptValue Transformer::translateFileConfig(ScriptEngine *scriptEngine, const Artifact *artifact, - const QString &defaultModuleName) +JSValue Transformer::translateFileConfig(ScriptEngine *engine, Artifact *artifact, + const QString &defaultModuleName) { - QScriptValue obj = scriptEngine->newObject(); - attachPointerTo(obj, artifact); - ModuleProperties::init(obj, artifact); - obj.setProperty(StringConstants::fileNameProperty(), artifact->fileName()); - obj.setProperty(StringConstants::filePathProperty(), artifact->filePath()); - setArtifactProperty(obj, StringConstants::baseNameProperty(), js_baseName, artifact); - setArtifactProperty(obj, StringConstants::completeBaseNameProperty(), js_completeBaseName, - artifact); - setArtifactProperty(obj, QStringLiteral("baseDir"), js_baseDir, artifact); - setArtifactProperty(obj, QStringLiteral("children"), js_children, artifact); - const QStringList fileTags = sorted(artifact->fileTags().toStringList()); - scriptEngine->setObservedProperty(obj, StringConstants::fileTagsProperty(), - scriptEngine->toScriptValue(fileTags)); - scriptEngine->observer()->addArtifactId(obj.objectId()); - if (!defaultModuleName.isEmpty()) - obj.setProperty(StringConstants::moduleNameProperty(), defaultModuleName); - return obj; + return engine->getArtifactScriptValue(artifact, defaultModuleName, [&](JSValue obj) { + ModuleProperties::init(engine, obj, artifact); + JSContext * const ctx = engine->context(); + setJsProperty(ctx, obj, StringConstants::fileNameProperty(), artifact->fileName()); + setJsProperty(ctx, obj, StringConstants::filePathProperty(), artifact->filePath()); + setArtifactProperty(ctx, obj, StringConstants::baseNameProperty(), js_baseName); + setArtifactProperty(ctx, obj, StringConstants::completeBaseNameProperty(), js_completeBaseName); + setArtifactProperty(ctx, obj, QStringLiteral("baseDir"), js_baseDir); + setArtifactProperty(ctx, obj, QStringLiteral("children"), js_children); + const QStringList fileTags = sorted(artifact->fileTags().toStringList()); + const ScopedJsValue jsFileTags(ctx, engine->toScriptValue(fileTags)); + engine->setObservedProperty(obj, StringConstants::fileTagsProperty(), jsFileTags); + engine->observer()->addArtifactId(jsObjectId(obj)); + if (!defaultModuleName.isEmpty()) + setJsProperty(ctx, obj, StringConstants::moduleNameProperty(), defaultModuleName); + }); } static bool compareByFilePath(const Artifact *a1, const Artifact *a2) @@ -143,9 +149,8 @@ static bool compareByFilePath(const Artifact *a1, const Artifact *a2) return a1->filePath() < a2->filePath(); } -QScriptValue Transformer::translateInOutputs(ScriptEngine *scriptEngine, - const ArtifactSet &artifacts, - const QString &defaultModuleName) +JSValue Transformer::translateInOutputs(ScriptEngine *engine, const ArtifactSet &artifacts, + const QString &defaultModuleName) { using TagArtifactsMap = QMap<QString, QList<Artifact*>>; TagArtifactsMap tagArtifactsMap; @@ -155,16 +160,16 @@ QScriptValue Transformer::translateInOutputs(ScriptEngine *scriptEngine, for (TagArtifactsMap::Iterator it = tagArtifactsMap.begin(); it != tagArtifactsMap.end(); ++it) std::sort(it.value().begin(), it.value().end(), compareByFilePath); - QScriptValue jsTagFiles = scriptEngine->newObject(); - for (TagArtifactsMap::const_iterator tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) { + JSValue jsTagFiles = engine->newObject(); + for (auto tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) { const QList<Artifact*> &artifacts = tag.value(); - QScriptValue jsFileConfig = scriptEngine->newArray(artifacts.size()); + JSValue jsFileConfig = JS_NewArray(engine->context()); int i = 0; for (Artifact * const artifact : artifacts) { - jsFileConfig.setProperty(i++, translateFileConfig(scriptEngine, artifact, - defaultModuleName)); + JS_SetPropertyUint32(engine->context(), jsFileConfig, i++, + translateFileConfig(engine, artifact, defaultModuleName)); } - jsTagFiles.setProperty(tag.key(), jsFileConfig); + setJsProperty(engine->context(), jsTagFiles, tag.key(), jsFileConfig); } return jsTagFiles; @@ -177,66 +182,70 @@ ResolvedProductPtr Transformer::product() const return (*outputs.cbegin())->product.lock(); } -void Transformer::setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs, - const QString &defaultModuleName) +void Transformer::setupInputs(ScriptEngine *engine, JSValue targetScriptValue, + const ArtifactSet &inputs, const QString &defaultModuleName) { - const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine()); - QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName); - targetScriptValue.setProperty(StringConstants::inputsVar(), scriptValue); - QScriptValue inputScriptValue; + JSValue scriptValue = translateInOutputs(engine, inputs, defaultModuleName); + setJsProperty(engine->context(), targetScriptValue, StringConstants::inputsVar(), scriptValue); + JSValue inputScriptValue = JS_UNDEFINED; if (inputs.size() == 1) { Artifact *input = *inputs.cbegin(); const FileTags &fileTags = input->fileTags(); QBS_ASSERT(!fileTags.empty(), return); - QScriptValue inputsForFileTag = scriptValue.property(fileTags.cbegin()->toString()); - inputScriptValue = inputsForFileTag.property(0); + const ScopedJsValue inputsForFileTag( + engine->context(), + getJsProperty(engine->context(), scriptValue, fileTags.cbegin()->toString())); + inputScriptValue = JS_GetPropertyUint32(engine->context(), inputsForFileTag, 0); } - targetScriptValue.setProperty(StringConstants::inputVar(), inputScriptValue); + setJsProperty(engine->context(), targetScriptValue, StringConstants::inputVar(), + inputScriptValue); } -void Transformer::setupInputs(QScriptValue targetScriptValue) +void Transformer::setupInputs(ScriptEngine *engine, const JSValue &targetScriptValue) { - setupInputs(targetScriptValue, inputs, rule->module->name); + setupInputs(engine, targetScriptValue, inputs, rule->module->name); } -void Transformer::setupOutputs(QScriptValue targetScriptValue) +void Transformer::setupOutputs(ScriptEngine *engine, JSValue targetScriptValue) { - const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine()); const QString &defaultModuleName = rule->module->name; - QScriptValue scriptValue = translateInOutputs(scriptEngine, outputs, defaultModuleName); - targetScriptValue.setProperty(StringConstants::outputsVar(), scriptValue); - QScriptValue outputScriptValue; + JSValue scriptValue = translateInOutputs(engine, outputs, defaultModuleName); + setJsProperty(engine->context(), targetScriptValue, StringConstants::outputsVar(), scriptValue); + JSValue outputScriptValue = JS_UNDEFINED; if (outputs.size() == 1) { Artifact *output = *outputs.cbegin(); const FileTags &fileTags = output->fileTags(); QBS_ASSERT(!fileTags.empty(), return); - QScriptValue outputsForFileTag = scriptValue.property(fileTags.cbegin()->toString()); - outputScriptValue = outputsForFileTag.property(0); + const ScopedJsValue outputsForFileTag( + engine->context(), + getJsProperty(engine->context(), scriptValue, fileTags.cbegin()->toString())); + outputScriptValue = JS_GetPropertyUint32(engine->context(), outputsForFileTag, 0); } - targetScriptValue.setProperty(StringConstants::outputVar(), outputScriptValue); + setJsProperty(engine->context(), targetScriptValue, StringConstants::outputVar(), + outputScriptValue); } -void Transformer::setupExplicitlyDependsOn(QScriptValue targetScriptValue) +void Transformer::setupExplicitlyDependsOn(ScriptEngine *engine, JSValue targetScriptValue) { - const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine()); - QScriptValue scriptValue = translateInOutputs(scriptEngine, explicitlyDependsOn, - rule->module->name); - targetScriptValue.setProperty(StringConstants::explicitlyDependsOnVar(), scriptValue); + JSValue scriptValue = translateInOutputs(engine, explicitlyDependsOn, rule->module->name); + setJsProperty(engine->context(), targetScriptValue, StringConstants::explicitlyDependsOnVar(), + scriptValue); } -AbstractCommandPtr Transformer::createCommandFromScriptValue(const QScriptValue &scriptValue, - const CodeLocation &codeLocation) +AbstractCommandPtr Transformer::createCommandFromScriptValue( + ScriptEngine *engine, const JSValue &scriptValue, const CodeLocation &codeLocation) { AbstractCommandPtr cmdBase; - if (scriptValue.isUndefined() || !scriptValue.isValid()) + if (JS_IsUndefined(scriptValue) || JS_IsUninitialized(scriptValue)) return cmdBase; - QString className = scriptValue.property(StringConstants::classNameProperty()).toString(); + QString className = getJsStringProperty(engine->context(), scriptValue, + StringConstants::classNameProperty()); if (className == StringConstants::commandType()) cmdBase = ProcessCommand::create(); else if (className == StringConstants::javaScriptCommandType()) cmdBase = JavaScriptCommand::create(); if (cmdBase) - cmdBase->fillFromScriptValue(&scriptValue, codeLocation); + cmdBase->fillFromScriptValue(engine->context(), &scriptValue, codeLocation); if (className == StringConstants::commandType()) { auto procCmd = static_cast<ProcessCommand *>(cmdBase.get()); procCmd->clearRelevantEnvValues(); @@ -248,18 +257,14 @@ AbstractCommandPtr Transformer::createCommandFromScriptValue(const QScriptValue } void Transformer::createCommands(ScriptEngine *engine, const PrivateScriptFunction &script, - const QScriptValueList &args) + const JSValueList &args) { - if (!script.scriptFunction.isValid() || script.scriptFunction.engine() != engine) { - script.scriptFunction = engine->evaluate(script.sourceCode(), - script.location().filePath(), - script.location().line()); - if (Q_UNLIKELY(!script.scriptFunction.isFunction())) - throw ErrorInfo(Tr::tr("Invalid prepare script."), script.location()); - } - - QScriptValue scriptValue = script.scriptFunction.call(QScriptValue(), args); - engine->releaseResourcesOfScriptObjects(); + JSValueList argv(args.cbegin(), args.cend()); + const JSValue function = script.getFunction(engine, Tr::tr("Invalid prepare script.")); + const ScopedJsValue scriptValue( + engine->context(), + JS_Call(engine->context(), function, engine->globalObject(), + int(argv.size()), argv.data())); propertiesRequestedInPrepareScript = engine->propertiesRequestedInScript(); propertiesRequestedFromArtifactInPrepareScript = engine->propertiesRequestedFromArtifact(); importedFilesUsedInPrepareScript = engine->importedFilesUsedInScript(); @@ -271,22 +276,24 @@ void Transformer::createCommands(ScriptEngine *engine, const PrivateScriptFuncti p->exportedModule)); } engine->clearRequestedProperties(); - if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue))) - throw engine->lastError(scriptValue, script.location()); + if (JsException ex = engine->checkAndClearException(script.location())) + throw ex.toErrorInfo(); commands.clear(); - if (scriptValue.isArray()) { - const int count = scriptValue.property(StringConstants::lengthProperty()).toInt32(); + if (JS_IsArray(engine->context(), scriptValue)) { + const int count = JS_VALUE_GET_INT(getJsProperty(engine->context(), scriptValue, + StringConstants::lengthProperty())); for (qint32 i = 0; i < count; ++i) { - QScriptValue item = scriptValue.property(i); - if (item.isValid() && !item.isUndefined()) { - const AbstractCommandPtr cmd - = createCommandFromScriptValue(item, script.location()); + ScopedJsValue item(engine->context(), + JS_GetPropertyUint32(engine->context(), scriptValue, i)); + if (!JS_IsUninitialized(item) && !JS_IsUndefined(item)) { + const AbstractCommandPtr cmd = createCommandFromScriptValue(engine, item, + script.location()); if (cmd) commands.addCommand(cmd); } } } else { - const AbstractCommandPtr cmd = createCommandFromScriptValue(scriptValue, + const AbstractCommandPtr cmd = createCommandFromScriptValue(engine, scriptValue, script.location()); if (cmd) commands.addCommand(cmd); diff --git a/src/lib/corelib/buildgraph/transformer.h b/src/lib/corelib/buildgraph/transformer.h index 2f6a8e56d..927572310 100644 --- a/src/lib/corelib/buildgraph/transformer.h +++ b/src/lib/corelib/buildgraph/transformer.h @@ -52,6 +52,8 @@ #include <tools/filetime.h> #include <tools/persistence.h> +#include <quickjs.h> + #include <QtCore/qhash.h> namespace qbs { @@ -91,15 +93,14 @@ public: bool commandsNeedChangeTracking = false; bool markedForRerun = false; - static QScriptValue translateFileConfig(ScriptEngine *scriptEngine, - const Artifact *artifact, - const QString &defaultModuleName); + static JSValue translateFileConfig(ScriptEngine *engine, Artifact *artifact, + const QString &defaultModuleName); ResolvedProductPtr product() const; - void setupInputs(QScriptValue targetScriptValue); - void setupOutputs(QScriptValue targetScriptValue); - void setupExplicitlyDependsOn(QScriptValue targetScriptValue); + void setupInputs(ScriptEngine *engine, const JSValue &targetScriptValue); + void setupOutputs(ScriptEngine *engine, JSValue targetScriptValue); + void setupExplicitlyDependsOn(ScriptEngine *engine, JSValue targetScriptValue); void createCommands(ScriptEngine *engine, const PrivateScriptFunction &script, - const QScriptValueList &args); + const JSValueList &args); void rescueChangeTrackingData(const TransformerConstPtr &other); Set<QString> jobPools() const; @@ -124,14 +125,13 @@ public: private: Transformer(); - AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue, + AbstractCommandPtr createCommandFromScriptValue(ScriptEngine *engine, const JSValue &scriptValue, const CodeLocation &codeLocation); - static void setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs, - const QString &defaultModuleName); - static QScriptValue translateInOutputs(ScriptEngine *scriptEngine, - const ArtifactSet &artifacts, - const QString &defaultModuleName); + static void setupInputs(ScriptEngine *engine, JSValue targetScriptValue, + const ArtifactSet &inputs, const QString &defaultModuleName); + static JSValue translateInOutputs(ScriptEngine *engine, const ArtifactSet &artifacts, + const QString &defaultModuleName); }; } // namespace Internal diff --git a/src/lib/corelib/buildgraph/transformerchangetracking.cpp b/src/lib/corelib/buildgraph/transformerchangetracking.cpp index 505f0cbba..ae43e8219 100644 --- a/src/lib/corelib/buildgraph/transformerchangetracking.cpp +++ b/src/lib/corelib/buildgraph/transformerchangetracking.cpp @@ -39,6 +39,7 @@ #include <tools/fileinfo.h> #include <tools/qbsassert.h> #include <tools/qttools.h> +#include <tools/stlutils.h> #include <QtCore/qvariant.h> @@ -71,7 +72,7 @@ private: const char *context) const; bool isExportedModuleUpToDate(const QString &productName, const ExportedModule &module) const; bool areExportedModulesUpToDate( - const std::unordered_map<QString, ExportedModule> exportedModules) const; + const std::unordered_map<QString, ExportedModule> &exportedModules) const; const Artifact *getArtifact(const QString &filePath, const QString &productName) const; const ResolvedProduct *getProduct(const QString &name) const; @@ -157,7 +158,7 @@ bool TrafoChangeTracker::checkForPropertyChange(const Property &restoredProperty case Property::PropertyInArtifact: QBS_CHECK(false); } - if (restoredProperty.value != v) { + if (!qVariantsEqual(restoredProperty.value, v)) { qCDebug(lcBuildGraph).noquote().nospace() << "Value for property '" << restoredProperty.moduleName << "." << restoredProperty.propertyName << "' has changed.\n" @@ -207,13 +208,11 @@ bool TrafoChangeTracker::isExportedModuleUpToDate(const QString &productName, } bool TrafoChangeTracker::areExportedModulesUpToDate( - const std::unordered_map<QString, ExportedModule> exportedModules) const + const std::unordered_map<QString, ExportedModule> &exportedModules) const { - for (const auto &kv : exportedModules) { - if (!isExportedModuleUpToDate(kv.first, kv.second)) - return false; - } - return true; + return Internal::all_of(exportedModules, [this](const auto &kv) { + return isExportedModuleUpToDate(kv.first, kv.second); + }); } const Artifact *TrafoChangeTracker::getArtifact(const QString &filePath, @@ -258,7 +257,7 @@ const ResolvedProduct *TrafoChangeTracker::getProduct(const QString &name) const bool TrafoChangeTracker::prepareScriptNeedsRerun() const { - for (const Property &property : qAsConst(m_transformer->propertiesRequestedInPrepareScript)) { + for (const Property &property : std::as_const(m_transformer->propertiesRequestedInPrepareScript)) { if (checkForPropertyChange(property, propertyMapByKind(property))) return true; } @@ -270,7 +269,7 @@ bool TrafoChangeTracker::prepareScriptNeedsRerun() const for (auto it = m_transformer->propertiesRequestedFromArtifactInPrepareScript.constBegin(); it != m_transformer->propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) { - for (const Property &property : qAsConst(it.value())) { + for (const Property &property : std::as_const(it.value())) { const Artifact * const artifact = getArtifact(it.key(), property.productName); if (!artifact) return true; @@ -298,15 +297,14 @@ bool TrafoChangeTracker::prepareScriptNeedsRerun() const bool TrafoChangeTracker::commandsNeedRerun() const { - for (const Property &property : qAsConst(m_transformer->propertiesRequestedInCommands)) { + for (const Property &property : std::as_const(m_transformer->propertiesRequestedInCommands)) { if (checkForPropertyChange(property, propertyMapByKind(property))) return true; } - QMap<QString, SourceArtifactConstPtr> artifactMap; for (auto it = m_transformer->propertiesRequestedFromArtifactInCommands.cbegin(); it != m_transformer->propertiesRequestedFromArtifactInCommands.cend(); ++it) { - for (const Property &property : qAsConst(it.value())) { + for (const Property &property : std::as_const(it.value())) { const Artifact * const artifact = getArtifact(it.key(), property.productName); if (!artifact) return true; @@ -333,7 +331,7 @@ bool TrafoChangeTracker::commandsNeedRerun() const return true; // TODO: Also track env access in JS commands and prepare scripts - for (const AbstractCommandPtr &c : qAsConst(m_transformer->commands.commands())) { + for (const AbstractCommandPtr &c : std::as_const(m_transformer->commands.commands())) { if (c->type() != AbstractCommand::ProcessCommandType) continue; const ProcessCommandPtr &processCmd = std::static_pointer_cast<ProcessCommand>(c); |