bool ItemReaderASTVisitor::visit(AST::UiScriptBinding *ast)
{
    QBS_CHECK(ast->qualifiedId);
    QBS_CHECK(!ast->qualifiedId->name.isEmpty());

    const QStringList bindingName = toStringList(ast->qualifiedId);

    if (bindingName.length() == 1 && bindingName.first() == QLatin1String("id")) {
        const auto * const expStmt = AST::cast<AST::ExpressionStatement *>(ast->statement);
        if (Q_UNLIKELY(!expStmt))
            throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
        const auto * const idExp = AST::cast<AST::IdentifierExpression *>(expStmt->expression);
        if (Q_UNLIKELY(!idExp || idExp->name.isEmpty()))
            throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
        m_item->m_id = idExp->name.toString();
        m_file->ensureIdScope(m_itemPool);
        m_file->idScope()->setProperty(m_item->id(), ItemValue::create(m_item));
        return false;
    }

    const JSSourceValuePtr value = JSSourceValue::create();
    handleBindingRhs(ast->statement, value);

    Item * const targetItem = targetItemForBinding(bindingName, value);
    checkDuplicateBinding(targetItem, bindingName, ast->qualifiedId->identifierToken);
    targetItem->setProperty(bindingName.last(), value);
    return false;
}
bool ItemReaderASTVisitor::handleBindingRhs(AST::Statement *statement,
                                            const JSSourceValuePtr &value)
{
    QBS_CHECK(statement);
    QBS_CHECK(value);

    if (AST::cast<AST::Block *>(statement))
        value->m_flags |= JSSourceValue::HasFunctionForm;

    value->setFile(m_file);
    value->setSourceCode(textRefOf(m_file->content(), statement));
    value->setLocation(statement->firstSourceLocation().startLine,
                       statement->firstSourceLocation().startColumn);

    bool usesBase, usesOuter, usesOriginal;
    IdentifierSearch idsearch;
    idsearch.add(QLatin1String("base"), &usesBase);
    idsearch.add(QLatin1String("outer"), &usesOuter);
    idsearch.add(QLatin1String("original"), &usesOriginal);
    idsearch.start(statement);
    if (usesBase)
        value->m_flags |= JSSourceValue::SourceUsesBase;
    if (usesOuter)
        value->m_flags |= JSSourceValue::SourceUsesOuter;
    if (usesOriginal)
        value->m_flags |= JSSourceValue::SourceUsesOriginal;
    return false;
}
Example #3
0
void InternalSetupProjectJob::execute()
{
    RulesEvaluationContextPtr evalContext(new RulesEvaluationContext(logger()));
    evalContext->setObserver(observer());

    switch (m_parameters.restoreBehavior()) {
    case SetupProjectParameters::ResolveOnly:
        resolveProjectFromScratch(evalContext->engine());
        resolveBuildDataFromScratch(evalContext);
        break;
    case SetupProjectParameters::RestoreOnly:
        m_newProject = restoreProject(evalContext).loadedProject;
        break;
    case SetupProjectParameters::RestoreAndTrackChanges: {
        const BuildGraphLoadResult loadResult = restoreProject(evalContext);
        m_newProject = loadResult.newlyResolvedProject;
        if (!m_newProject)
            m_newProject = loadResult.loadedProject;
        if (!m_newProject) {
            resolveProjectFromScratch(evalContext->engine());
            resolveBuildDataFromScratch(evalContext);
        } else {
            QBS_CHECK(m_newProject->buildData);
        }
        break;
    }
    }

    if (!m_parameters.dryRun())
        storeBuildGraph(m_newProject);

    // The evalutation context cannot be re-used for building, which runs in a different thread.
    m_newProject->buildData->evaluationContext.clear();
}
Example #4
0
void ModuleMerger::insertProperties(Item::PropertyMap *dst, Item *srcItem, PropertiesType type)
{
    QSet<const Item *> &seenInstances
            = type == ScalarProperties ? m_seenInstancesTopDown : m_seenInstancesBottomUp;
    Item *origSrcItem = srcItem;
    do {
        if (!seenInstances.contains(srcItem)) {
            seenInstances.insert(srcItem);
            for (Item::PropertyMap::const_iterator it = srcItem->properties().constBegin();
                 it != srcItem->properties().constEnd(); ++it) {
                const ValuePtr &srcVal = it.value();
                if (srcVal->type() != Value::JSSourceValueType)
                    continue;
                const PropertyDeclaration srcDecl = srcItem->propertyDeclaration(it.key());
                if (!srcDecl.isValid() || srcDecl.isScalar() != (type == ScalarProperties))
                    continue;
                ValuePtr &v = (*dst)[it.key()];
                if (v && type == ScalarProperties)
                    continue;
                ValuePtr clonedVal = srcVal->clone();
                m_decls[clonedVal] = srcDecl;
                clonedVal->setDefiningItem(origSrcItem);
                if (v) {
                    QBS_CHECK(!clonedVal->next());
                    clonedVal->setNext(v);
                }
                v = clonedVal;
            }
        }
        srcItem = srcItem->prototype();
    } while (srcItem && srcItem->type() == ItemType::ModuleInstance);
}
Example #5
0
void InternalSetupProjectJob::resolveProjectFromScratch(ScriptEngine *engine)
{
    Loader loader(engine, logger());
    loader.setSearchPaths(m_parameters.searchPaths());
    loader.setProgressObserver(observer());
    m_newProject = loader.loadProject(m_parameters);
    QBS_CHECK(m_newProject);
}
bool ItemReaderASTVisitor::visit(AST::UiObjectDefinition *ast)
{
    const QString typeName = ast->qualifiedTypeNameId->name.toString();
    Item *item = Item::create(m_itemPool);
    item->setFile(m_file);
    item->setTypeName(typeName);
    item->setLocation(toCodeLocation(ast->qualifiedTypeNameId->identifierToken));

    if (m_item)
        Item::addChild(m_item, item); // Add this item to the children of the parent item.
    else
        m_item = item; // This is the root item.

    const Item *inheritorItem = nullptr;

    // Inheritance resolving, part 1: Find out our actual type name (needed for setting
    // up children and alternatives).
    const QStringList fullTypeName = toStringList(ast->qualifiedTypeNameId);
    const QString baseTypeFileName = m_typeNameToFile.value(fullTypeName);
    if (!baseTypeFileName.isEmpty()) {
        inheritorItem = m_visitorState.readFile(baseTypeFileName, m_file->searchPaths(),
                                                m_itemPool);
        QBS_CHECK(inheritorItem->type() <= ItemType::LastActualItem);
        item->setType(inheritorItem->type());
    } else {
        item->setType(BuiltinDeclarations::instance().typeForName(typeName));
        if (item->type() == ItemType::Properties && item->parent()
                && item->parent()->type() == ItemType::SubProject) {
            item->setType(ItemType::PropertiesInSubProject);
        }
    }

    if (ast->initializer) {
        qSwap(m_item, item);
        ast->initializer->accept(this);
        qSwap(m_item, item);
    }

    ASTPropertiesItemHandler(item).handlePropertiesItems();

    // Inheritance resolving, part 2 (depends on alternatives having been set up).
    if (inheritorItem) {
        inheritItem(item, inheritorItem);
        if (inheritorItem->file()->idScope()) {
            // Make ids from the derived file visible in the base file.
            // ### Do we want to turn off this feature? It's QMLish but kind of strange.
            item->file()->ensureIdScope(m_itemPool);
            inheritorItem->file()->idScope()->setPrototype(item->file()->idScope());
        }
    } else {
        // Only the item at the top of the inheritance chain is a built-in item.
        // We cannot do this in "part 1", because then the visitor would complain about duplicate
        // bindings.
        item->setupForBuiltinType(m_logger);
    }

    return false;
}
Example #7
0
BuildOptions CommandLineFrontend::buildOptions(const Project &project) const
{
    BuildOptions options = m_parser.buildOptions(m_projects.front().profile());
    if (options.maxJobCount() <= 0) {
        const QString profileName = project.profile();
        QBS_CHECK(!profileName.isEmpty());
        options.setMaxJobCount(Preferences(m_settings, profileName).jobs());
    }
    return options;
}
Example #8
0
ProductData CommandLineFrontend::getTheOneRunnableProduct()
{
    QBS_CHECK(m_projects.size() == 1); // Has been checked earlier.

    if (m_parser.products().size() == 1) {
        const auto products = m_projects.front().projectData().allProducts();
        for (const ProductData &p : products) {
            if (p.name() == m_parser.products().constFirst())
                return p;
        }
        QBS_CHECK(false);
    }
    QBS_CHECK(m_parser.products().size() == 0);

    QList<ProductData> runnableProducts;
    const auto products = m_projects.front().projectData().allProducts();
    for (const ProductData &p : products) {
        if (p.isRunnable())
            runnableProducts.push_back(p);
    }

    if (runnableProducts.size() == 1)
        return runnableProducts.front();

    if (runnableProducts.empty()) {
        throw ErrorInfo(Tr::tr("Cannot execute command '%1': Project has no runnable product.")
                        .arg(m_parser.commandName()));
    }

    ErrorInfo error(Tr::tr("Ambiguous use of command '%1': No product given, but project "
                           "has more than one runnable product.").arg(m_parser.commandName()));
    error.append(Tr::tr("Use the '--products' option with one of the following products:"));
    for (const ProductData &p : qAsConst(runnableProducts)) {
        QString productRepr = QLatin1String("\t") + p.name();
        if (p.profile() != m_projects.front().profile()) {
            productRepr.append(QLatin1String(" [")).append(p.profile())
                    .append(QLatin1Char(']'));
        }
        error.append(productRepr);
    }
    throw error;
}
Example #9
0
void CommandLineFrontend::generate()
{
    QBS_CHECK(!!m_generator);
    const ErrorInfo error = m_generator->generate(m_projects,
                                                  m_parser.buildConfigurations(),
                                                  m_parser.installOptions(QString()),
                                                  m_parser.settingsDir(),
                                                  ConsoleLogger::instance(m_settings));
    if (error.hasError())
        throw error;
}
void ItemReaderASTVisitor::inheritItem(Item *dst, const Item *src)
{
    int insertPos = 0;
    for (int i = 0; i < src->m_children.count(); ++i) {
        Item *child = src->m_children.at(i);
        dst->m_children.insert(insertPos++, child);
        child->m_parent = dst;
    }

    for (auto it = src->properties().constBegin(); it != src->properties().constEnd(); ++it) {
        ValuePtr &v = dst->m_properties[it.key()];
        if (!v) {
            v = it.value();
            continue;
        }
        if (v->type() != it.value()->type())
            continue;
        switch (v->type()) {
        case Value::JSSourceValueType: {
            JSSourceValuePtr sv = v.staticCast<JSSourceValue>();
            QBS_CHECK(!sv->baseValue());
            const JSSourceValuePtr baseValue = it.value().staticCast<JSSourceValue>();
            sv->setBaseValue(baseValue);
            for (auto it = sv->m_alternatives.begin(); it != sv->m_alternatives.end(); ++it)
                it->value->setBaseValue(baseValue);
            break;
        }
        case Value::ItemValueType:
            inheritItem(v.staticCast<ItemValue>()->item(),
                        it.value().staticCast<const ItemValue>()->item());
            break;
        default:
            QBS_CHECK(!"unexpected value type");
        }
    }

    for (auto it = src->propertyDeclarations().constBegin();
         it != src->propertyDeclarations().constEnd(); ++it) {
        dst->setPropertyDeclaration(it.key(), it.value());
    }
}
Example #11
0
    void visitProduct(const ResolvedProductConstPtr &product)
    {
        QBS_CHECK(product->buildData);
        ArtifactVisitor::visitProduct(product);

        // For target artifacts, we have to update the on-disk timestamp, because
        // the executor will look at it.
        for (Artifact * const targetArtifact : product->targetArtifacts()) {
            if (FileInfo(targetArtifact->filePath()).exists())
                QFile(targetArtifact->filePath()).open(QIODevice::WriteOnly | QIODevice::Append);
        }
    }
Example #12
0
void ModuleMerger::appendPrototypeValueToNextChain(Item *moduleProto, const QString &propertyName,
        const ValuePtr &sv)
{
    const PropertyDeclaration pd = m_mergedModuleItem->propertyDeclaration(propertyName);
    if (pd.isScalar())
        return;
    ValuePtr protoValue = moduleProto->property(propertyName);
    QBS_CHECK(protoValue);
    if (!m_clonedModulePrototype) {
        m_clonedModulePrototype = moduleProto->clone();
        Item * const scope = Item::create(m_clonedModulePrototype->pool());
        scope->setFile(m_clonedModulePrototype->file());
        m_mergedModuleItem->scope()->copyProperty(QLatin1String("project"), scope);
        m_mergedModuleItem->scope()->copyProperty(QLatin1String("product"), scope);
        m_clonedModulePrototype->setScope(scope);
    }
    const ValuePtr clonedValue = protoValue->clone();
    clonedValue->setDefiningItem(m_clonedModulePrototype);
    lastInNextChain(sv)->setNext(clonedValue);
}
Example #13
0
void CommandList::load(PersistentPool &pool)
{
    m_commands.clear();
    int count = pool.load<int>();
    m_commands.reserve(count);
    while (--count >= 0) {
        const auto cmdType = pool.load<quint8>();
        AbstractCommandPtr cmd;
        switch (cmdType) {
        case AbstractCommand::JavaScriptCommandType:
            cmd = pool.load<JavaScriptCommandPtr>();
            break;
        case AbstractCommand::ProcessCommandType:
            cmd = pool.load<ProcessCommandPtr>();
            break;
        default:
            QBS_CHECK(false);
        }
        addCommand(cmd);
    }
}
Example #14
0
QList<AbstractCommandPtr> loadCommandList(PersistentPool &pool)
{
    QList<AbstractCommandPtr> commands;
    int count = pool.load<int>();
    commands.reserve(count);
    while (--count >= 0) {
        const auto cmdType = pool.load<quint8>();
        AbstractCommandPtr cmd;
        switch (cmdType) {
        case AbstractCommand::JavaScriptCommandType:
            cmd = pool.load<JavaScriptCommandPtr>();
            break;
        case AbstractCommand::ProcessCommandType:
            cmd = pool.load<ProcessCommandPtr>();
            break;
        default:
            QBS_CHECK(false);
        }
        commands += cmd;
    }
    return commands;
}
Example #15
0
void ModuleMerger::start()
{
    Item::Module m;
    m.item = m_rootItem;
    const Item::PropertyMap props = dfs(m, Item::PropertyMap());
    if (m_required)
        m_mergedModule.required = true;
    m_mergedModule.versionRange.narrowDown(m_versionRange);
    Item::PropertyMap mergedProps = m_mergedModule.item->properties();

    Item *moduleProto = m_mergedModule.item->prototype();
    while (moduleProto->prototype())
        moduleProto = moduleProto->prototype();

    for (auto it = props.constBegin(); it != props.constEnd(); ++it) {
        appendPrototypeValueToNextChain(moduleProto, it.key(), it.value());
        mergedProps[it.key()] = it.value();
    }
    m_mergedModule.item->setProperties(mergedProps);

    foreach (Item *moduleInstanceContainer, m_moduleInstanceContainers) {
        Item::Modules modules;
        foreach (const Item::Module &dep, moduleInstanceContainer->modules()) {
            const bool isTheModule = dep.name == m_mergedModule.name;
            Item::Module m = dep;
            if (isTheModule && m.item != m_mergedModule.item) {
                QBS_CHECK(m.item->type() == ItemType::ModuleInstance);
                replaceItemInValues(m.name, moduleInstanceContainer, m.item);
                replaceItemInScopes(m.item);
                m.item = m_mergedModule.item;
                if (m_required)
                    m.required = true;
                m.versionRange.narrowDown(m_versionRange);
            }
            modules << m;
        }
        moduleInstanceContainer->setModules(modules);
    }
Example #16
0
VisualStudioVersionInfo::VisualStudioVersionInfo(const Version &version)
    : m_version(version)
{
    QBS_CHECK(version.minorVersion() == 0 || version == Version(7, 1));
}
Example #17
0
void JavaScriptCommand::setupForJavaScript(QScriptValue targetObject)
{
    QBS_CHECK(targetObject.isObject());
    QScriptValue ctor = targetObject.engine()->newFunction(js_JavaScriptCommand, 0);
    targetObject.setProperty(StringConstants::javaScriptCommandType(), ctor);
}
Example #18
0
void JavaScriptCommand::setupForJavaScript(QScriptValue targetObject)
{
    QBS_CHECK(targetObject.isObject());
    QScriptValue ctor = targetObject.engine()->newFunction(js_JavaScriptCommand, 0);
    targetObject.setProperty(QLatin1String("JavaScriptCommand"), ctor);
}
Example #19
0
void RuleNode::apply(const Logger &logger,
                     const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
                     const std::unordered_map<QString, const ResolvedProject *> &projectsByName,
                     ApplicationResult *result)
{
    ArtifactSet allCompatibleInputs = currentInputArtifacts();
    const ArtifactSet explicitlyDependsOn
            = RulesApplicator::collectExplicitlyDependsOn(m_rule.get(), product.get());
    const ArtifactSet auxiliaryInputs
            = RulesApplicator::collectAuxiliaryInputs(m_rule.get(), product.get());
    const ArtifactSet addedInputs = allCompatibleInputs - m_oldInputArtifacts;
    const ArtifactSet removedInputs = m_oldInputArtifacts - allCompatibleInputs;
    const ArtifactSet changedInputs = changedInputArtifacts(allCompatibleInputs,
                                                            explicitlyDependsOn,
                                                            auxiliaryInputs);
    bool upToDate = changedInputs.empty() && addedInputs.empty() && removedInputs.empty();

    qCDebug(lcBuildGraph).noquote().nospace()
            << "consider " << (m_rule->isDynamic() ? "dynamic " : "")
            << (m_rule->multiplex ? "multiplex " : "")
            << "rule node " << m_rule->toString()
            << "\n\tchanged: " << changedInputs.toString()
            << "\n\tcompatible: " << allCompatibleInputs.toString()
            << "\n\tadded: " << addedInputs.toString()
            << "\n\tremoved: " << removedInputs.toString();

    ArtifactSet inputs = changedInputs;
    if (m_rule->multiplex)
        inputs = allCompatibleInputs;
    else
        inputs += addedInputs;

    for (Artifact * const input : allCompatibleInputs) {
        for (const Artifact * const output : input->parentArtifacts()) {
            if (output->transformer->rule != m_rule)
                continue;
            if (prepareScriptNeedsRerun(output->transformer.get(),
                                        output->transformer->product().get(),
                                        productsByName, projectsByName)) {
                upToDate = false;
                inputs += input;
            }
            break;
        }
        if (m_rule->multiplex)
            break;
    }

    // Handle rules without inputs: We want to run such a rule if and only if it has not run yet
    // or its transformer is not up to date regarding the prepare script.
    if (upToDate && (!m_rule->declaresInputs() || !m_rule->requiresInputs) && inputs.empty()) {
        bool hasOutputs = false;
        for (const Artifact * const output : filterByType<Artifact>(parents)) {
            if (output->transformer->rule != m_rule)
                continue;
            hasOutputs = true;
            if (prepareScriptNeedsRerun(output->transformer.get(),
                                        output->transformer->product().get(),
                                        productsByName, projectsByName)) {
                upToDate = false;
                break;
            }
            if (m_rule->multiplex)
                break;
        }
        if (!hasOutputs)
            upToDate = false;
    }

    if (upToDate) {
        qCDebug(lcExec) << "rule is up to date. Skipping.";
        return;
    }

    const bool mustApplyRule = !inputs.empty() || !m_rule->declaresInputs()
            || !m_rule->requiresInputs;

    // For a non-multiplex rule, the removal of an input always implies that the
    // corresponding outputs disappear.
    // For a multiplex rule, the outputs disappear only if *all* inputs are gone *and*
    // the rule requires inputs. This is exactly the opposite condition of whether to
    // re-apply the rule.
    const bool removedInputForcesOutputRemoval = !m_rule->multiplex || !mustApplyRule;
    ArtifactSet outputArtifactsToRemove;
    std::vector<std::pair<Artifact *, Artifact *>> connectionsToBreak;
    for (Artifact * const artifact : removedInputs) {
        if (!artifact) // dummy artifact
            continue;
        for (Artifact *parent : filterByType<Artifact>(artifact->parents)) {
            if (parent->transformer->rule != m_rule) {
                // parent was not created by our rule.
                continue;
            }

            // parent must always have a transformer, because it's generated.
            QBS_CHECK(parent->transformer);

            // artifact is a former input of m_rule and parent was created by m_rule
            // the inputs of the transformer must contain artifact
            QBS_CHECK(parent->transformer->inputs.contains(artifact));

            if (removedInputForcesOutputRemoval)
                outputArtifactsToRemove += parent;
            else
                connectionsToBreak.push_back(std::make_pair(parent, artifact));
        }
        disconnect(this, artifact);
    }
    for (const auto &connection : connectionsToBreak)
        disconnect(connection.first, connection.second);
    if (!outputArtifactsToRemove.empty()) {
        RulesApplicator::handleRemovedRuleOutputs(inputs, outputArtifactsToRemove,
                                                  result->removedArtifacts, logger);
    }

    if (mustApplyRule) {
        RulesApplicator applicator(product.lock(), productsByName, projectsByName, logger);
        applicator.applyRule(this, inputs, explicitlyDependsOn);
        result->createdArtifacts = applicator.createdArtifacts();
        result->invalidatedArtifacts = applicator.invalidatedArtifacts();
        m_lastApplicationTime = FileTime::currentTime();
        if (applicator.ruleUsesIo())
            m_needsToConsiderChangedInputs = true;
    } else {
        qCDebug(lcExec).noquote() << "prepare script does not need to run";
    }
    m_oldInputArtifacts = allCompatibleInputs;
    m_oldExplicitlyDependsOn = explicitlyDependsOn;
    m_oldAuxiliaryInputs = auxiliaryInputs;
    product->topLevelProject()->buildData->setDirty();
}