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; }
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(); }
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); }
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; }
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; }
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; }
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()); } }
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); } }
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); }
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); } }
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; }
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); }
VisualStudioVersionInfo::VisualStudioVersionInfo(const Version &version) : m_version(version) { QBS_CHECK(version.minorVersion() == 0 || version == Version(7, 1)); }
void JavaScriptCommand::setupForJavaScript(QScriptValue targetObject) { QBS_CHECK(targetObject.isObject()); QScriptValue ctor = targetObject.engine()->newFunction(js_JavaScriptCommand, 0); targetObject.setProperty(StringConstants::javaScriptCommandType(), ctor); }
void JavaScriptCommand::setupForJavaScript(QScriptValue targetObject) { QBS_CHECK(targetObject.isObject()); QScriptValue ctor = targetObject.engine()->newFunction(js_JavaScriptCommand, 0); targetObject.setProperty(QLatin1String("JavaScriptCommand"), ctor); }
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(); }