diff options
Diffstat (limited to 'src/lib/corelib/buildgraph/transformer.cpp')
-rw-r--r-- | src/lib/corelib/buildgraph/transformer.cpp | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp new file mode 100644 index 000000000..5c095f9db --- /dev/null +++ b/src/lib/corelib/buildgraph/transformer.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Build Suite. +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ +#include "transformer.h" + +#include "artifact.h" +#include "command.h" +#include "rulesevaluationcontext.h" +#include <jsextensions/moduleproperties.h> +#include <language/language.h> +#include <language/scriptengine.h> +#include <logging/translator.h> +#include <tools/error.h> +#include <tools/persistence.h> +#include <tools/qbsassert.h> + +namespace qbs { +namespace Internal { + +Transformer::Transformer() +{ +} + +Transformer::~Transformer() +{ + qDeleteAll(commands); +} + +QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artifact *artifact, const QString &defaultModuleName) +{ + QScriptValue artifactConfig = scriptEngine->newObject(); + ModuleProperties::init(artifactConfig, artifact); + artifactConfig.setProperty(QLatin1String("fileName"), artifact->filePath()); + const QStringList fileTags = artifact->fileTags.toStringList(); + artifactConfig.setProperty(QLatin1String("fileTags"), scriptEngine->toScriptValue(fileTags)); + if (!defaultModuleName.isEmpty()) + artifactConfig.setProperty(QLatin1String("moduleName"), defaultModuleName); + return artifactConfig; +} + +QScriptValue Transformer::translateInOutputs(QScriptEngine *scriptEngine, const ArtifactList &artifacts, const QString &defaultModuleName) +{ + typedef QMap<QString, QList<Artifact*> > TagArtifactsMap; + TagArtifactsMap tagArtifactsMap; + foreach (Artifact *artifact, artifacts) + foreach (const FileTag &fileTag, artifact->fileTags) + tagArtifactsMap[fileTag.toString()].append(artifact); + + QScriptValue jsTagFiles = scriptEngine->newObject(); + for (TagArtifactsMap::const_iterator tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) { + const QList<Artifact*> &artifactList = tag.value(); + QScriptValue jsFileConfig = scriptEngine->newArray(artifactList.count()); + int i=0; + foreach (Artifact *artifact, artifactList) { + jsFileConfig.setProperty(i++, translateFileConfig(scriptEngine, artifact, defaultModuleName)); + } + jsTagFiles.setProperty(tag.key(), jsFileConfig); + } + + return jsTagFiles; +} + +ResolvedProductPtr Transformer::product() const +{ + if (outputs.isEmpty()) + return ResolvedProductPtr(); + return (*outputs.begin())->product; +} + +void Transformer::setupInputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue) +{ + const QString &defaultModuleName = rule->module->name; + QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName); + targetScriptValue.setProperty("inputs", scriptValue); + if (inputs.count() == 1) { + Artifact *input = *inputs.begin(); + const FileTags &fileTags = input->fileTags; + QBS_ASSERT(!fileTags.isEmpty(), return); + QScriptValue inputsForFileTag = scriptValue.property(fileTags.begin()->toString()); + QScriptValue inputScriptValue = inputsForFileTag.property(0); + targetScriptValue.setProperty("input", inputScriptValue); + } else { + targetScriptValue.setProperty(QLatin1String("input"), scriptEngine->undefinedValue()); + } +} + +void Transformer::setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue) +{ + const QString &defaultModuleName = rule->module->name; + QScriptValue scriptValue = translateInOutputs(scriptEngine, outputs, defaultModuleName); + targetScriptValue.setProperty("outputs", scriptValue); + if (outputs.count() == 1) { + Artifact *output = *outputs.begin(); + const FileTags &fileTags = output->fileTags; + QBS_ASSERT(!fileTags.isEmpty(), return); + QScriptValue outputsForFileTag = scriptValue.property(fileTags.begin()->toString()); + QScriptValue outputScriptValue = outputsForFileTag.property(0); + targetScriptValue.setProperty("output", outputScriptValue); + } else { + targetScriptValue.setProperty(QLatin1String("output"), scriptEngine->undefinedValue()); + } +} + +static AbstractCommand *createCommandFromScriptValue(const QScriptValue &scriptValue, + const CodeLocation &codeLocation) +{ + if (scriptValue.isUndefined() || !scriptValue.isValid()) + return 0; + AbstractCommand *cmdBase = 0; + QString className = scriptValue.property("className").toString(); + if (className == "Command") + cmdBase = new ProcessCommand; + else if (className == "JavaScriptCommand") + cmdBase = new JavaScriptCommand; + if (cmdBase) + cmdBase->fillFromScriptValue(&scriptValue, codeLocation); + return cmdBase; +} + +void Transformer::createCommands(const ScriptFunctionConstPtr &script, + const RulesEvaluationContextPtr &evalContext, const QScriptValueList &args) +{ + ScriptEngine * const engine = evalContext->engine(); + if (!script->scriptFunction.isValid() || script->scriptFunction.engine() != engine) { + script->scriptFunction = engine->evaluate(script->sourceCode); + if (Q_UNLIKELY(!script->scriptFunction.isFunction())) + throw ErrorInfo(Tr::tr("Invalid prepare script."), script->location); + } + + QScriptValue scriptValue = script->scriptFunction.call(QScriptValue(), args); + propertiesRequestedInPrepareScript = engine->propertiesRequestedInScript(); + propertiesRequestedFromArtifactInPrepareScript = engine->propertiesRequestedFromArtifact(); + engine->clearRequestedProperties(); + if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue))) + throw ErrorInfo("evaluating prepare script: " + engine->uncaughtException().toString(), + CodeLocation(script->location.fileName(), + script->location.line() + engine->uncaughtExceptionLineNumber() - 1)); + + qDeleteAll(commands); + commands.clear(); + if (scriptValue.isArray()) { + const int count = scriptValue.property("length").toInt32(); + for (qint32 i = 0; i < count; ++i) { + QScriptValue item = scriptValue.property(i); + if (item.isValid() && !item.isUndefined()) { + AbstractCommand *cmd = createCommandFromScriptValue(item, script->location); + if (cmd) + commands += cmd; + } + } + } else { + AbstractCommand *cmd = createCommandFromScriptValue(scriptValue, script->location); + if (cmd) + commands += cmd; + } +} + +static void restorePropertyList(PersistentPool &pool, PropertyList &list) +{ + int count; + pool.stream() >> count; + list.reserve(count); + while (--count >= 0) { + Property p; + p.moduleName = pool.idLoadString(); + p.propertyName = pool.idLoadString(); + int k; + pool.stream() >> p.value >> k; + p.kind = static_cast<Property::Kind>(k); + list += p; + } +} + +void Transformer::load(PersistentPool &pool) +{ + rule = pool.idLoadS<Rule>(); + pool.loadContainer(inputs); + pool.loadContainer(outputs); + restorePropertyList(pool, propertiesRequestedInPrepareScript); + restorePropertyList(pool, propertiesRequestedInCommands); + int count; + pool.stream() >> count; + propertiesRequestedFromArtifactInPrepareScript.reserve(count); + while (--count >= 0) { + const QString artifactName = pool.idLoadString(); + int listCount; + pool.stream() >> listCount; + PropertyList list; + list.reserve(listCount); + while (--listCount >= 0) { + Property p; + p.moduleName = pool.idLoadString(); + p.propertyName = pool.idLoadString(); + pool.stream() >> p.value; + p.kind = Property::PropertyInModule; + list += p; + } + propertiesRequestedFromArtifactInPrepareScript.insert(artifactName, list); + } + int cmdType; + pool.stream() >> count; + commands.reserve(count); + while (--count >= 0) { + pool.stream() >> cmdType; + AbstractCommand *cmd = AbstractCommand::createByType(static_cast<AbstractCommand::CommandType>(cmdType)); + cmd->load(pool.stream()); + commands += cmd; + } +} + +static void storePropertyList(PersistentPool &pool, const PropertyList &list) +{ + pool.stream() << list.count(); + foreach (const Property &p, list) { + pool.storeString(p.moduleName); + pool.storeString(p.propertyName); + pool.stream() << p.value << static_cast<int>(p.kind); + } +} + +void Transformer::store(PersistentPool &pool) const +{ + pool.store(rule); + pool.storeContainer(inputs); + pool.storeContainer(outputs); + storePropertyList(pool, propertiesRequestedInPrepareScript); + storePropertyList(pool, propertiesRequestedInCommands); + pool.stream() << propertiesRequestedFromArtifactInPrepareScript.count(); + for (QHash<QString, PropertyList>::ConstIterator it = propertiesRequestedFromArtifactInPrepareScript.constBegin(); + it != propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) { + pool.storeString(it.key()); + const PropertyList &properties = it.value(); + pool.stream() << properties.count(); + foreach (const Property &p, properties) { + pool.storeString(p.moduleName); + pool.storeString(p.propertyName); + pool.stream() << p.value; // kind is always PropertyInModule + } + } + pool.stream() << commands.count(); + foreach (AbstractCommand *cmd, commands) { + pool.stream() << int(cmd->type()); + cmd->store(pool.stream()); + } +} + +} // namespace Internal +} // namespace qbs |