diff options
Diffstat (limited to 'src')
31 files changed, 253 insertions, 175 deletions
diff --git a/src/app/app.pri b/src/app/app.pri index 8dc6dc95b..3a86e5a0f 100644 --- a/src/app/app.pri +++ b/src/app/app.pri @@ -1,6 +1,12 @@ QT = core TEMPLATE = app -DESTDIR = ../../../bin +!isEmpty(QBS_APPS_DESTDIR):DESTDIR = $${QBS_APPS_DESTDIR} +else:DESTDIR = ../../../bin + +!isEmpty(QBS_APPS_RPATH_DIR) { + linux-*:QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$${QBS_APPS_RPATH_DIR}\' + macx:QMAKE_LFLAGS += -Wl,-rpath,$${QBS_APPS_RPATH_DIR} +} CONFIG += console CONFIG -= app_bundle @@ -8,5 +14,6 @@ CONFIG -= app_bundle include($${PWD}/../lib/corelib/use_corelib.pri) include($${PWD}/shared/logging/logging.pri) -target.path = $${QBS_INSTALL_PREFIX}/bin +!isEmpty(QBS_APPS_INSTALL_DIR):target.path = $${QBS_APPS_INSTALL_DIR} +else:target.path = $${QBS_INSTALL_PREFIX}/bin INSTALLS += target diff --git a/src/app/apps.qbs b/src/app/apps.qbs index 56d8d3289..f3a87f81e 100644 --- a/src/app/apps.qbs +++ b/src/app/apps.qbs @@ -1,6 +1,7 @@ import qbs Project { + property string appInstallDir: "bin" references: [ "config/config.qbs", "config-ui/config-ui.qbs", diff --git a/src/app/apptemplate.qbs b/src/app/apptemplate.qbs index 2d87a0233..7ebe33b2d 100644 --- a/src/app/apptemplate.qbs +++ b/src/app/apptemplate.qbs @@ -13,7 +13,7 @@ Product { Group { fileTagsFilter: product.type qbs.install: true - qbs.installDir: "bin" + qbs.installDir: project.appInstallDir } Group { name: "logging" diff --git a/src/app/qbs-setup-qt/setupqt.cpp b/src/app/qbs-setup-qt/setupqt.cpp index b36272713..c7b1ea16d 100644 --- a/src/app/qbs-setup-qt/setupqt.cpp +++ b/src/app/qbs-setup-qt/setupqt.cpp @@ -180,6 +180,7 @@ QtEnvironment SetupQt::fetchEnvironment(const QString &qmakePath) qtEnvironment.binaryPath = pathQueryValue(queryOutput, "QT_INSTALL_BINS"); qtEnvironment.documentationPath = pathQueryValue(queryOutput, "QT_INSTALL_DOCS"); qtEnvironment.pluginPath = pathQueryValue(queryOutput, "QT_INSTALL_PLUGINS"); + qtEnvironment.qmlPath = pathQueryValue(queryOutput, "QT_INSTALL_QML"); qtEnvironment.qmlImportPath = pathQueryValue(queryOutput, "QT_INSTALL_IMPORTS"); qtEnvironment.qtVersion = QString::fromLocal8Bit(queryOutput.value("QT_VERSION")); diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp index 63793dc63..4c35c4b65 100644 --- a/src/app/qbs-setup-toolchains/probe.cpp +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -250,9 +250,18 @@ void probe(Settings *settings) void createProfile(const QString &profileName, const QString &toolchainType, const QString &compilerFilePath, Settings *settings) { + QFileInfo compiler(compilerFilePath); + if (compilerFilePath == compiler.fileName() && !compiler.exists()) + compiler = QFileInfo(findExecutable(compilerFilePath)); + + if (!compiler.exists()) { + throw qbs::ErrorInfo(Tr::tr("Compiler '%1' not found") + .arg(compilerFilePath)); + } + QStringList toolchainTypes; if (toolchainType.isEmpty()) - toolchainTypes = toolchainTypeFromCompilerName(QFileInfo(compilerFilePath).fileName()); + toolchainTypes = toolchainTypeFromCompilerName(compiler.fileName()); else toolchainTypes = completeToolchainList(toolchainType); @@ -262,7 +271,7 @@ void createProfile(const QString &profileName, const QString &toolchainType, } if (toolchainTypes.contains(QLatin1String("gcc"))) - createGccProfile(compilerFilePath, settings, toolchainTypes, profileName); + createGccProfile(compiler.absoluteFilePath(), settings, toolchainTypes, profileName); else throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type.")); } diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp index bea570aa3..dce270ef1 100644 --- a/src/lib/corelib/api/project.cpp +++ b/src/lib/corelib/api/project.cpp @@ -585,6 +585,14 @@ void ProjectPrivate::prepareChangeToProject() retrieveProjectData(m_projectData, internalProject); } +static bool productIsRunnable(const ResolvedProductConstPtr &product) +{ + return product->fileTags.contains("application") + || (product->fileTags.contains("applicationbundle") + && product->moduleProperties->qbsPropertyValue(QLatin1String("targetOS")) + .toStringList().contains(QLatin1String("darwin"))); +} + void ProjectPrivate::retrieveProjectData(ProjectData &projectData, const ResolvedProjectConstPtr &internalProject) { @@ -596,6 +604,7 @@ void ProjectPrivate::retrieveProjectData(ProjectData &projectData, product.d->name = resolvedProduct->name; product.d->location = resolvedProduct->location; product.d->isEnabled = resolvedProduct->enabled; + product.d->isRunnable = productIsRunnable(resolvedProduct); foreach (const GroupPtr &resolvedGroup, resolvedProduct->groups) product.d->groups << createGroupDataFromGroup(resolvedGroup); if (resolvedProduct->enabled) { diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp index a5cc6093e..31ba1ef33 100644 --- a/src/lib/corelib/api/projectdata.cpp +++ b/src/lib/corelib/api/projectdata.cpp @@ -414,6 +414,12 @@ bool ProductData::isEnabled() const return d->isEnabled; } +bool ProductData::isRunnable() const +{ + QBS_ASSERT(isValid(), return false); + return d->isRunnable; +} + bool operator==(const ProductData &lhs, const ProductData &rhs) { return lhs.name() == rhs.name() diff --git a/src/lib/corelib/api/projectdata.h b/src/lib/corelib/api/projectdata.h index ba0016d0c..1d7625b82 100644 --- a/src/lib/corelib/api/projectdata.h +++ b/src/lib/corelib/api/projectdata.h @@ -176,6 +176,7 @@ public: QList<TargetArtifact> targetArtifacts() const; QList<GroupData> groups() const; bool isEnabled() const; + bool isRunnable() const; private: QExplicitlySharedDataPointer<Internal::ProductDataPrivate> d; diff --git a/src/lib/corelib/api/projectdata_p.h b/src/lib/corelib/api/projectdata_p.h index 2eda4d932..b0006e89a 100644 --- a/src/lib/corelib/api/projectdata_p.h +++ b/src/lib/corelib/api/projectdata_p.h @@ -84,6 +84,7 @@ public: QList<GroupData> groups; QList<TargetArtifact> targetArtifacts; bool isEnabled; + bool isRunnable; bool isValid; }; diff --git a/src/lib/corelib/api/runenvironment.cpp b/src/lib/corelib/api/runenvironment.cpp index b0be216f8..386a1769d 100644 --- a/src/lib/corelib/api/runenvironment.cpp +++ b/src/lib/corelib/api/runenvironment.cpp @@ -129,6 +129,21 @@ int RunEnvironment::runShell() return system(command.toLocal8Bit().constData()); } +static QString findExecutable(const QStringList &fileNames) +{ + const QStringList path = QString::fromLocal8Bit(qgetenv("PATH")) + .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + + foreach (const QString &fileName, fileNames) { + foreach (const QString &ppath, path) { + const QString fullPath = ppath + QLatin1Char('/') + fileName; + if (QFileInfo(fullPath).exists()) + return QDir::cleanPath(fullPath); + } + } + return QString(); +} + int RunEnvironment::runTarget(const QString &targetBin, const QStringList &arguments) { const QStringList targetOS = PropertyFinder().propertyValue( @@ -154,6 +169,15 @@ int RunEnvironment::runTarget(const QString &targetBin, const QStringList &argum } } + if (completeSuffix == QLatin1String("js")) { + // The Node.js binary is called nodejs on Debian/Ubuntu-family operating systems due to a + // conflict with another package containing a binary named node + targetExecutable = findExecutable(QStringList() + << QLatin1String("nodejs") + << QLatin1String("node")); + targetArguments.prepend(targetBin); + } + // Only check if the target is executable if we're not running it through another // known application such as msiexec or wine, as we can't check in this case anyways if (targetBin == targetExecutable && !QFileInfo(targetExecutable).isExecutable()) { @@ -166,6 +190,7 @@ int RunEnvironment::runTarget(const QString &targetBin, const QStringList &argum d->logger.qbsInfo() << Tr::tr("Starting target '%1'.").arg(QDir::toNativeSeparators(targetBin)); QProcess process; + process.setWorkingDirectory(QFileInfo(targetBin).absolutePath()); process.setProcessEnvironment(d->resolvedProduct->runEnvironment); process.setProcessChannelMode(QProcess::ForwardedChannels); process.start(targetExecutable, targetArguments); diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp index 024decbc7..283958220 100644 --- a/src/lib/corelib/buildgraph/executor.cpp +++ b/src/lib/corelib/buildgraph/executor.cpp @@ -791,8 +791,10 @@ void Executor::runTransformer(const TransformerPtr &transformer) for (; it != transformer->outputs.end(); ++it) { Artifact *output = *it; QDir outDir = QFileInfo(output->filePath()).absoluteDir(); - if (!outDir.exists()) - outDir.mkpath(QLatin1String(".")); + if (!outDir.exists() && !outDir.mkpath(QLatin1String("."))) { + throw ErrorInfo(tr("Failed to create directory '%1'.") + .arg(QDir::toNativeSeparators(outDir.absolutePath()))); + } } } diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp index 7cbcbf997..d80c10432 100644 --- a/src/lib/corelib/buildgraph/rulesapplicator.cpp +++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp @@ -307,7 +307,7 @@ Artifact *RulesApplicator::createOutputArtifactFromRuleArtifact( QScriptValue scriptValue = engine()->evaluate(ruleArtifact->fileName); if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue))) throw ErrorInfo(Tr::tr("Error in Rule.Artifact fileName: ") + scriptValue.toString()); - QString outputPath = scriptValue.toString(); + QString outputPath = FileInfo::resolvePath(m_product->buildDirectory(), scriptValue.toString()); return createOutputArtifact(outputPath, ruleArtifact->fileTags, ruleArtifact->alwaysUpdated, inputArtifacts); } @@ -428,7 +428,8 @@ Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValu const ArtifactSet &inputArtifacts) { QBS_CHECK(obj.isObject()); - const QString filePath = obj.property(QLatin1String("filePath")).toVariant().toString(); + const QString filePath = FileInfo::resolvePath(m_product->buildDirectory(), + obj.property(QLatin1String("filePath")).toVariant().toString()); const FileTags fileTags = FileTags::fromStringList( obj.property(QLatin1String("fileTags")).toVariant().toStringList()); const QVariant alwaysUpdatedVar = obj.property(QLatin1String("alwaysUpdated")).toVariant(); diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp index 492feeb0c..c82962ed7 100644 --- a/src/lib/corelib/buildgraph/transformer.cpp +++ b/src/lib/corelib/buildgraph/transformer.cpp @@ -54,12 +54,7 @@ QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artif { QScriptValue obj = scriptEngine->newObject(); ModuleProperties::init(obj, artifact); - - // ### undeprecate "fileName" and turn into a real file name in qbs 1.3 - ScriptEngine *qbsScriptEngine = static_cast<ScriptEngine *>(scriptEngine); - qbsScriptEngine->setDeprecatedProperty(obj, QLatin1String("fileName"), - QLatin1String("filePath"), artifact->filePath()); - + obj.setProperty(QLatin1String("fileName"), artifact->fileName()); obj.setProperty(QLatin1String("filePath"), artifact->filePath()); const QStringList fileTags = artifact->fileTags.toStringList(); obj.setProperty(QLatin1String("fileTags"), scriptEngine->toScriptValue(fileTags)); diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp index 02046e358..3ff60944b 100644 --- a/src/lib/corelib/language/builtindeclarations.cpp +++ b/src/lib/corelib/language/builtindeclarations.cpp @@ -109,6 +109,11 @@ static PropertyDeclaration nameProperty() return PropertyDeclaration(QLatin1String("name"), PropertyDeclaration::String); } +static PropertyDeclaration buildDirProperty() +{ + return PropertyDeclaration(QLatin1String("buildDirectory"), PropertyDeclaration::Path); +} + static PropertyDeclaration prepareScriptProperty() { PropertyDeclaration decl(QLatin1String("prepare"), PropertyDeclaration::Verbatim); @@ -155,10 +160,6 @@ void BuiltinDeclarations::addExportItem() void BuiltinDeclarations::addFileTaggerItem() { ItemDeclaration item(QLatin1String("FileTagger")); - - // TODO: Remove in 1.3 - item << PropertyDeclaration(QLatin1String("pattern"), PropertyDeclaration::StringList); - item << PropertyDeclaration(QLatin1String("patterns"), PropertyDeclaration::StringList); item << PropertyDeclaration(QLatin1String("fileTags"), PropertyDeclaration::Variant); insert(item); @@ -245,9 +246,9 @@ void BuiltinDeclarations::addProductItem() item << nameProperty(); decl = PropertyDeclaration(QLatin1String("targetName"), PropertyDeclaration::String); decl.setInitialValueSource(QLatin1String("name")); + item << buildDirProperty(); item << decl; decl = PropertyDeclaration(QLatin1String("destinationDirectory"), PropertyDeclaration::String); - decl.setInitialValueSource(QLatin1String("'.'")); item << decl; item << PropertyDeclaration(QLatin1String("consoleApplication"), PropertyDeclaration::Boolean); @@ -272,6 +273,8 @@ void BuiltinDeclarations::addProjectItem() << QLatin1String("Rule")); item << nameProperty(); item << conditionProperty(); + item << buildDirProperty(); + item << PropertyDeclaration(QLatin1String("sourceDirectory"), PropertyDeclaration::Path); item << PropertyDeclaration(QLatin1String("references"), PropertyDeclaration::Variant, PropertyDeclaration::PropertyNotAvailableInConfig); item << PropertyDeclaration(QLatin1String("qbsSearchPaths"), diff --git a/src/lib/corelib/language/item.cpp b/src/lib/corelib/language/item.cpp index 7b42df5e6..8b1dcb72a 100644 --- a/src/lib/corelib/language/item.cpp +++ b/src/lib/corelib/language/item.cpp @@ -122,6 +122,14 @@ JSSourceValuePtr Item::sourceProperty(const QString &name) const return v.staticCast<JSSourceValue>(); } +VariantValuePtr Item::variantProperty(const QString &name) const +{ + ValuePtr v = property(name); + if (!v || v->type() != Value::VariantValueType) + return VariantValuePtr(); + return v.staticCast<VariantValue>(); +} + const PropertyDeclaration Item::propertyDeclaration(const QString &name) const { const PropertyDeclaration decl = m_propertyDeclarations.value(name); diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h index 866927a0e..8859957b0 100644 --- a/src/lib/corelib/language/item.h +++ b/src/lib/corelib/language/item.h @@ -102,6 +102,7 @@ public: ValuePtr property(const QString &name) const; ItemValuePtr itemProperty(const QString &name, bool create = false); JSSourceValuePtr sourceProperty(const QString &name) const; + VariantValuePtr variantProperty(const QString &name) const; void setPropertyObserver(ItemObserver *observer) const; void setProperty(const QString &name, const ValuePtr &value); void setPropertyDeclaration(const QString &name, const PropertyDeclaration &declaration); diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp index eb5ad9652..4974a3e68 100644 --- a/src/lib/corelib/language/language.cpp +++ b/src/lib/corelib/language/language.cpp @@ -585,11 +585,6 @@ static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType e const QScriptValue getEnvValue = engine->newFunction(js_getEnv, 1); const QScriptValue putEnvValue = engine->newFunction(js_putEnv, 1); - - // TODO: Remove in 1.3 - scope.setProperty(QLatin1String("getenv"), getEnvValue); - scope.setProperty(QLatin1String("putenv"), putEnvValue); - scope.setProperty(QLatin1String("getEnv"), getEnvValue); scope.setProperty(QLatin1String("putEnv"), putEnvValue); @@ -817,6 +812,14 @@ QStringList ResolvedProduct::generatedFiles(const QString &baseFile, const FileT return QStringList(); } +QString ResolvedProduct::buildDirectory() const +{ + const QString result = productProperties.value(QLatin1String("buildDirectory")).toString(); + QBS_CHECK(!result.isEmpty()); + return result; +} + + ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(0) { } diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h index 780af8f47..a76c8ad26 100644 --- a/src/lib/corelib/language/language.h +++ b/src/lib/corelib/language/language.h @@ -397,6 +397,7 @@ public: TopLevelProject *topLevelProject() const; QStringList generatedFiles(const QString &baseFile, const FileTags &tags) const; + QString buildDirectory() const; private: ResolvedProduct(); diff --git a/src/lib/corelib/language/loader.cpp b/src/lib/corelib/language/loader.cpp index 62c1aadde..622cfa073 100644 --- a/src/lib/corelib/language/loader.cpp +++ b/src/lib/corelib/language/loader.cpp @@ -120,11 +120,7 @@ TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters ¶meters) cancelationTimer.start(1000); } - ModuleLoaderResult loadResult - = m_moduleLoader->load(parameters.projectFilePath(), - parameters.overriddenValuesTree(), - parameters.buildConfigurationTree(), - true); + ModuleLoaderResult loadResult = m_moduleLoader->load(parameters); const TopLevelProjectPtr project = m_projectResolver->resolve(loadResult, parameters); // E.g. if the top-level project is disabled. diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index abe09814c..3dc7df283 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -36,6 +36,7 @@ #include "item.h" #include "itemreader.h" #include "scriptengine.h" +#include "value.h" #include <language/language.h> #include <language/scriptengine.h> #include <logging/logger.h> @@ -101,15 +102,12 @@ void ModuleLoader::setSearchPaths(const QStringList &searchPaths) } } -ModuleLoaderResult ModuleLoader::load(const QString &filePath, - const QVariantMap &overriddenProperties, const QVariantMap &buildConfigProperties, - bool wrapWithProjectItem) +ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters ¶meters) { if (m_logger.traceEnabled()) - m_logger.qbsTrace() << "[MODLDR] load" << filePath; + m_logger.qbsTrace() << "[MODLDR] load" << parameters.projectFilePath(); + m_parameters = parameters; m_reader->clearItemCache(); - m_overriddenProperties = overriddenProperties; - m_buildConfigProperties = buildConfigProperties; m_validItemPropertyNamesPerItem.clear(); m_disabledItems.clear(); @@ -117,14 +115,21 @@ ModuleLoaderResult ModuleLoader::load(const QString &filePath, m_pool = result.itemPool.data(); m_reader->setPool(m_pool); - Item *root = m_reader->readFile(filePath); + Item *root = m_reader->readFile(parameters.projectFilePath()); if (!root) return ModuleLoaderResult(); - if (wrapWithProjectItem && root->typeName() != QLatin1String("Project")) + if (root->typeName() != QLatin1String("Project")) root = wrapWithProject(root); - handleProject(&result, root, QSet<QString>() << QDir::cleanPath(filePath)); + const QString buildDirectory + = TopLevelProject::deriveBuildDirectory(parameters.buildRoot(), + TopLevelProject::deriveId(parameters.finalBuildConfigurationTree())); + root->setProperty(QLatin1String("sourceDirectory"), + VariantValue::create(QFileInfo(root->file()->filePath()).absolutePath())); + root->setProperty(QLatin1String("buildDirectory"), VariantValue::create(buildDirectory)); + handleProject(&result, root, buildDirectory, + QSet<QString>() << QDir::cleanPath(parameters.projectFilePath())); result.root = root; result.qbsFiles = m_reader->filesRead(); return result; @@ -198,19 +203,20 @@ private: }; void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item, - const QSet<QString> &referencedFilePaths) + const QString &buildDirectory, const QSet<QString> &referencedFilePaths) { if (!checkItemCondition(item)) return; ProjectContext projectContext; projectContext.result = loadResult; + projectContext.buildDirectory = buildDirectory; projectContext.localModuleSearchPath = FileInfo::resolvePath(item->file()->dirPath(), moduleSearchSubDir); ProductContext dummyProductContext; dummyProductContext.project = &projectContext; loadBaseModule(&dummyProductContext, item); - overrideItemProperties(item, QLatin1String("project"), m_overriddenProperties); + overrideItemProperties(item, QLatin1String("project"), m_parameters.overriddenValuesTree()); projectContext.extraSearchPaths = readExtraSearchPaths(item); m_reader->pushExtraSearchPaths(projectContext.extraSearchPaths); @@ -228,7 +234,7 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item, handleSubProject(&projectContext, child, referencedFilePaths); } else if (child->typeName() == QLatin1String("Project")) { copyProperties(item, child); - handleProject(loadResult, child, referencedFilePaths); + handleProject(loadResult, child, buildDirectory, referencedFilePaths); } } @@ -267,7 +273,7 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item, handleProduct(&projectContext, subItem); } else if (subItem->typeName() == QLatin1String("Project")) { copyProperties(item, subItem); - handleProject(loadResult, subItem, + handleProject(loadResult, subItem, buildDirectory, QSet<QString>(referencedFilePaths) << absReferencePath); } else { throw ErrorInfo(Tr::tr("The top-level item of a file in a \"references\" list must be " @@ -290,6 +296,7 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item) if (m_logger.traceEnabled()) m_logger.qbsTrace() << "[MODLDR] handleProduct " << item->file()->filePath(); + initProductProperties(projectContext, item); ProductContext productContext; productContext.project = projectContext; bool extraSearchPathsSet = false; @@ -332,6 +339,23 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item) m_reader->popExtraSearchPaths(); } +void ModuleLoader::initProductProperties(const ProjectContext *project, Item *item) +{ + QString productName = m_evaluator->stringValue(item, QLatin1String("name")); + if (productName.isEmpty()) { + productName = FileInfo::completeBaseName(item->file()->filePath()); + item->setProperty(QLatin1String("name"), VariantValue::create(productName)); + } + + item->setProperty(QLatin1String("buildDirectory"), + VariantValue::create( + FileInfo::resolvePath(project->buildDirectory, productName))); + + item->setProperty(QLatin1String("sourceDirectory"), + VariantValue::create( + QFileInfo(item->file()->filePath()).absolutePath())); +} + void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext, Item *item, const QSet<QString> &referencedFilePaths) { @@ -380,7 +404,7 @@ void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext Item::addChild(item, loadedItem); item->setScope(projectContext->scope); - handleProject(projectContext->result, loadedItem, + handleProject(projectContext->result, loadedItem, projectContext->buildDirectory, QSet<QString>(referencedFilePaths) << subProjectFilePath); } @@ -792,7 +816,7 @@ Item *ModuleLoader::loadModuleFile(ProductContext *productContext, const QString // Module properties that are defined in the profile are used as default values. const QVariantMap profileModuleProperties - = m_buildConfigProperties.value(fullModuleName).toMap(); + = m_parameters.buildConfigurationTree().value(fullModuleName).toMap(); for (QVariantMap::const_iterator vmit = profileModuleProperties.begin(); vmit != profileModuleProperties.end(); ++vmit) { @@ -827,7 +851,6 @@ void ModuleLoader::setupBaseModulePrototype(Item *prototype) BuiltinValue::create(BuiltinValue::GetNativeSettingFunction)); const BuiltinValuePtr getEnvValue = BuiltinValue::create(BuiltinValue::GetEnvFunction); prototype->setProperty(QLatin1String("getEnv"), getEnvValue); - prototype->setProperty(QLatin1String("getenv"), getEnvValue); // TODO: Remove in 1.3. prototype->setProperty(QLatin1String("getHostOS"), BuiltinValue::create(BuiltinValue::GetHostOSFunction)); prototype->setProperty(QLatin1String("canonicalArchitecture"), @@ -913,7 +936,8 @@ void ModuleLoader::instantiateModule(ProductContext *productContext, Item *insta } // override module properties given on the command line - const QVariantMap userModuleProperties = m_overriddenProperties.value(fullName).toMap(); + const QVariantMap userModuleProperties + = m_parameters.overriddenValuesTree().value(fullName).toMap(); for (QVariantMap::const_iterator vmit = userModuleProperties.begin(); vmit != userModuleProperties.end(); ++vmit) { if (Q_UNLIKELY(!moduleInstance->hasProperty(vmit.key()))) { @@ -993,7 +1017,7 @@ void ModuleLoader::checkCancelation() const { if (m_progressObserver && m_progressObserver->canceled()) { throw ErrorInfo(Tr::tr("Project resolving canceled for configuration %1.") - .arg(TopLevelProject::deriveId(m_buildConfigProperties))); + .arg(TopLevelProject::deriveId(m_parameters.buildConfigurationTree()))); } } diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h index f6f62a2c3..5df1747fc 100644 --- a/src/lib/corelib/language/moduleloader.h +++ b/src/lib/corelib/language/moduleloader.h @@ -33,6 +33,7 @@ #include "forward_decls.h" #include "itempool.h" #include <logging/logger.h> +#include <tools/setupprojectparameters.h> #include <QMap> #include <QSet> @@ -98,9 +99,7 @@ public: void setSearchPaths(const QStringList &searchPaths); Evaluator *evaluator() const { return m_evaluator; } - ModuleLoaderResult load(const QString &filePath, - const QVariantMap &overriddenProperties, const QVariantMap &buildConfigProperties, - bool wrapWithProjectItem = false); + ModuleLoaderResult load(const SetupProjectParameters ¶meters); static QString fullModuleName(const QStringList &moduleName); static void overrideItemProperties(Item *item, const QString &buildConfigKey, @@ -124,6 +123,7 @@ private: { public: ModuleLoaderResult *result; + QString buildDirectory; QString localModuleSearchPath; }; @@ -146,9 +146,10 @@ private: typedef QPair<Item *, ModuleLoaderResult::ProductInfo::Dependency> ProductDependencyResult; typedef QList<ProductDependencyResult> ProductDependencyResults; - void handleProject(ModuleLoaderResult *loadResult, Item *item, + void handleProject(ModuleLoaderResult *loadResult, Item *item, const QString &buildDirectory, const QSet<QString> &referencedFilePaths); void handleProduct(ProjectContext *projectContext, Item *item); + void initProductProperties(const ProjectContext *project, Item *item); void handleSubProject(ProjectContext *projectContext, Item *item, const QSet<QString> &referencedFilePaths); void createAdditionalModuleInstancesInProduct(ProductContext *productContext); @@ -198,8 +199,7 @@ private: QMap<QString, QStringList> m_moduleDirListCache; QHash<Item *, QSet<QString> > m_validItemPropertyNamesPerItem; QSet<Item *> m_disabledItems; - QVariantMap m_overriddenProperties; - QVariantMap m_buildConfigProperties; + SetupProjectParameters m_parameters; }; } // namespace Internal diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp index fec042e1e..8b58912ed 100644 --- a/src/lib/corelib/language/projectresolver.cpp +++ b/src/lib/corelib/language/projectresolver.cpp @@ -274,23 +274,17 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext) ProductContext productContext; m_productContext = &productContext; productContext.item = item; - const QString productSourceDirectory = QFileInfo(item->file()->filePath()).absolutePath(); - item->setProperty(QLatin1String("sourceDirectory"), - VariantValue::create(productSourceDirectory)); - item->setProperty(QLatin1String("buildDirectory"), VariantValue::create(projectContext - ->project->topLevelProject()->buildDirectory)); ResolvedProductPtr product = ResolvedProduct::create(); product->project = projectContext->project; m_productItemMap.insert(product, item); projectContext->project->products += product; productContext.product = product; product->name = m_evaluator->stringValue(item, QLatin1String("name")); - if (product->name.isEmpty()) { - product->name = FileInfo::completeBaseName(item->file()->filePath()); - item->setProperty(QLatin1String("name"), VariantValue::create(product->name)); - } m_logger.qbsTrace() << "[PR] resolveProduct " << product->name; + // product->buildDirectory() isn't valid yet, because the productProperties map is not ready. + productContext.buildDirectory = m_evaluator->stringValue(item, QLatin1String("buildDirectory")); + if (std::find_if(item->modules().begin(), item->modules().end(), ModuleNameEquals(product->name)) != item->modules().end()) { throw ErrorInfo( @@ -314,11 +308,20 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext) product->fileTags = m_evaluator->fileTagsValue(item, QLatin1String("type")); product->targetName = m_evaluator->stringValue(item, QLatin1String("targetName")); - product->sourceDirectory = productSourceDirectory; - product->destinationDirectory - = m_evaluator->stringValue(item, QLatin1String("destinationDirectory")); + product->sourceDirectory = m_evaluator->stringValue(item, QLatin1String("sourceDirectory")); + const QString destDirKey = QLatin1String("destinationDirectory"); + product->destinationDirectory = m_evaluator->stringValue(item, destDirKey); + + if (product->destinationDirectory.isEmpty()) { + product->destinationDirectory = productContext.buildDirectory; + } else { + product->destinationDirectory = FileInfo::resolvePath( + product->topLevelProject()->buildDirectory, + product->destinationDirectory); + } product->location = item->location(); product->productProperties = createProductConfig(); + product->productProperties.insert(destDirKey, product->destinationDirectory); QVariantMap moduleProperties; moduleProperties.insert(QLatin1String("modules"), product->productProperties.take(QLatin1String("modules"))); @@ -384,15 +387,6 @@ void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item, m_productContext->additionalFileTags += m_evaluator->fileTagsValue(item, QLatin1String("additionalProductTypes")); - // TODO: Remove in 1.3. - bool additionalProductFileTagsWasSet; - const QStringList additionalProductFileTags = m_evaluator->stringListValue(item, QLatin1String("additionalProductFileTags"), - &additionalProductFileTagsWasSet); - if (additionalProductFileTagsWasSet) { - m_logger.printWarning(ErrorInfo(Tr::tr("The 'additionalProductFileTags' property is deprecated. Please " - "use 'additionalProductTypes' instead."), item->location())); - m_productContext->additionalFileTags += FileTags::fromStringList(additionalProductFileTags); - } foreach (const Item::Module &m, item->modules()) module->moduleDependencies += ModuleLoader::fullModuleName(m.name); @@ -463,6 +457,17 @@ void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext) if (Q_UNLIKELY(!files.isEmpty())) throw ErrorInfo(Tr::tr("Group.files and Group.fileTagsFilters are exclusive."), item->location()); + + ProductContext::ArtifactPropertiesInfo apinfo + = m_productContext->artifactPropertiesPerFilter.value(fileTagsFilter); + if (apinfo.first) { + if (apinfo.second.fileName() == item->location().fileName()) { + ErrorInfo error(Tr::tr("Conflicting fileTagsFilter in Group items.")); + error.append(Tr::tr("First item"), apinfo.second); + error.append(Tr::tr("Second item"), item->location()); + throw error; + } + } if (!isEnabled) return; ArtifactPropertiesPtr aprops = ArtifactProperties::create(); @@ -471,6 +476,8 @@ void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext) cfg->setValue(evaluateModuleValues(item)); aprops->setPropertyMapInternal(cfg); m_productContext->product->artifactProperties += aprops; + m_productContext->artifactPropertiesPerFilter.insert(fileTagsFilter, + ProductContext::ArtifactPropertiesInfo(aprops, item->location())); return; } if (Q_UNLIKELY(files.isEmpty() && !item->hasProperty(QLatin1String("files")))) { @@ -710,24 +717,14 @@ void ProjectResolver::resolveFileTagger(Item *item, ProjectContext *projectConte checkCancelation(); QList<FileTaggerConstPtr> &fileTaggers = m_productContext ? m_productContext->product->fileTaggers : projectContext->fileTaggers; - QStringList patterns = m_evaluator->stringListValue(item, QLatin1String("patterns")); + const QStringList patterns = m_evaluator->stringListValue(item, QLatin1String("patterns")); + if (patterns.isEmpty()) + throw ErrorInfo(Tr::tr("FileTagger.patterns must be a non-empty list."), item->location()); + const FileTags fileTags = m_evaluator->fileTagsValue(item, QLatin1String("fileTags")); if (fileTags.isEmpty()) throw ErrorInfo(Tr::tr("FileTagger.fileTags must not be empty."), item->location()); - // TODO: Remove in 1.3. - bool patternWasSet; - const QStringList oldPatterns = m_evaluator->stringListValue(item, QLatin1String("pattern"), - &patternWasSet); - if (patternWasSet) { - m_logger.printWarning(ErrorInfo(Tr::tr("The 'pattern' property is deprecated. Please " - "use 'patterns' instead."), item->location())); - patterns << oldPatterns; - } - - if (patterns.isEmpty()) - throw ErrorInfo(Tr::tr("FileTagger.patterns must be a non-empty list."), item->location()); - foreach (const QString &pattern, patterns) { if (pattern.isEmpty()) throw ErrorInfo(Tr::tr("A FileTagger pattern must not be empty."), item->location()); @@ -760,8 +757,8 @@ void ProjectResolver::resolveTransformer(Item *item, ProjectContext *projectCont QString fileName = m_evaluator->stringValue(child, QLatin1String("fileName")); if (Q_UNLIKELY(fileName.isEmpty())) throw ErrorInfo(Tr::tr("Artifact fileName must not be empty.")); - artifact->absoluteFilePath = FileInfo::resolvePath(m_productContext->product->topLevelProject()->buildDirectory, - fileName); + artifact->absoluteFilePath + = FileInfo::resolvePath(m_productContext->buildDirectory, fileName); artifact->fileTags = m_evaluator->fileTagsValue(child, QLatin1String("fileTags")); if (artifact->fileTags.isEmpty()) artifact->fileTags.insert(unknownFileTag()); diff --git a/src/lib/corelib/language/projectresolver.h b/src/lib/corelib/language/projectresolver.h index 636fd7299..630786db7 100644 --- a/src/lib/corelib/language/projectresolver.h +++ b/src/lib/corelib/language/projectresolver.h @@ -77,8 +77,11 @@ private: struct ProductContext { ResolvedProductPtr product; + QString buildDirectory; FileTags additionalFileTags; Item *item; + typedef QPair<ArtifactPropertiesPtr, CodeLocation> ArtifactPropertiesInfo; + QHash<QStringList, ArtifactPropertiesInfo> artifactPropertiesPerFilter; }; struct ModuleContext diff --git a/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs b/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs new file mode 100644 index 000000000..54b3343e4 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs @@ -0,0 +1,13 @@ +import qbs 1.0 + +Application { + Group { + fileTagsFilter: "application" + qbs.install: true + } + Group { + fileTagsFilter: "application" + qbs.install: false + } +} + diff --git a/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs b/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs index cc1b7b2a2..0ba76c6f6 100644 --- a/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs +++ b/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs @@ -2,14 +2,15 @@ import qbs 1.0 Project { Application { - name: { + name: "product1" + type: { if (!(dummy.cFlags instanceof Array)) throw new Error("dummy.cFlags: Array type expected."); if (!(dummy.cxxFlags instanceof Array)) throw new Error("dummy.cxxFlags: Array type expected."); if (!(dummy.defines instanceof Array)) throw new Error("dummy.defines: Array type expected."); - return "product1"; + return "application"; } Depends { name: "dummy" } // dummy.cxxFlags is set via profile and is not overridden diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp index ff19b8afe..6873391e9 100644 --- a/src/lib/corelib/language/tst_language.cpp +++ b/src/lib/corelib/language/tst_language.cpp @@ -370,6 +370,8 @@ void TestLanguage::erroneousFiles_data() << "Unexpected item type 'Narf'"; QTest::newRow("invalid_child_item_type") << "Items of type 'Project' cannot contain items of type 'Depends'."; + QTest::newRow("conflicting_fileTagsFilter") + << "Conflicting fileTagsFilter in Group items"; } void TestLanguage::erroneousFiles() @@ -1113,7 +1115,7 @@ void TestLanguage::productDirectories() QVERIFY(product); const QVariantMap config = product->productProperties; QCOMPARE(config.value(QLatin1String("buildDirectory")).toString(), - buildDir(defaultParameters)); + buildDir(defaultParameters) + QLatin1Char('/') + product->name); QCOMPARE(config.value(QLatin1String("sourceDirectory")).toString(), testDataDir()); } catch (const ErrorInfo &e) { diff --git a/src/lib/corelib/tools/settings.cpp b/src/lib/corelib/tools/settings.cpp index f59e83e2c..617fa2137 100644 --- a/src/lib/corelib/tools/settings.cpp +++ b/src/lib/corelib/tools/settings.cpp @@ -45,26 +45,6 @@ static QSettings::Format format() return HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat; } -static void migrateValue(QSettings *settings, const QString &key) -{ - const QVariant v = settings->value(key); - if (!v.isValid()) - return; - settings->setValue(QLatin1String("org/qt-project/qbs/") + key, v); - settings->remove(key); -} - -static void migrateGroup(QSettings *settings, const QString &group) -{ - QStringList fullKeys; - settings->beginGroup(group); - foreach (const QString &key, settings->allKeys()) - fullKeys += group + QLatin1Char('/') + key; - settings->endGroup(); - foreach (const QString &key, fullKeys) - migrateValue(settings, key); -} - static QSettings *createQSettings(const QString &baseDir) { return baseDir.isEmpty() @@ -75,13 +55,6 @@ static QSettings *createQSettings(const QString &baseDir) Settings::Settings(const QString &baseDir) : m_settings(createQSettings(baseDir)) { - // Migrate settings to internal group. - // ### remove in qbs 1.3 - if (!m_settings->childGroups().contains(QLatin1String("org/qt-project/qbs"))) { - migrateValue(m_settings, QLatin1String("defaultProfile")); - migrateGroup(m_settings, QLatin1String("profiles")); - migrateGroup(m_settings, QLatin1String("preferences")); - } // Actual qbs settings are stored transparently within a group, because QSettings // can see non-qbs fallback settings e.g. from QtProject that we're not interested in. m_settings->beginGroup(QLatin1String("org/qt-project/qbs")); diff --git a/src/lib/qtprofilesetup/qtprofilesetup.cpp b/src/lib/qtprofilesetup/qtprofilesetup.cpp index 31b6950a1..ddaec6c8e 100644 --- a/src/lib/qtprofilesetup/qtprofilesetup.cpp +++ b/src/lib/qtprofilesetup/qtprofilesetup.cpp @@ -119,6 +119,13 @@ static void addDesignerComponentsModule(QList<QtModuleInfo> &modules) modules << module; } +static QString quotedPath(const QString &str) +{ + return QLatin1Char('"') + + QDir::fromNativeSeparators(str).replace(QLatin1Char('"'), QLatin1String("\\\"")) + + QLatin1Char('"'); +} + static void createModules(Profile &profile, Settings *settings, const QtEnvironment &qtEnvironment) { @@ -314,9 +321,21 @@ static void createModules(Profile &profile, Settings *settings, const QByteArray debugMacro = module.qbsName == QLatin1String("declarative") || qtEnvironment.qtMajorVersion < 5 ? "QT_DECLARATIVE_DEBUG" : "QT_QML_DEBUG"; - propertiesString = "property bool qmlDebugging: false\n" - " cpp.defines: " - "qmlDebugging ? base.concat('" + debugMacro + "') : base"; + + const QString indent = QLatin1String(" "); + QTextStream s(&propertiesString); + s << "property bool qmlDebugging: false" << endl + << indent << "cpp.defines: " + << "qmlDebugging ? base.concat('" + debugMacro + "') : base" << endl; + + s << indent << "property string qmlPath"; + if (qtEnvironment.qmlPath.isEmpty()) + s << endl; + else + s << ": " << quotedPath(qtEnvironment.qmlPath) << endl; + + s << indent << "property string qmlImportsPath: " + << quotedPath(qtEnvironment.qmlImportPath); } content.replace("### special properties", propertiesString); moduleFile.resize(0); diff --git a/src/lib/qtprofilesetup/qtprofilesetup.h b/src/lib/qtprofilesetup/qtprofilesetup.h index a2283202a..88e56d2c9 100644 --- a/src/lib/qtprofilesetup/qtprofilesetup.h +++ b/src/lib/qtprofilesetup/qtprofilesetup.h @@ -43,6 +43,7 @@ public: QString libraryPath; QString includePath; QString binaryPath; + QString qmlPath; QString qmlImportPath; QString documentationPath; QString dataPath; diff --git a/src/lib/qtprofilesetup/templates/core.qbs b/src/lib/qtprofilesetup/templates/core.qbs index 0d706b255..deb7695d4 100644 --- a/src/lib/qtprofilesetup/templates/core.qbs +++ b/src/lib/qtprofilesetup/templates/core.qbs @@ -37,7 +37,7 @@ Module { // These are deliberately not path types // We don't want to resolve them against the source directory - property string generatedFilesDir: "GeneratedFiles/" + product.name + property string generatedFilesDir: product.buildDirectory + "/GeneratedFiles" property string qmFilesDir: product.destinationDirectory // private properties @@ -62,7 +62,7 @@ Module { paths.push(libPath + '/QtCore' + libInfix + '.framework/Versions/' + versionMajor + '/Headers'); paths.push(incPath + '/QtCore'); paths.push(incPath); - paths.push(product.buildDirectory + '/' + generatedFilesDir); + paths.push(generatedFilesDir); return paths; } cpp.libraryPaths: { @@ -117,54 +117,29 @@ Module { additionalProductTypes: ["qm"] validate: { - var requiredProperties = { - "binPath": binPath, - "incPath": incPath, - "libPath": libPath, - "mkspecPath": mkspecPath, - "version": version, - "config": config, - "qtConfig": qtConfig, - // Validate these in case 'version' is in some non-standard format - "versionMajor": versionMajor, - "versionMinor": versionMinor, - "versionPatch": versionPatch - }; - - if (!staticBuild) { - requiredProperties["pluginPath"] = pluginPath; - } - - var missingProperties = []; - for (var i in requiredProperties) { - if (requiredProperties[i] === undefined) { - missingProperties.push("Qt.core." + i); - } - } - - var invalidProperties = {}; - if (versionMajor <= 0) - invalidProperties["versionMajor"] = "must be > 0"; - if (versionMinor < 0) - invalidProperties["versionMinor"] = "must be >= 0"; - if (versionPatch < 0) - invalidProperties["versionPatch"] = "must be >= 0"; - - var errorMessage = ""; - if (missingProperties.length > 0) { - errorMessage += "The following Qt module properties are not set. " + - "Set them in your profile:\n" + - missingProperties.sort().join("\n"); - } - - if (Object.keys(invalidProperties).length > 0) { - errorMessage += "The following Qt module properties have invalid values:\n" + - Object.map(invalidProperties, - function(msg, prop) { return prop + ": " + msg; }).join("\n"); - } - - if (errorMessage.length > 0) - throw errorMessage; + var validator = new ModUtils.PropertyValidator("Qt.core"); + validator.setRequiredProperty("binPath", binPath); + validator.setRequiredProperty("incPath", incPath); + validator.setRequiredProperty("libPath", libPath); + validator.setRequiredProperty("mkspecPath", mkspecPath); + validator.setRequiredProperty("version", version); + validator.setRequiredProperty("config", config); + validator.setRequiredProperty("qtConfig", qtConfig); + validator.setRequiredProperty("versionMajor", versionMajor); + validator.setRequiredProperty("versionMinor", versionMinor); + validator.setRequiredProperty("versionPatch", versionPatch); + + if (!staticBuild) + validator.setRequiredProperty("pluginPath", pluginPath); + + // Allow custom version suffix since some distributions might want to do this, + // but otherwise the version must start with a valid 3-component string + validator.addVersionValidator("version", version, 3, 3, true); + validator.addRangeValidator("versionMajor", versionMajor, 1); + validator.addRangeValidator("versionMinor", versionMinor, 0); + validator.addRangeValidator("versionPatch", versionPatch, 0); + + validator.validate(); } setupRunEnvironment: { diff --git a/src/lib/qtprofilesetup/templates/gui.qbs b/src/lib/qtprofilesetup/templates/gui.qbs index 02b90f40c..b0a17aa6c 100644 --- a/src/lib/qtprofilesetup/templates/gui.qbs +++ b/src/lib/qtprofilesetup/templates/gui.qbs @@ -18,7 +18,7 @@ QtModule { Artifact { // ### TODO we want to access the module's property "Qt.core.generatedFilesDir" here. But without evaluating all available properties a priori. - fileName: 'GeneratedFiles/' + product.name + '/ui_' + input.completeBaseName + '.h' + fileName: 'GeneratedFiles/ui_' + input.completeBaseName + '.h' fileTags: ["hpp"] } |